SEHException при вызове WTSVirtualChannelClose
Я создаю приложение, которое будет взаимодействовать с сервером, используя API-интерфейс Remote Desktop Service.
Я создаю свое приложение с помощью кода, найденного здесь: https://code.google.com/p/tstunnels/ и здесь: http://www.codeproject.com/Articles/16374/How-to-Write-a-Terminal-Services-Add-in-in-Pure-C
Я создаю клиентскую библиотеку, которая загружается при подключении к удаленному рабочему столу, и я могу отправлять и получать сообщения.
На стороне сервера я получаю SEHException, когда пытаюсь закрыть приложение.
Я использую эти методы, используя DLLImport:
[DllImport("Wtsapi32.dll", SetLastError = true)]
public static extern IntPtr WTSVirtualChannelOpen(IntPtr server, int sessionId, [MarshalAs(UnmanagedType.LPStr)] string virtualName);
[DllImport("Wtsapi32.dll", SetLastError = true)]
public static extern bool WTSVirtualChannelQuery(IntPtr channelHandle, WtsVirtualClass Class, out IntPtr data, out int bytesReturned);
[DllImport("Wtsapi32.dll")]
public static extern void WTSFreeMemory(IntPtr memory);
[DllImport("Wtsapi32.dll", SetLastError = true)]
public static extern bool WTSVirtualChannelWrite(IntPtr channelHandle, byte[] data, int length, out int bytesWritten);
[DllImport("Wtsapi32.dll")]
public static extern bool WTSVirtualChannelClose(IntPtr channelHandle);
public static Stream WTSVirtualChannelQuery_WTSVirtualFileHandle(IntPtr channelHandle)
{
int len;
IntPtr buffer;
var b = WTSVirtualChannelQuery(channelHandle, WtsVirtualClass.WTSVirtualFileHandle, out buffer, out len);
if (!b) throw new Win32Exception();
var fileHandle = new SafeFileHandle(Marshal.ReadIntPtr(buffer), true);
WTSFreeMemory(buffer);
return new FileStream(fileHandle, FileAccess.ReadWrite, 0x1000, true);
}
Затем внутри моего приложения я делаю это:
mHandle = Native.WTSVirtualChannelOpen(IntPtr.Zero, -1, ChannelName);
if (mHandle == IntPtr.Zero)
{
Log("RDP Virtual channel Open Failed: " + new Win32Exception().Message);
return;
}
try
{
var stream = Native.WTSVirtualChannelQuery_WTSVirtualFileHandle(mHandle);
reader = new BinaryReader(new BufferedStream(stream));
}
catch (Win32Exception ex)
{
Log("RDP Virtual channel Query Failed: " + ex.Message);
return;
}
После этого я могу писать и читать через Виртуальный канал, но когда я пытаюсь закрыть, используя приведенный ниже код, я получаю сообщение об ошибке.
if (reader != null) reader.Close();
var ret = Native.WTSVirtualChannelClose(mHandle);
Вот ошибка:
System.Runtime.InteropServices.SEHException (0x80004005): внешний компонент выдал исключение. в Server.Native.WTSVirtualChannelClose(IntPtr channelHandle) в Server.Server.Disconnect() в i:\Tomka\TS\v2\TSAddin\Server\Server.cs: строка 73
Я пытался получить последний Win32Error с помощью Marshal.GetLastWin32Error()
но возвращается 1008
,
Глядя в msdn, я нашел это описание ошибки:
ERROR_NO_TOKEN 1008 (0x3F0) Была сделана попытка сослаться на несуществующий токен.
Я нашел статью на MSDN, но код на C++, так что это не помогло, так или иначе, вот ссылка: http://msdn.microsoft.com/en-us/library/aa383852(v=vs.85).aspx
РЕДАКТИРОВАТЬ 1 Я сделал, как написал @Hans. Вот первое исключение, которое я получил:
Исключение первого шанса в 0x00000000771105B7 (ntdll.dll) в Server.exe: 0xC0000008: указан неверный дескриптор.
РЕДАКТИРОВАТЬ 2
Вот классы, которые я использую на сервере:
class Native
{
[DllImport("Wtsapi32.dll", SetLastError = true)]
public static extern IntPtr WTSVirtualChannelOpen(IntPtr server, int sessionId, [MarshalAs(UnmanagedType.LPStr)] string virtualName);
[DllImport("Wtsapi32.dll", SetLastError = true)]
public static extern bool WTSVirtualChannelQuery(IntPtr channelHandle, WtsVirtualClass Class, out IntPtr data, out int bytesReturned);
[DllImport("Wtsapi32.dll")]
public static extern void WTSFreeMemory(IntPtr memory);
[DllImport("Wtsapi32.dll", SetLastError = true)]
public static extern bool WTSVirtualChannelWrite(IntPtr channelHandle, byte[] data, int length, out int bytesWritten);
[DllImport("Wtsapi32.dll", SetLastError = true)]
public static extern bool WTSVirtualChannelRead(IntPtr channelHandle, int timeOut, IntPtr data, int length, out int bytesRead);
[DllImport("Wtsapi32.dll")]
public static extern bool WTSVirtualChannelClose(IntPtr channelHandle);
public static Stream WTSVirtualChannelQuery_WTSVirtualFileHandle(IntPtr channelHandle)
{
int len;
IntPtr buffer;
var b = WTSVirtualChannelQuery(channelHandle, WtsVirtualClass.WTSVirtualFileHandle, out buffer, out len);
if (!b) throw new Win32Exception();
var fileHandle = new SafeFileHandle(Marshal.ReadIntPtr(buffer), true);
WTSFreeMemory(buffer);
return new FileStream(fileHandle, FileAccess.ReadWrite, 0x1000, true);
}
public enum WtsVirtualClass
{
WTSVirtualClient = 0,
WTSVirtualFileHandle = 1
}
}
и Server.cs:
public class Server
{
private IntPtr mHandle;
private BinaryReader reader;
public bool IsConnected { get; private set; }
private bool SeenHello;
public const string ChannelName = "TEST";
private delegate void Action();
public void Connect()
{
mHandle = Native.WTSVirtualChannelOpen(IntPtr.Zero, -1, ChannelName);
if (mHandle == IntPtr.Zero)
{
Log("RDP Virtual channel Open Failed: " + new Win32Exception().Message);
return;
}
try
{
var stream = Native.WTSVirtualChannelQuery_WTSVirtualFileHandle(mHandle);
reader = new BinaryReader(new BufferedStream(stream));
}
catch (Win32Exception ex)
{
Log("RDP Virtual channel Query Failed: " + ex.Message);
return;
}
IsConnected = true;
Log("Connected");
Action process = Process;
process.BeginInvoke(process.EndInvoke, null);
Action hello = () =>
{
while (!SeenHello && IsConnected)
{
WriteMessage("HELLO");
Log("Sending HELLO");
Thread.Sleep(200);
}
};
hello.BeginInvoke(hello.EndInvoke, null);
}
public void Disconnect()
{
IsConnected = false;
if (reader != null) reader.Close();
var ret = Native.WTSVirtualChannelClose(mHandle);
}
private void Process()
{
while (IsConnected)
{
try
{
var len = reader.ReadInt32();
Log(len.ToString());
byte[] buff = reader.ReadBytes(len);
Log(Encoding.UTF8.GetString(buff));
MessageReceived(Encoding.UTF8.GetString(buff));
}
catch (OperationCanceledException ex)
{
Log(ex);
return;
}
catch (Exception ex)
{
Log(ex);
}
}
}
public bool WriteMessage(string msg)
{
byte[] data = Encoding.UTF8.GetBytes(msg);
int written;
var ret = Native.WTSVirtualChannelWrite(mHandle, data, data.Length, out written);
if (ret) return true;
var ex = new Win32Exception();
if (!SeenHello && ex.NativeErrorCode == 1 /* Incorrect Function */) return false;
Log("RDP Virtual channel Write Failed: " + ex.Message);
return false;
}
public void MessageReceived(string msg)
{
Log(msg);
if (msg.ToUpper() == "HELLO:RESPONSE")
{
if (!SeenHello)
{
SeenHello = true;
OnConnected(msg);
}
}
OnMessage(msg);
}
public EventHandler<MessageEventArgs> Connected;
protected void OnConnected(string msg)
{
if (Connected != null)
Connected(this, new MessageEventArgs(msg));
}
public EventHandler<MessageEventArgs> Message;
protected void OnMessage(string msg)
{
if (Message != null)
Message(this, new MessageEventArgs(msg));
}
public void Log(object message)
{
OnMessageLogged(message.ToString());
}
public EventHandler<MessageEventArgs> MessageLogged;
protected void OnMessageLogged(string message)
{
if (MessageLogged != null)
MessageLogged(this, new MessageEventArgs(message));
}
}
и использование выглядит так:
В основном конструкторе формы я создаю объект сервера, в Load я вызываю Server.Connect() и в FormClosed Server.Disconnect().