Working with RDP Plug-ins

A

amdrit

I've been working on an RDP plug-in solution written in C#. The concept came
from a CodeProject article. I encountered a problem in testing and so
opened a case with MS. Rather than looking into the problem they cited a KB
article http://support.microsoft.com/kb/841927.

My question is, does anyone have anything to support that MSTSC.EXE is
included amoung the executables that require this special consideration?

I've spent a lot of time on this project and it seems now that I have wasted
a lot of company time. I would like to exhaust all options before looking
to go to a pure c solution. No one on my team has the experience to create
the components we require in c.

Thanks in advance.
 
M

Mike Lovell

I've been working on an RDP plug-in solution written in C#. The concept
came from a CodeProject article. I encountered a problem in testing and
so opened a case with MS. Rather than looking into the problem they cited
a KB article http://support.microsoft.com/kb/841927.

What was the problem?
My question is, does anyone have anything to support that MSTSC.EXE is
included amoung the executables that require this special consideration?

I've spent a lot of time on this project and it seems now that I have
wasted a lot of company time. I would like to exhaust all options before
looking to go to a pure c solution. No one on my team has the experience
to create the components we require in c.

I think it's likely we'd need more details on what you're exactly trying to
do (if you can disclose this information).

Perhaps the lines of source relevant to the problem.
 
A

amdrit

This whole thing started after I read the article:
http://www.codeproject.com/KB/system/TSAddinInCS.aspx?msg=3401010#xx3401010xx

I have since implemented this concept into my application. We currently
have a hosted VB6 application that rests on a Citrix server that speaks to a
VB6 applicationi on the the local machine using the Citrix Virtual channels.
Granted the virtual channel component was written in C. None the less, our
clients are asking for an RDP option as well.

Upon reading that article, we set out to prototype this solution and asked
MS to assist use with the the finer points 2 years ago. At the time, no one
said hey this is a very bad idea. The prototype is fairly simple but it
works none the less.

Now that we have the pieces in place for the testing stage, I encountered an
odd behavior. The first time the hosted application requests the local
machine to perform a task the local machine does so and then sends a result
back to the hosted app. The workflow is that the local machine then closes
the UI. Any subsequent call from the hosted app still invokes the local
machine's UI, however, the result information is never recieved by the
hosted application.

I have verified that the message is generated and sent to the channel. I
have verified that the channel receives the message and calls the send
message api. The server side of the channel never recieves the data. No
errors are generated and I am dumbfounded as to what my problem is. I have
proven (at least I think) that the channel is still open - any attempt to
close it on the server side cause an application crash.

So when I went to MS with this problem, they never even looked at the code.
They simply said that it was a bad idea and that I should abort, citing that
KB article.

Assuming that the message is being sent up the channel here is the failing
code on the server side:

void workerThread_DoWork(object sender, DoWorkEventArgs e)
{
//Variable Declaration Section.
bool success = false;
IntPtr intHandlePtr;
byte[] bytes = new byte[2048];
uint bytesread = 0;
int bytesRead = 0;
bool bContinue = true;
BackgroundWorker bgw = (BackgroundWorker)sender;

if (!bgw.CancellationPending)
{
//Query the Virtual Channel and get the underlying File Handle.
success = WtsApi32.WTSVirtualChannelQuery(
mHandle,
(int)WtsApi32.WTS_VIRTUAL_CLASS.WTSVirtualFileHandle,
out intHandlePtr,
ref bytesRead
);
//intHandlePtr is a double pointer buffer in the unmanaged world.
Retrieve the File Handle Pointer
//correctly by marshalling it.
IntPtr pFileHandle = Marshal.ReadIntPtr(intHandlePtr);

// Create an manual-reset event, which is initially set to Non
Signalled.
IntPtr myEventHandle = Kernel32.CreateEvent(IntPtr.Zero, false,
false, "MyEvent");

//Declare and initialize the OVERLAPPED structure and initialize it
correctly.
System.Threading.NativeOverlapped ovr = new
System.Threading.NativeOverlapped();
ovr.InternalHigh = IntPtr.Zero;
ovr.InternalLow = IntPtr.Zero;
ovr.OffsetHigh = 0;
ovr.OffsetLow = 0;
ovr.EventHandle = IntPtr.Zero;

//Assign the Created Event handle in the Overlapped structure.
ovr.EventHandle = myEventHandle;

while (bContinue == true)
{
//Reset the event.Still the client is in progress.
Kernel32.ResetEvent(ovr.EventHandle);

//Read any Progress data from the channel if any.
bool b = Kernel32.ReadFile(pFileHandle, bytes, (uint)bytes.Length,
out bytesread, ref ovr);

if (this.OnMessageReceived != null)
{
try
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
System.IO.MemoryStream ms = new System.IO.MemoryStream(bytes);
SimpleMessage sr = (SimpleMessage)bf.Deserialize(ms);

//Notify the calling app that we have new data
this.OnMessageReceived(bytes);

bContinue = false;
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.ToString());
}
if (bgw.CancellationPending) break;
}
//If not wait till the data arrives.Once the data arrives , the
event handle in the overlapped
//structure will get signaled and the WaitforSingleObject will
continue going for the next data.
int i = Kernel32.WaitForSingleObject(ovr.EventHandle,
(int)Kernel32.INFINITE);
}

//Thread is going to exit.Release the Pointer obtained.
WtsApi32.WTSFreeMemory(intHandlePtr);

//Close the channel here depending upon your requirement.
WtsApi32.WTSVirtualChannelClose(intHandlePtr);
}

}
 
M

Mike Lovell

amdrit said:
This whole thing started after I read the article:
http://www.codeproject.com/KB/system/TSAddinInCS.aspx?msg=3401010#xx3401010xx

I have since implemented this concept into my application. We currently
have a hosted VB6 application that rests on a Citrix server that speaks to
a VB6 applicationi on the the local machine using the Citrix Virtual
channels. Granted the virtual channel component was written in C. None
the less, our clients are asking for an RDP option as well.

Upon reading that article, we set out to prototype this solution and asked
MS to assist use with the the finer points 2 years ago. At the time, no
one said hey this is a very bad idea. The prototype is fairly simple but
it works none the less.

Now that we have the pieces in place for the testing stage, I encountered
an odd behavior. The first time the hosted application requests the local
machine to perform a task the local machine does so and then sends a
result back to the hosted app. The workflow is that the local machine
then closes the UI. Any subsequent call from the hosted app still invokes
the local machine's UI, however, the result information is never recieved
by the hosted application.

I have verified that the message is generated and sent to the channel. I
have verified that the channel receives the message and calls the send
message api. The server side of the channel never recieves the data. No
errors are generated and I am dumbfounded as to what my problem is. I
have proven (at least I think) that the channel is still open - any
attempt to close it on the server side cause an application crash.

So when I went to MS with this problem, they never even looked at the
code. They simply said that it was a bad idea and that I should abort,
citing that KB article.

Assuming that the message is being sent up the channel here is the failing
code on the server side:
[snip]

Looks like something it's going to be hard to get to the bottom of without
running the entire platform and debugging it (something of course I cannot
do).

If something works on the first hit and not in the second, and you're
working with multiple threads, usually this is a sign that you're not
accepting the second request, perhaps because you're no longer listening for
it. It's quite a common thing that crops up when writing a TcpServer. Not
accepting the connection and beginning to listen for new ones again would
result in this kind of behavior (although we're of course talking about a
different technology here).

Are you saying that:

"Server PC" talks to "Client PC" over RDP. "Client PC" opens up UI and
requires human interactions, "Client PC" returns said data to "Server PC"
across the RDP channel?

Or...

"Server PC" just talks to the "Client PC" in order to retrieve something it
knows to be present (a file, a setting) and "Client PC" immediately returns
the data across RDP?

I'm still trying to clear up in my head exactly what you're trying to
achieve. It seems there are probably better technologies for the client ->
server relationship - WCF comes to mind.
 
A

amdrit

The first option is the goal. We present the user with a UI on the local
machine to complete the server request - operating a scanner for example.

Aye, I believe that it's simply a matter of not listening for subsequent
responses. And you are correct, loading the entire platform in a debugging
environment is challenging and complex - but that is where I am at now. I
am hoping that I simply fat fingered something and am just not noticing it.
Though I rewrote this piece from scratch to avoid the isolated testing
complexities - to no avail.

We had looked at WCF, TCPSockets and Web Services as an alternative
solution. I still have the original prototypes tucked away in source
control. There were two major down sides to these approaches.

1. Additional configuration and maitenance.
2. Pairing an RDP session with the local machine.

Definately these are doable solutions, but virtual channels provides it all
out of the box. We decided that was the route we wanted to tap into.


Mike Lovell said:
amdrit said:
This whole thing started after I read the article:
http://www.codeproject.com/KB/system/TSAddinInCS.aspx?msg=3401010#xx3401010xx

I have since implemented this concept into my application. We currently
have a hosted VB6 application that rests on a Citrix server that speaks
to a VB6 applicationi on the the local machine using the Citrix Virtual
channels. Granted the virtual channel component was written in C. None
the less, our clients are asking for an RDP option as well.

Upon reading that article, we set out to prototype this solution and
asked MS to assist use with the the finer points 2 years ago. At the
time, no one said hey this is a very bad idea. The prototype is fairly
simple but it works none the less.

Now that we have the pieces in place for the testing stage, I encountered
an odd behavior. The first time the hosted application requests the
local machine to perform a task the local machine does so and then sends
a result back to the hosted app. The workflow is that the local machine
then closes the UI. Any subsequent call from the hosted app still
invokes the local machine's UI, however, the result information is never
recieved by the hosted application.

I have verified that the message is generated and sent to the channel. I
have verified that the channel receives the message and calls the send
message api. The server side of the channel never recieves the data. No
errors are generated and I am dumbfounded as to what my problem is. I
have proven (at least I think) that the channel is still open - any
attempt to close it on the server side cause an application crash.

So when I went to MS with this problem, they never even looked at the
code. They simply said that it was a bad idea and that I should abort,
citing that KB article.

Assuming that the message is being sent up the channel here is the
failing code on the server side:
[snip]

Looks like something it's going to be hard to get to the bottom of without
running the entire platform and debugging it (something of course I cannot
do).

If something works on the first hit and not in the second, and you're
working with multiple threads, usually this is a sign that you're not
accepting the second request, perhaps because you're no longer listening
for it. It's quite a common thing that crops up when writing a TcpServer.
Not accepting the connection and beginning to listen for new ones again
would result in this kind of behavior (although we're of course talking
about a different technology here).

Are you saying that:

"Server PC" talks to "Client PC" over RDP. "Client PC" opens up UI and
requires human interactions, "Client PC" returns said data to "Server PC"
across the RDP channel?

Or...

"Server PC" just talks to the "Client PC" in order to retrieve something
it knows to be present (a file, a setting) and "Client PC" immediately
returns the data across RDP?

I'm still trying to clear up in my head exactly what you're trying to
achieve. It seems there are probably better technologies for the
client -> server relationship - WCF comes to mind.
 
M

Mike Lovell

The first option is the goal. We present the user with a UI on the local
machine to complete the server request - operating a scanner for example.

Aye, I believe that it's simply a matter of not listening for subsequent
responses. And you are correct, loading the entire platform in a
debugging environment is challenging and complex - but that is where I am
at now. I am hoping that I simply fat fingered something and am just not
noticing it. Though I rewrote this piece from scratch to avoid the
isolated testing complexities - to no avail.

We had looked at WCF, TCPSockets and Web Services as an alternative
solution. I still have the original prototypes tucked away in source
control. There were two major down sides to these approaches.

1. Additional configuration and maitenance.
2. Pairing an RDP session with the local machine.

Definately these are doable solutions, but virtual channels provides it
all out of the box. We decided that was the route we wanted to tap into.

In which case, I think what I'd do it open up the server application and add
"Debug.WriteLine" to everything related to the channel..

ServerListening
ClientIncoming <id>
ClientAccepted <id>
ClientDataReceived <data>
ClientDataSent <data>
ClientClosed <id>

And the status of the channel before, during and after the interaction with
the UI. I'd do this on both the client and server (obviously the client
would not be listening but you get the idea).

Then I'd setup a tracer on debug to dump to a log file, and "tail -f" it on
both machines while I run my tests

Now if the server (the end user PC) goes:

ServerListening -> ClientIncoming -> ??? -> ClientClose -> Server Listening
.... repeat ...

You're good, if it doesn't start listening against after the connection,
then that isolates the problem a little bit.

Then pack between ClientClosed and where 'ServerListening' SHOULD be with as
much debug output as possible and delve into it that way.


Yes it's a bit of a nightmare when you get so far and then hit a
functionality brick wall like this.
 
W

wlburgess

amdrit said:
This whole thing started after I read the article:
http://www.codeproject.com/KB/system/TSAddinInCS.aspx?msg=3401010#xx3401010xx

I have since implemented this concept into my application. We currently
have a hosted VB6 application that rests on a Citrix server that speaks to a
VB6 applicationi on the the local machine using the Citrix Virtual channels.
Granted the virtual channel component was written in C. None the less, our
clients are asking for an RDP option as well.

Upon reading that article, we set out to prototype this solution and asked
MS to assist use with the the finer points 2 years ago. At the time, no one
said hey this is a very bad idea. The prototype is fairly simple but it
works none the less.

Now that we have the pieces in place for the testing stage, I encountered an
odd behavior. The first time the hosted application requests the local
machine to perform a task the local machine does so and then sends a result
back to the hosted app. The workflow is that the local machine then closes
the UI. Any subsequent call from the hosted app still invokes the local
machine's UI, however, the result information is never recieved by the
hosted application.

I have verified that the message is generated and sent to the channel. I
have verified that the channel receives the message and calls the send
message api. The server side of the channel never recieves the data. No
errors are generated and I am dumbfounded as to what my problem is. I have
proven (at least I think) that the channel is still open - any attempt to
close it on the server side cause an application crash.

So when I went to MS with this problem, they never even looked at the code.
They simply said that it was a bad idea and that I should abort, citing that
KB article.

Assuming that the message is being sent up the channel here is the failing
code on the server side:

void workerThread_DoWork(object sender, DoWorkEventArgs e)
{
//Variable Declaration Section.
bool success = false;
IntPtr intHandlePtr;
byte[] bytes = new byte[2048];
uint bytesread = 0;
int bytesRead = 0;
bool bContinue = true;
BackgroundWorker bgw = (BackgroundWorker)sender;

if (!bgw.CancellationPending)
{
//Query the Virtual Channel and get the underlying File Handle.
success = WtsApi32.WTSVirtualChannelQuery(
mHandle,
(int)WtsApi32.WTS_VIRTUAL_CLASS.WTSVirtualFileHandle,
out intHandlePtr,
ref bytesRead
);
//intHandlePtr is a double pointer buffer in the unmanaged world.
Retrieve the File Handle Pointer
//correctly by marshalling it.
IntPtr pFileHandle = Marshal.ReadIntPtr(intHandlePtr);

// Create an manual-reset event, which is initially set to Non
Signalled.
IntPtr myEventHandle = Kernel32.CreateEvent(IntPtr.Zero, false,
false, "MyEvent");

//Declare and initialize the OVERLAPPED structure and initialize it
correctly.
System.Threading.NativeOverlapped ovr = new
System.Threading.NativeOverlapped();
ovr.InternalHigh = IntPtr.Zero;
ovr.InternalLow = IntPtr.Zero;
ovr.OffsetHigh = 0;
ovr.OffsetLow = 0;
ovr.EventHandle = IntPtr.Zero;

//Assign the Created Event handle in the Overlapped structure.
ovr.EventHandle = myEventHandle;

while (bContinue == true)
{
//Reset the event.Still the client is in progress.
Kernel32.ResetEvent(ovr.EventHandle);

//Read any Progress data from the channel if any.
bool b = Kernel32.ReadFile(pFileHandle, bytes, (uint)bytes.Length,
out bytesread, ref ovr);

if (this.OnMessageReceived != null)
{
try
{
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
System.IO.MemoryStream ms = new System.IO.MemoryStream(bytes);
SimpleMessage sr = (SimpleMessage)bf.Deserialize(ms);

//Notify the calling app that we have new data
this.OnMessageReceived(bytes);

bContinue = false;
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.ToString());
}
if (bgw.CancellationPending) break;
}
//If not wait till the data arrives.Once the data arrives , the
event handle in the overlapped
//structure will get signaled and the WaitforSingleObject will
continue going for the next data.
int i = Kernel32.WaitForSingleObject(ovr.EventHandle,
(int)Kernel32.INFINITE);
}

//Thread is going to exit.Release the Pointer obtained.
WtsApi32.WTSFreeMemory(intHandlePtr);

//Close the channel here depending upon your requirement.
WtsApi32.WTSVirtualChannelClose(intHandlePtr);
}

}


Mike Lovell said:
What was the problem?


I think it's likely we'd need more details on what you're exactly trying
to do (if you can disclose this information).

Perhaps the lines of source relevant to the problem.


.

I have the same issue.
I used the same codeproject source for the same reason.
Then I really wanted threading to work correctly (because RDP Virtual
Channels can be a little lame across the various platforms -- like a lot of
success where none should exist).
Anyway, your ideas were important to me because WTSVirtualChannelRead is not
thread-safe, and that explains some of the hit&miss I see in using it in
thread (which is part of that article).
I had 2 goals, research the overlapped method (leading me to your post via
google), and avoiding the term "unsafe" in C#.
I started with your problem and dug deaper into Overlapped, which is where I
think your root problem is. It "seems" to be working for me (more testing
ongoing), once all the rules for using native overlapped are followed. I'll
let you be the judge. Here's some code:

// Note: IntPtr _vcFileHandle;
// Note: AutoResetEvent _eventOverlappedRead = new AutoResetEvent(false);
// may be better to use ManualReset, but the code structure should support
AutoReset correctly

private void vcReadThread_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;

// Extract the argument.
int arg = (int)e.Argument;

IntPtr h = _eventOverlappedRead.Handle; // causes warning - should use
SafeWaitHandle instead -- later improvement

//Declare and initialize the OVERLAPPED structure and initialize it
correctly.
NativeOverlapped ovr = new NativeOverlapped();
ovr.InternalHigh = IntPtr.Zero;
ovr.InternalLow = IntPtr.Zero;
ovr.OffsetHigh = 0;
ovr.OffsetLow = 0;
ovr.EventHandle = IntPtr.Zero;

//Assign the Created Event handle in the Overlapped structure.
ovr.EventHandle = h;

IntPtr data = Marshal.AllocHGlobal(1600); // VERY IMPORTANT -- Must PIN
the memory usage in .NET if using Overlapped IO

uint bytesread = 0;
while (!bw.CancellationPending && this._mHandle != IntPtr.Zero) //
acutally use the Handle from WTSVirtualChannelQuery which is _vcFileHandle
{
//Read any Progress data from the channel if any.
bool b = winapi.ReadFile(_vcFileHandle, data, 1600, out bytesread,
ref ovr);
if (b)
{
// result is immediately available
}
else
{
int lastError = Marshal.GetLastWin32Error();
if(lastError == winapi.ERROR_IO_PENDING)
{
// this is OK -- time to wait on the overlapped event handle
eventOverlappedRead.WaitOne();
bool rc = winapi.GetOverlappedResult(_vcFileHandle, ref ovr,
out bytesread, false);
if(!rc)
{
this.Log("Error in GetOverlappedResult: error=" +
lastError.ToString());
}
}
else
{
// bad news
bytesread = 0;
this.Log("Error in ReadFile: error=" + lastError.ToString());
}
}

if (bytesread > 0) // This method of using bytesread is important
because it works for both immediate return and from GetOverlappedResult
{
byte[] buff = new byte[bytesread];
Marshal.Copy(data, buff, 0, (int)bytesread);
lock (this._qReadBuffer)
{
this._qReadBuffer.AddRange(buff);
}
}
}

Marshal.FreeHGlobal(data);

if (bw.CancellationPending)
{
e.Cancel = true;
}
}
 
A

amdrit

Interesting.

Thanks for the input, I'll put this to the test. Did you have any
conclusions in your testing?


wlburgess said:
amdrit said:
This whole thing started after I read the article:
http://www.codeproject.com/KB/system/TSAddinInCS.aspx?msg=3401010#xx3401010xx

I have since implemented this concept into my application. We currently
have a hosted VB6 application that rests on a Citrix server that speaks
to a
VB6 applicationi on the the local machine using the Citrix Virtual
channels.
Granted the virtual channel component was written in C. None the less,
our
clients are asking for an RDP option as well.

Upon reading that article, we set out to prototype this solution and
asked
MS to assist use with the the finer points 2 years ago. At the time, no
one
said hey this is a very bad idea. The prototype is fairly simple but it
works none the less.

Now that we have the pieces in place for the testing stage, I encountered
an
odd behavior. The first time the hosted application requests the local
machine to perform a task the local machine does so and then sends a
result
back to the hosted app. The workflow is that the local machine then
closes
the UI. Any subsequent call from the hosted app still invokes the local
machine's UI, however, the result information is never recieved by the
hosted application.

I have verified that the message is generated and sent to the channel. I
have verified that the channel receives the message and calls the send
message api. The server side of the channel never recieves the data. No
errors are generated and I am dumbfounded as to what my problem is. I
have
proven (at least I think) that the channel is still open - any attempt to
close it on the server side cause an application crash.

So when I went to MS with this problem, they never even looked at the
code.
They simply said that it was a bad idea and that I should abort, citing
that
KB article.

Assuming that the message is being sent up the channel here is the
failing
code on the server side:

void workerThread_DoWork(object sender, DoWorkEventArgs e)
{
//Variable Declaration Section.
bool success = false;
IntPtr intHandlePtr;
byte[] bytes = new byte[2048];
uint bytesread = 0;
int bytesRead = 0;
bool bContinue = true;
BackgroundWorker bgw = (BackgroundWorker)sender;

if (!bgw.CancellationPending)
{
//Query the Virtual Channel and get the underlying File Handle.
success = WtsApi32.WTSVirtualChannelQuery(
mHandle,
(int)WtsApi32.WTS_VIRTUAL_CLASS.WTSVirtualFileHandle,
out intHandlePtr,
ref bytesRead
);
//intHandlePtr is a double pointer buffer in the unmanaged world.
Retrieve the File Handle Pointer
//correctly by marshalling it.
IntPtr pFileHandle = Marshal.ReadIntPtr(intHandlePtr);

// Create an manual-reset event, which is initially set to Non
Signalled.
IntPtr myEventHandle = Kernel32.CreateEvent(IntPtr.Zero, false,
false, "MyEvent");

//Declare and initialize the OVERLAPPED structure and initialize
it
correctly.
System.Threading.NativeOverlapped ovr = new
System.Threading.NativeOverlapped();
ovr.InternalHigh = IntPtr.Zero;
ovr.InternalLow = IntPtr.Zero;
ovr.OffsetHigh = 0;
ovr.OffsetLow = 0;
ovr.EventHandle = IntPtr.Zero;

//Assign the Created Event handle in the Overlapped structure.
ovr.EventHandle = myEventHandle;

while (bContinue == true)
{
//Reset the event.Still the client is in progress.
Kernel32.ResetEvent(ovr.EventHandle);

//Read any Progress data from the channel if any.
bool b = Kernel32.ReadFile(pFileHandle, bytes,
(uint)bytes.Length,
out bytesread, ref ovr);

if (this.OnMessageReceived != null)
{
try
{

System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
bf = new
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
System.IO.MemoryStream ms = new
System.IO.MemoryStream(bytes);
SimpleMessage sr = (SimpleMessage)bf.Deserialize(ms);

//Notify the calling app that we have new data
this.OnMessageReceived(bytes);

bContinue = false;
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.ToString());
}
if (bgw.CancellationPending) break;
}
//If not wait till the data arrives.Once the data arrives , the
event handle in the overlapped
//structure will get signaled and the WaitforSingleObject will
continue going for the next data.
int i = Kernel32.WaitForSingleObject(ovr.EventHandle,
(int)Kernel32.INFINITE);
}

//Thread is going to exit.Release the Pointer obtained.
WtsApi32.WTSFreeMemory(intHandlePtr);

//Close the channel here depending upon your requirement.
WtsApi32.WTSVirtualChannelClose(intHandlePtr);
}

}


Mike Lovell said:
I've been working on an RDP plug-in solution written in C#. The
concept
came from a CodeProject article. I encountered a problem in testing
and
so opened a case with MS. Rather than looking into the problem they
cited a KB article http://support.microsoft.com/kb/841927.

What was the problem?

My question is, does anyone have anything to support that MSTSC.EXE is
included amoung the executables that require this special
consideration?

I've spent a lot of time on this project and it seems now that I have
wasted a lot of company time. I would like to exhaust all options
before
looking to go to a pure c solution. No one on my team has the
experience
to create the components we require in c.

I think it's likely we'd need more details on what you're exactly
trying
to do (if you can disclose this information).

Perhaps the lines of source relevant to the problem.


.

I have the same issue.
I used the same codeproject source for the same reason.
Then I really wanted threading to work correctly (because RDP Virtual
Channels can be a little lame across the various platforms -- like a lot
of
success where none should exist).
Anyway, your ideas were important to me because WTSVirtualChannelRead is
not
thread-safe, and that explains some of the hit&miss I see in using it in
thread (which is part of that article).
I had 2 goals, research the overlapped method (leading me to your post via
google), and avoiding the term "unsafe" in C#.
I started with your problem and dug deaper into Overlapped, which is where
I
think your root problem is. It "seems" to be working for me (more testing
ongoing), once all the rules for using native overlapped are followed.
I'll
let you be the judge. Here's some code:

// Note: IntPtr _vcFileHandle;
// Note: AutoResetEvent _eventOverlappedRead = new AutoResetEvent(false);
// may be better to use ManualReset, but the code structure should support
AutoReset correctly

private void vcReadThread_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;

// Extract the argument.
int arg = (int)e.Argument;

IntPtr h = _eventOverlappedRead.Handle; // causes warning - should use
SafeWaitHandle instead -- later improvement

//Declare and initialize the OVERLAPPED structure and initialize it
correctly.
NativeOverlapped ovr = new NativeOverlapped();
ovr.InternalHigh = IntPtr.Zero;
ovr.InternalLow = IntPtr.Zero;
ovr.OffsetHigh = 0;
ovr.OffsetLow = 0;
ovr.EventHandle = IntPtr.Zero;

//Assign the Created Event handle in the Overlapped structure.
ovr.EventHandle = h;

IntPtr data = Marshal.AllocHGlobal(1600); // VERY IMPORTANT -- Must
PIN
the memory usage in .NET if using Overlapped IO

uint bytesread = 0;
while (!bw.CancellationPending && this._mHandle != IntPtr.Zero) //
acutally use the Handle from WTSVirtualChannelQuery which is _vcFileHandle
{
//Read any Progress data from the channel if any.
bool b = winapi.ReadFile(_vcFileHandle, data, 1600, out bytesread,
ref ovr);
if (b)
{
// result is immediately available
}
else
{
int lastError = Marshal.GetLastWin32Error();
if(lastError == winapi.ERROR_IO_PENDING)
{
// this is OK -- time to wait on the overlapped event
handle
eventOverlappedRead.WaitOne();
bool rc = winapi.GetOverlappedResult(_vcFileHandle, ref
ovr,
out bytesread, false);
if(!rc)
{
this.Log("Error in GetOverlappedResult: error=" +
lastError.ToString());
}
}
else
{
// bad news
bytesread = 0;
this.Log("Error in ReadFile: error=" +
lastError.ToString());
}
}

if (bytesread > 0) // This method of using bytesread is important
because it works for both immediate return and from GetOverlappedResult
{
byte[] buff = new byte[bytesread];
Marshal.Copy(data, buff, 0, (int)bytesread);
lock (this._qReadBuffer)
{
this._qReadBuffer.AddRange(buff);
}
}
}

Marshal.FreeHGlobal(data);

if (bw.CancellationPending)
{
e.Cancel = true;
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top