Thanks for the input, I'll put this to the test. Did you have any
conclusions in your testing?
"wlburgess" <***@discussions.microsoft.com> wrote in message
> "amdrit" wrote:
>> This whole thing started after I read the article:
>> 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
>> Granted the virtual channel component was written in C. None the less,
>> clients are asking for an RDP option as well.
>> Upon reading that article, we set out to prototype this solution and
>> MS to assist use with the the finer points 2 years ago. At the time, no
>> 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
>> 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
>> back to the hosted app. The workflow is that the local machine then
>> 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
>> 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
>> They simply said that it was a bad idea and that I should abort, citing
>> KB article.
>> Assuming that the message is being sent up the channel here is the
>> code on the server side:
>> void workerThread_DoWork(object sender, DoWorkEventArgs e)
>> //Variable Declaration Section.
>> bool success = false;
>> IntPtr intHandlePtr;
>> byte bytes = new byte;
>> 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(
>> 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
>> IntPtr myEventHandle = Kernel32.CreateEvent(IntPtr.Zero, false,
>> false, "MyEvent");
>> //Declare and initialize the OVERLAPPED structure and initialize
>> System.Threading.NativeOverlapped ovr = new
>> 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.
>> //Read any Progress data from the channel if any.
>> bool b = Kernel32.ReadFile(pFileHandle, bytes,
>> out bytesread, ref ovr);
>> if (this.OnMessageReceived != null)
>> bf = new
>> System.IO.MemoryStream ms = new
>> SimpleMessage sr = (SimpleMessage)bf.Deserialize(ms);
>> //Notify the calling app that we have new data
>> bContinue = false;
>> catch (Exception ex)
>> 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,
>> //Thread is going to exit.Release the Pointer obtained.
>> //Close the channel here depending upon your requirement.
>> "Mike Lovell" <***@gotinker.com> wrote in message
>> >> 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.
>> > --
>> > Mike
>> > GoTinker, C# Blog
>> > http://www.gotinker.com
> 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
> success where none should exist).
> Anyway, your ideas were important to me because WTSVirtualChannelRead is
> 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
> 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.
> 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
> 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
> 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
> int lastError = Marshal.GetLastWin32Error();
> if(lastError == winapi.ERROR_IO_PENDING)
> // this is OK -- time to wait on the overlapped event
> bool rc = winapi.GetOverlappedResult(_vcFileHandle, ref
> out bytesread, false);
> this.Log("Error in GetOverlappedResult: error=" +
> // bad news
> bytesread = 0;
> this.Log("Error in ReadFile: error=" +
> 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)
> if (bw.CancellationPending)
> e.Cancel = true;