- Details
- Category: Code
- By Stuart Mathews
- Hits: 2680
I’ve progressed somewhat in my recent efforts with regards to utilizing a virtual channel between receiver and the VDA. I’ve converted the driver to C++ which makes utilizing C-type syntax easier – I can declare variables and assign them in between the code for example. Anyway, the real issue I was having right was calling the C/C++ DLL from WPF/C# which establishes the connection to the driver.
So the first time I call the function in the C++ DLL that initiates the connection to the client driver, this all works, do it again and it failed. But I fixed it by doing a little googling: I was doing this from c#:
[DllImport("VDAAPPV.dll", EntryPoint = "SendAndRecieve", SetLastError = true)] private static extern int SendAndRecieve(string message, int messageLength, StringBuilder response, int responseLength); private void btnCreateVC_Click( object sender, RoutedEventArgs e ) { try { var sb = new StringBuilder(100); SendAndRecieve("Stuart Mathews is a dude","Stuart Mathews is a dude".Length, sb, sb.Capacity); Status.Content = sb.ToString(); MessageBox.Show("Result was " + sb); } catch (Exception ex) { MessageBox.Show("Error sendreceive: "+ ex.Message); } }
The problem was that the DllImport should have been like this:
[DllImport("VDAAPPV.dll", EntryPoint = "SendAndRecieve", SetLastError = true, CharSet = CharSet.Ansi)] private static extern int SendAndRecieve(string message, int messageLength, StringBuilder response, int responseLength);
Note the CharSet = CharSet.Ansi part. Thats because the C function looks like this:
extern "C" EXPORT_ME int SendAndRecieve(const char* message, int messageLength, char* responseBuf, int responseLength) { ... }
A pointer to a char is ANSI. Because I’m a fool, I forgot about this for a full day. Anyway, the results are that I can send a message to receiver from the VDA and get a response:
And that’s probably the most important step in working with the Citrix Virtual Channel SDK.
So onwards and upwards!
- Details
- Category: Code
- By Stuart Mathews
- Hits: 6652
So recently I’ve been working on implementing on a driver for Windows receiver. Its basically to setup a virtual channel between the receiver and our custom App-V application launcher on the VDA. Specifically. I’ve had the idea for a long time because I’d like to port the functionality that XenApp 6.5’s ‘offline’ App-V functionality had where you can use the local/native App-V client on the receiver machine to launch the application if you’ve got the application and App-V client locally – instead of launching the application remotely via the ICA protocol, effectively running the app-v application on the VDA(remote server) and then remoting it to the client workstation via ICA and Receiver.
The idea is basically to establish a channel of communication between receiver attempting to launch the app-v application via ICA and the process that usually launches those app-v application on the VDA. When the launcher indicates that it can and should preferably offload the launching to the app-v client on the receiver – it will tell receiver this and in response, receiver will instruct the App-A client to launch locally. If the package is not available, the channel will provide the path to the app-v package and this can then be used to locate and ‘add’ the package before locally publishing and launching it locally on the receiver machine.
The cool thing is that the whole Virtual Channel SDK is freely available from Citrix, so I was able to implement a rudimentary driver and channel that will send/receive a basic XML-type protocol back and forth between the host (VDA application) and the client (receiver driver). The samples are all written in C which is great because this fits me well. The only issue I’ve noticed about this is that the documentation has said that we should be using the Visual Studio 2010 toolset which uses C 89 standard which is sucky – like you can’t do joint variable declaration and assignments in one statement and you need to declare all your variables before you use them. Sucky. But it works for now, and I’m working on moving to C++ but still use the C subset to keep the code simple:I find using C++ only constructs early on makes things unnecessarily complex, particularly when its not complex to to begin with.
The documentation is pretty good, and its a whole lot better than the Fast Connect SDK that I’ve worked on before.
On of the interesting aspects of allowing app-v applications to “stream-to-client” which is what its called really in the XenApp 6.5 world, is what if the receiver client machine does not have access to the app-v package to have it run locally? Do you fall back to the “stream-to-server” mechanism and then have the app-v application launched on the VDA and remoted as per usual? Also what if its not a good idea to launch an app-v application locally on the receiver endpoint, even if you can – some sort of security policy or something. Clearly perhaps as part of the communication between the receiver endpoint and the VDA is reading/interpreting policy information from the XenDesktop environment about what should and should not be done.
The other always interesting aspect of networking, that I found while developing the named pipe between the broker plugin and the App-V service, is how to correctly arrange for the parties involved in the communication(both endpoints) to know how much data is available to read when data is sent from one side to the receiving side. Usually, you need to send a packet upfront which gives the receiving end a clue on how much data it is to expect from the sender – so it knows when to stop reading. This is the case when implementing raw BSD sockets and using named pipes in Windows. Though, unusually with the Citrix Virtual Channel SDK, the client driver always gets all the data provided to it, when its send from the server(VDA), it doesn’t require that you code into the protocol that a certain amount of bytes is to be expected – this seems to be coded for you in the WinStation driver engine embedded in the Receiver engine. Basically in the client driver you get a call-back called with all the data send by the server:
static void WFCAPI ICADataArrival(PVD pVD, USHORT uchan, LPBYTE pBuf, USHORT Length);
So you’ll get Length amount of data in pBuf. Thats it, you just deal with that data in pBuf.
However on the server side, you don't have this luxury meaning that any data send from the client, needs to first send an indication of how much data will be sent, then send that amount of data. So when it comes to responding to the server(VDA), the driver needs to send a response packet and also a ‘data’ packet which was described by the response packet. This is so that the server knows that it should stop reading. Quite interesting about the not needing that when going in the opposite direction!
So on the client, you need to do a read the ‘prepacket’ in this case which indicates the size of the upcoming data form the client. Then once, you’ve got that, then you need to read only that amount of data. The SendReceiveBuffer will contain the upcoming data. That structure is quite interesting too – see it later.
//Read Response // A response copnsists of two packets sent from the client.(PrePacket and a SendReceiveBuffer) // The Prepacket contains the length of data in the SendReceiveBuffer that is sent next // Read prepacket rc = WFVirtualChannelRead(hVC, VC_TIMEOUT_MILLISECONDS, (PCHAR)&prepacket, sizeof(Prepacket), &ulBytesRead); if (rc != TRUE) { _tprintf(_T("Could not receive message from virtual channel.\n")); PrintMessage(GetLastError()); SafelyExit(pBuffer, hVC); return 0; } // Read actual data rc = WFVirtualChannelRead(hVC, VC_TIMEOUT_MILLISECONDS, (PCHAR)pBuffer, prepacket.length, &ulBytesRead); if (rc != TRUE) { _tprintf(_T("Could not receive message from virtual channel.\n")); PrintMessage(GetLastError()); SafelyExit(pBuffer, hVC); return 0; } _tprintf(_T("Received Message (%s) of (%lu) bytes from virtual channel.\n"), pBuffer->payload, ulBytesRead); SafelyExit(pBuffer, hVC);
The ‘packets’ looks like this:
#pragma pack(1) typedef struct Prepacket { int length; } Prepacket; /* AppV buffer format*/ typedef struct SRB { int length; char payload[1]; } SendReceiveBuffer, *PSendReceiveBuffer;
Yes, this is just illustrative and I could probably consolidate this into just a SendReceiveBuffer and ditch the PrePacket. So anyway the client sends a prepacket and a SendReceiveBuffer as a response to the ‘host’(VDA).
One thing that can happen is that the amount of data that you want to send exceeds the number that can beheld by the int length in the Prepacket. There are some interesting ways to solve this problem. Such as determining the size of your intended data to send, and then determine how many bytes it would take to represent and store that number, and then send that number to the receiving side, then the receiving side will know it should read that many bytes into a buffer(and fetch that many bytes) and then interpret that buffer as a number – now it knows how much to receive, irrespective of the the length variable and also means you can send/receive in theory any number of bytes wihout having to artificially limit the size of the data in your protocol for example.
- Details
- Category: Code
- By Stuart Mathews
- Hits: 2426
I was again up against an interesting problem recently which was solved by going complex and then doing a full turn-around and resorting to a much simpler approach which worked well. I'm currently developing a component at work whereby it needs to interact with another component. Each of the components are separate processes and so the communication I've implemented using named pipes. The solution is in place but I came accross an interesting problem.
I've implemented the basics in a simple loop which is in a new thread that basically waits for the other component to connect to us, once it does that it processes the communication(while blocking any other connects - its synchonous and this is fine for our purposes). Once the connection is accepted and then its processed, we again wait for new connections. At this point the other component can send another message. If it had tried to while we were processing the last message, it would just wait. Anyway because the named pipes server is actually within a Windows Service, I found that this mechanism keeps the service from stopping correctly when you issue a restart/stop command from Windows. The result was that the exe that underlies the service stays running. If you start the service(while the other exe is still running) things catch on fire and people die. So. The problem was that the two processes would basically trample over each other's feet as both would be providing the named pipe connection service. I couldn't understand why this was happening - why the exe kept running. Then it hit, me. Its because the thread is stil sitting there listening for the next connection and the exe will not stop because a child thread it still running!
Initially thats great that i found a reason for it, but I then had no way to shutdown the listening thread. I was thinking that I'd need to implement the named pipes server as a asynchrnous routine as this would make it interruptable and then we can stop it listening when we get a stop command from windows. Doing that meant that I'd have to re-architect the named pipe listening thread. It was new, the code would need to be re-tested and it sucked big time for me. Then I tried to instead kill the thread by aborting it unceriomonsiously. Never a good idea but netter than re-writting everything just so can restart the service! That also started back firing as that didn't work. I went home that day pretty frustrated. i knew that I'd probably have to reimplment the functionality...
As it happens, i was in the shower as usually when i got a good idea which solves the problem and does't change the already implemented(and working/suitable) solution: Send myself a message on restart/stop that will shutdown the listening thread. Clearly the thread is waiting for a connection and preventing our thread from shutting down. So send it a message which its waiting for and let that message mean that the server doesn't have to wait anymore, so instead of it processing the message and re-listeninig - it just says 'oh this is a stop listening message'. lets break the loop now. Brilliant! That works well. The service shuts down or restarts, the thread closes itself down upon getting the 'shutdown' message (which we send to ourself) and bobs yer uncle.
So again, looking for a simple solution came from going into the depths of complexity to realize it. And as a result, it was easy to now move onto the next problem which was to send responses/acknowledgments back to the component that sends a message. Usually we'd just 'fire and forget' without the sender getting anything back after sending a message to the named pipe server. I just assumed that everything went according to plan. That was fine in the initial sprints but as the solution got more mature and the requirements started stacking up, it became essential that a acknowledgment came back with the message. So now the client waits for a reply after each message sent, and the server always responds with at least a ackolwdgement that things completed. In the case that they dont complete, the reply is an error reply and the client and determine what to do next. But I'd never had been able to start(mentally for my own sanity) this functionality had I not solved the problem with with the service restart problem. i could have but it would have been in the back of my mind all the time while doing this code.
A victory for simplicity over complexity.
More Articles …
Subcategories
Game Development Article Count: 28
I discovered the realms of game development purely by accident, having picked up a book entitled 'Core Techniques and Algorithms in Game Programming' and discovered a surprising niche of innovation in programming quite unparalleled to my day-to-day needs as a developer. Here optimisation, graphics rendering, and algorithms are used on a totally different level and its very interesting.
Page 14 of 17