Procedural Epistemology Thinker RSS 2.0
 Friday, August 15, 2008

Terminal Server API Programming in C# (Part 2)

This is Part II of my Terminal Server API Programming. You can read Part I here.

As to my Terminal Services code I have found that an enumeration thru the sessions was going to give me more overhead then I wanted. Therefore I found you can use the WTSQuerySessionInformation once you have the session you want. To get the session that I want I find the processId  that my current application is running and from that I use a ProcessIdToSessionId from the kernel32 assembly.

Add one more p/invoke:

[DllImport("kernel32.dll")]

        internal static extern bool ProcessIdToSessionId(uint dwProcessId, out uint pSessionId);

Download Code Here:

Let’s review the code.

First off I created a NativeMethods.cs class that houses all my Imported Dll’s I have separated this class into a safe and UnsafeNativeMethods. Any method that uses the UnmanagedType Gets placed in the UnsafeNativeMethods, which at the top of the class I use the [SuppressUnmanagedCodeSecurity] attribute.

In the SessionInfoHelper.cs Class, everything in this class supports the call to SessionInfoBag GetClientInfo(). My company wanted to make one call to this and return all the information to be used. SessionInfoBag is my custom object that houses the structure of data that I need.  Currently you will find:

SessionInfoBag:

·         SessionId – The final sessionId that the current client is using.

·         Type – Is this a RDP or ISA (Citrix)

·         User – The connected username used

·         Machine – The machine of the connected session

·         ProcessId  - Current application ProcessId

·         IsRemoteLogin – Is this connection a remote login (RDP or Citrix) or not.

I also have a ProcessInfoBag k which contains the ProcessId and the ProcessSessionId

In the Session Helper class I also wrapped the OpenServer and CloseServer Invokes just to add another level of abstraction.

Lets walk thru the control of the class.

1)      Call GetClientInfo()

a.       Create Structures

b.      Get Process Information

c.       ProcessInfoBag GetCurrentProcessInfo()//Populates ProcessInfoBag

d.      Check for a Remote Connection and set the bool if it is

                                                               i.      If this is a remote session then let’s call “GetSession” and pass in the info (we have found the correct sessionId by this point)

                                                             ii.      Else let’s populate the SessionInfoBag object with local variables.

e.      Return Client Info

In the GetSession ():

1)      Open Server

2)      Since we now know the exact session we want on the server we use a point target search. This is done with the UnsafeNativeMethods.WTSQuerySessionInformation

a.       When done with that we populate the SessionInfoBag with this returned data.

b.      Close the Server

c.       FreeUpMemory

 

It’s a fairly straight forward solution utilizing the WTS Services for both Citrix and RDP in one class.

 

 

 

Thursday, August 14, 2008 11:28:26 PM (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
Technical | Terminal Services
 Wednesday, July 09, 2008

Terminal Server API Programming in C# (Part I)

 

Sorry for such a late response on my blog. Life has been very full for me. I now have a new job as a Senior Software Engineer at a awesome company named MPi (http://mpifix.com) This company is organized and sharp. I think probably the best development environment I've been in yet. (Well see how the camaraderie is).

Why do companies use RDP?

A RDP is a multi-channel protocol that allows a user to connect to a computer running Microsoft Terminal Services for the purpose of running programs installed on the server without having to install that same program on the client machine.

For example we install our application on a server hosted in their corporate office and then a bunch of other shops can run our program on their client machines without ever having to install it. Neat-o.

Scenario:

My first task at my new job was to identify a connecting Clients Public IP address, Hostname and Username.  The client can connect using RDP or thru a Citrix Farm. So I need to determine which they are connecting with and then gleam the information.

Research:

It turns out that Microsoft has a great Terminal Services API. Here is a reference link: http://msdn.microsoft.com/en-us/library/aa383468(VS.85).aspx

As I read more about RDP I found that on every server the RDP connection connects with a type listing a 3 character prefix “RDP” and any Citrix connection connected with “ICA” for example Type: RDPClient or ICAClient. Both of these are each stored in sessions on the server. Interesting don’t you think.. hmm.

Turns out the Terminal Services API has a public method named: WTSEnumerateSessions. This method has a WTS_INFO_CLASS enumeration type that contains your structure when you retrieve the information from WTSQuerySessionInformation. The method takes:

hServer: Handle to a terminal server.

Reserved: Just know this must be zero.

Version: This is the version of the enumeration request. Must be 1

ppSessionInfo: This is a pointer to a variable that receives a pointer to an array of the WTS_SESSION_INFO (another structure for the object).

pCount: A pointer to the variable that receives the number of WTS_SESSION_INFO structure.

Lets write some psuedo code utilizing the WTSEnumerateSessions.

1)      I need to use Pinvoke to import the Terminal Services API functions to use in my code.

a.       The assembling is “wtsapi32.dll

2)      The functions I need to accomplish using WTSEnumerateSessions are:

a.      WTSOpenServer

b.      WTSCloseServer

c.       WTSEnumerateSessions

d.      WTSQuerySessionInformation

                                                               i.      I need to manually pass the correct information to WTSQuerySessionInformation to setup the use of WTSEnumerateSessions.

e.       WTSFreeMemory

                                                               i.      Because I’lll be pulling the session info using pointers then after I’m finished I need to free up the buffer again using this method.

3)      WTS_SESSION_INFO: The wtsapi32 provides me with a existing structure that I can use to store information about a client session. The name of that struct is named WTS_SESSION_INFO. * *NOTE: Structs are great when all you care about is the properties of an object and they are going to be very small. Structs get put in the heap which when the object is orphaned or not used it falls off the heap. This in turn uses less memory. ^_^ less memory is a good thing. Only use what you need. Again only use Structs when it’s a small object (16 bytes or less) AND when you only care about the properties of the object.

4)      SessionInfoBag: I also want to create a public structure for my session object so I can pass it around my code. Because I only care about the properties and everything coming back from the wtsapi32 api are structs then a public struct is the perfect choice.

5)      Wrapper classes: For good design I’m going to wrap up my WTSOpenServer, WTSCloseServer and WTSQuerySessionInformation and add another layer of abstraction between the API and my code. This way if things change I can just change the wrapper class implementation and this makes code maintaince much easier.

6)      GetRDPSessions: This will be my main Public method that will utilize WTSEnumerateSessions. The meat and potatos so to speak.

 

In Part II, I’ll start implementing the code.

Wednesday, July 09, 2008 3:19:37 PM (GMT Standard Time, UTC+00:00)  #    Comments [0] - Trackback
Technical | Terminal Services
Archive
<January 2009>
SunMonTueWedThuFriSat
28293031123
45678910
11121314151617
18192021222324
25262728293031
1234567
About the Author/Disclaimer
Currently I am a Senior Software Engineer at Mobile Productive Inc a automotive tech company. Check us out at http://www.mpifix.com

Experience
  • Project Management: 4 Years (Apple Computers)
  • Computer Instructor: 2 Years (CompUSA)
  • Developer: 4 Years (RemedyMD, HRN, MPi)

  • Education
  • B.S in Computer Science from Neumont University
  • Certificate of Continuing Education from MIT

  • Linkedin

    Disclaimer
    The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

    © Copyright 2009
    Joshua T Stroup
    Sign In
    Statistics
    Total Posts: 19
    This Year: 0
    This Month: 0
    This Week: 0
    Comments: 5
    All Content © 2009, Joshua T Stroup