我尝试构建两个将通过RDP相互通信的应用程序。 我有基本的沟通,但我想优化代码并删除所有异常。
在服务器上我打开FileStream,当我从客户端发送内容时,我使用BeginRead
阅读它:
public void Connect()
{
mHandle = Native.WTSVirtualChannelOpen(IntPtr.Zero, -1, CHANNEL_NAME);
Task.Factory.StartNew(() =>
{
IntPtr tempPointer = IntPtr.Zero;
uint pointerLen = 0;
if (Native.WTSVirtualChannelQuery(mHandle, WtsVirtualClass.WTSVirtualFileHandle, out tempPointer, ref pointerLen))
{
IntPtr realPointer = Marshal.ReadIntPtr(tempPointer);
Native.WTSFreeMemory(tempPointer);
var fileHandle = new SafeFileHandle(realPointer, false);
fileStream = new FileStream(fileHandle, FileAccess.ReadWrite, 0x640, true);
var os = new ObjectState();
os.RDPStream = fileStream;
fileStream.BeginRead(os.Buffer, 0, 8, CallBack, os);
}
});
}
和我的回调:
private void CallBack(IAsyncResult iar)
{
if (iar.IsCompleted || iar.CompletedSynchronously)
{
var os = iar.AsyncState as ObjectState;
int readed = 0;
try
{
readed = os.RDPStream.EndRead(iar);
}
catch (IOException ex)
{
MessageBox.Show(ex.Message);
}
if (readed != 0)
{
switch (os.State)
{
case FileDownloadStatus.Length:
os.Length = BitConverter.ToInt32(os.Buffer, 0);
os.State = FileDownloadStatus.Body;
os.RDPStream.BeginRead(os.Buffer, 0, os.Length, CallBack, os);
break;
case FileDownloadStatus.Body:
var message = Encoding.UTF8.GetString(os.Buffer, 0, readed);
Debug.WriteLine(message);
var newos = new ObjectState {RDPStream = os.RDPStream};
os.RDPStream.BeginRead(newos.Buffer, 0, 8, CallBack, newos);
break;
}
}
}
}
和所需物品:
enum FileDownloadStatus
{
Length,
Body,
}
class ObjectState
{
public int Length = 0;
public FileDownloadStatus State = FileDownloadStatus.Length;
public byte[] Buffer = new byte[0x640];
public FileStream RDPStream = null;
}
不幸的是,当我尝试关闭我的应用程序时,我得到异常System.OperationCanceledException
。
我想将其转换为ReadAsync,因为我可以使用CancelationToken,但之后我必须升级到.NET 4.5。
我发现similar有关CancelationToken的问题,但它是用F#编写的,我需要C#。
所以我的问题是:
如何停止BeginRead或将此代码转换为.NET 4.5并使用ReadAsync?
答案 0 :(得分:0)
转换为async / await:
将fileStream.BeginRead(os.Buffer, 0, 8, CallBack, os);
替换为:
Task<int> task = fileStream.ReadAsync(os.Buffer, 0, 8, cancelToken);
try
{
await task;
await CallBack(os, task);
}
catch (IOException ex)
{
// TODO: unpack task exception and display correctly
MessageBox.Show(ex.Message);
}
请注意,我假设您有一个包含cancelToken
对象的新CancelationToken
变量。我想您可以将其存储在ObjectState
对象中,以便随时可以调用ReadAsync()
。
将CallBack()
方法的第一部分(if (readed != 0)
语句之前)更改为:
private async Task CallBack(ObjectState os, Task<int> task)
{
if (task.Status != TaskStatus.Canceled)
{
int readed = task.Result;
即。用上面的if (readed != 0)
语句替换所有代码,改变上面的方法声明。
对CallBack()
方法中的代码应用类似的更改,您也可以在其中调用BeginRead()
。即使用ReadAsync
,将await
与返回的Task<int>
一起使用,然后在Task<int>
完成时将await
传递给回调,并使用await
进行调用同样。 (您应该只能在Connect()
方法中使用一个try / catch块...在continuation中发生的异常应该传播回该try / catch块。
请注意,除了对CallBack()
方法声明的更改之外,您还需要更改void Connect()
方法声明,使其为async Task Connect()
。另请注意,这将强制调用者自己成为async
方法并在调用时使用await
(依此类推,直到调用链,直到到达UI事件处理程序,可以是{{1}并且使用async
,虽然合法地是await
方法),或者直接处理返回的void
(不推荐,因为这通常意味着否定异步/等待成语的好处) )。
如果你继续使用Task
方法,据我所知,取消它的唯一方法是关闭BeginRead()
对象(这将导致操作以{{ 1}})。
最后请注意:我对RDP I / O不够熟悉,无法评估您对异步读取的字节数结果的使用。但在其他情况下,假设您已成功读取最初要求的字节数将是一个非常糟糕的主意。通常,您必须保持循环,读取数据,直到您拥有所需的字节数为止。