$theTitle=wp_title(" - ", false); if($theTitle != "") { ?>
About Virtualization, VDI, SBC, Application Compatibility and anything else I feel like
19 Nov // php the_time('Y') ?>
Hello, my name is Danila Galimov and i will write here sometimes 🙂
My first post is about communications between Terminal Server sessions and Terminal Server service process (termsrv.exe/dll). Terminal Server service needs to communicate with each session for many tasks, such as sending window message, getting message reply and so on. So, on init, Terminal Server creates a SmSsWinStationApiPort port in global namespace and runs a few WinStationLpcThread threads, which are listening on port and are used to process port messages. When csrss.exe is started, it parses its command line, which usually looks like:
%SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=4096,4096,1024 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ProfileControl=Off MaxRequestThreads=16
and loads the required dlls (winsrv.dll in our case). Initialization of winsrv.dll creates a thread, which connects to SmSsWinStationApiPort port and does the loop for processing Terminal Server messages until it receives WinStationTerminate message.
We’ll try to fool Terminal Server (more…)
1 May // php the_time('Y') ?>
Recently I needed to convert a C header file to Delphi which contained bitfields. Let’s take a look at a sample structure that contains bitfields:
1 2 3 4 5 6 7 | typedef struct _BITFIELDSTRUCTURE { DWORD dwValue1; ULONG BitValue1: 1; ULONG BitValue2: 1; ULONG BitValue3: 1; ULONG BitValue4: 1; } BITFIELDSTRUCTURE, * BITFIELDSTRUCTURE; |
It means that there is a DWORD (Cardinal) dwValue1 followed by a bitfield with the size of a ULONG (32 bits). In this bitfield 4 values are defined (BitValue1..4) which are used as boolean’s because the value can offcourse be 0 or 1. Since Delphi doesn’t know a bitfield type the question is how to translate it. Usually it would mean that we simply treat the whole bitfield value as a ULONG and extract the required properties by applying a bitmask (shl/shr). Starting from BDS2006 we can define a record with propertes and use getters and setters. Using this technique we can present boolean values to the user:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | type _BITFIELDSTRUCTURE = record dwValue1: DWORD; strict private BitField: DWORD; function GetBitValue1: Boolean; function GetBitValue2: Boolean; function GetBitValue3: Boolean; function GetBitValue4: Boolean; procedure SetBitValue1(const Value: Boolean); procedure SetBitValue2(const Value: Boolean); procedure SetBitValue3(const Value: Boolean); procedure SetBitValue4(const Value: Boolean); public property BitValue1: Boolean read GetBitValue1 write SetBitValue1; property BitValue2: Boolean read GetBitValue2 write SetBitValue2; property BitValue3: Boolean read GetBitValue3 write SetBitValue3; property BitValue4: Boolean read GetBitValue4 write SetBitValue4; end; TBitFieldStructure = _BITFIELDSTRUCTURE; PBitFieldStructure = ^_BITFIELDSTRUCTURE; |
Code completion shows that the record has one DWORD Value and 4 Boolean Values which is just what we want!
Offcourse we need to implement the Getters and Setters:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | function _BITFIELDSTRUCTURE.GetBitValue1; begin Result := BitField and 1 = 1; end; function _BITFIELDSTRUCTURE.GetBitValue2; begin Result := BitField and 2 = 2; end; function _BITFIELDSTRUCTURE.GetBitValue3; begin Result := BitField and 4 = 4; end; function _BITFIELDSTRUCTURE.GetBitValue4; begin Result := BitField and 8 = 8; end; procedure _BITFIELDSTRUCTURE.SetBitValue1(const Value: Boolean); begin if Value then BitField := BitField or 1 else BitField := BitField and (not 1); end; procedure _BITFIELDSTRUCTURE.SetBitValue2(const Value: Boolean); begin if Value then BitField := BitField or 2 else BitField := BitField and (not 2); end; procedure _BITFIELDSTRUCTURE.SetBitValue3(const Value: Boolean); begin if Value then BitField := BitField or 4 else BitField := BitField and (not 4); end; procedure _BITFIELDSTRUCTURE.SetBitValue4(const Value: Boolean); begin if Value then BitField := BitField or 8 else BitField := BitField and (not 8); end; |
We can even add a constructor to it, this can be used to e.g. initialize the record (in the example below we fill with zeroes). Note that only a constructor with at least one argument can be used:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | ... public constructor Create(const dummy: word); ... implementation constructor _BITFIELDSTRUCTURE.Create; // Did you know that Delphi permits leaving out (const dummy: word) here? begin ZeroMemory(@Self, SizeOf(Self)); end; ... procedure TForm1.Button1Click(Sender: TObject); var BitFieldStructure: TBitFieldStructure; begin BitFieldStructure := TBitFieldStructure.Create(0); |
So why not use a class instead of record? The answer is that a class is just a pointer we can never pass this to a function, procedure or api call that expects a record. But if we want to support older Delphi versions, like Delphi 6 or Delphi 7 and even Delphi 2005, which are still used a lot we need to find another solution. I came up with (ab)using sets to emulate bitfields, we can do this because a set is actually a set of bits (limited to 256 bits). The example structure could look like this if we use sets:
1 2 3 4 5 6 7 8 | _BITFIELDSTRUCT = record dwValue1: DWORD; BitField: Set Of ( BitValue1, BitValue2, BitValue3, BitValue4 ); end; TBitFieldStruct = _BITFIELDSTRUCT; PBitFieldStruct = ^_BITFIELDSTRUCT; |
We can use normal set operations to get and set bitvalues:
1 2 3 4 5 6 7 8 9 | procedure TForm1.Button2Click(Sender: TObject); var bValue: Boolean; BitFieldStruct: TBitFieldStruct; begin bValue := BitValue2 in BitFieldStruct.BitField; BitFieldStruct.BitField := [BitValue1, BitValue3]; BitFieldStruct.BitField := BitFieldStruct.BitField - [BitValue3]; end; |
Settings like minimal enum size and record alignment are important because we need to asssure that te record size matches the C structure’s size (especially when using structures with a lot of bitfields. I choose to do this with a litte trick, first I declare some constants:
1 2 3 4 5 6 7 8 9 | const al32Bit=31; al64bit=63; al96bit=95; al128bit=127; al160bit=159; al192bit=191; al224bit=221; al256bit=255; |
We use these constants to force the correct size, in the example the bitfield was a ULONG which is 32 bits. We add the al32Bit constant to the bitfield:
1 2 3 4 5 6 7 8 | _BITFIELDSTRUCT = record dwValue1: DWORD; BitField: Set Of ( BitValue1, BitValue2, BitValue3, BitValue4, al32Bit ); end; TBitFieldStruct = _BITFIELDSTRUCT; PBitFieldStruct = ^_BITFIELDSTRUCT; |
So I thought I had it figured out… until I came to this line in the C header file:
1 2 3 | ULONG SomeValue : 1; ULONG OtherValue : 1; ULONG ColorDepth : 3; |
So we have a bitfield consisting off multiple bits! This gave me some headaches but I finally came up with the following approach
1 2 3 | BitField: Set Of ( SomeValue, OtherValue, ColorDepth1, ColorDepth2, ColorDepth3, al32Bit ); |
We need a helper function to retreive the numeric value of ColorDepth:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | function ValueFromBitSet(var ABitSet; const StartBit: Byte; const Count: Byte): Int64; var MaxBitSet: TMaxBitSet; i, BitValue: Integer; begin // The result can contain max. 64 bit value, Raise if Count > 64 if Count > 64 then Raise EIntOverflow.Create('Count cannot exceed 64'); // A Delphi Set contains at most 256 bits. So we raise Exception is we exceed if StartBit + Count > 255 then Raise EIntOverflow.Create('Startbit + Count cannot exceed maximum set size (255)'); Result := 0; BitValue := 1; // A Delphi Set Of can hold a maximum of 256 bits, since we do not know // which size was passed to us we cast to 256 bits. MaxBitSet := TMaxBitSet(ABitSet); // Loop through the requested bits from end to start (Little Endian) for i := StartBit+Count-1 downto StartBit do begin // is the bit set? if i in MaxBitSet then begin // Multiply with BitValue and add to result Result := Result + BitValue; end; // Multiply BitValue by 2 BitValue := BitValue shl 1; end; end; |
The helper function is used like this:
1 2 3 | Struct.BitFields := [OtherValue, ColorDepth1, ColorDepth3]; WriteLn(Format('Value=%d', [ValueFromBitSet(Struct.BitFields, Integer(ColorDepth1), 3)])); end. |
Some limitations remain, although I don’t think you are likely to encouter these:
2 Mar // php the_time('Y') ?>
Several months ago I wrote about encrypting and decrypting RDP passwords. I left one thing open: encrypting the password up to the full 1329 bytes as mstsc does.
Many people were curious about it so I hope the answer is not a disappointment because it’s actually really simple (but I took me a while to figure that out nonetheless). In what I figure is an attempt to hide the password length mstsc always fills up the password with zeroes until it has 512 bytes length.
Then the password is encrypted like I described earlier which gives us a 1328 bytes password hash. So we have one mystery left, how to reach the 1329 bytes size which still is a strange value since the password is in Unicode which takes 2 bytes per char (so the size should be even).
As it turns out, mstsc just adds a zero!
Remote Desktop Password Encryption & Decryption Tool (66185 downloads )2 Mar // php the_time('Y') ?>
In a previous article I wrote about changes in utildll in vista that breaked compatibality for Terminal Server. Even though release notes for Service Pack 1 don’t indicate changes or fixes in this area my testing shows that Microsoft has taken over the Windows 2008 implementation of utildll to Vista.
This is a good thing, because applications depending on utildll work again. I have updated JwaWinsta for SP1, all Vista versions of the utildll functions are renamed to VistaRTM and all Safe functions were updated to check for SP1. This means that the Safe functions can be used on all OS versions and Service packs! You are strongly advised to use only the Safe functions.
Some observations with SP1:
Update: I just tried to install hotfix KB941561 but this fail with the error: The update does not apply to your system. If you do want to get this bug fixed you need to manually replace winsta.dll (take ownership and set permissions to full control). winsta.dll from hotfix KB941561 (X86) (4362 downloads )
27 Jan // php the_time('Y') ?>
I just wanted to show some of the progress that I made in development of TSAdminEx. I thought the best way would be to show some screenshots. Which reminds me I installed a nice Javascript to enlarge the thumbnails, click to see it…
Edit: A beta is ready!
This screenshot shows TSAdminEx after startup. In the Left Treeview you can see the This Computer, Favorites and All Listed Servers icon. On Startup all available domains are enumerated. | |
Here you see the Users tab. If you move the mouse over some columns you can get extra info in the hint. In this hint you get the actual shadow permissions of the highlighted session. | |
This is the Sessions tab where extra details of a session are shown. By default you can see sessions statistics such as Incomingbytes and Outgoingbytes, this makes it easy to identify sessions that have much traffic.Interesting detail is that the Remote Address column lists the real ip address that is connected to Terminal Server! Hovering the mouse also shows the port number. | |
Now the Process tab is my favorite! It lists far more details than TSAdmin and also some usefull columns that cannot be show with TSAdmin or any documented Terminal Server API!The Process Age columns shows how long the process is running. You can compare this with the CPU Time column to see how much CPU Time the process has allocated since startup.The Mem Usage shows the amount of physical RAM a process uses while the VM Size column shows the amount of Private Bytes (Virtual Memory) a process uses. | |
If you click on a domain in the Left Treeview, TSAdminEx will enumerate all Terminal Servers for that domain. You can continue using and even enumerate multiple domains, because enumeration is done from seperate threads! | |
If you select (highlight) a particular session the appropriate toolbar buttons are enabled or disabled automically indicating the actions that can be performed on the selected session. | |
And offcourse, an about dialog… |
Hope you like it! Comments are open…
25 Jan // php the_time('Y') ?>
If you develop an application for Terminal Server you might want to react on session events. This means that your application is notified when a user logs on, logs off or becomes idle. This can be done with the WTSWaitSystemEvent function. Implementing it is rather simple and could look something like this:
1 2 3 4 5 6 7 8 9 10 11 | procedure TJwWTSEventThread.Execute; begin while not Terminated do begin if WTSWaitSystemEvent(ServerHandle, WTS_EVENT_ALL, FEventFlag) then begin Synchronize(DispatchEvent); end; Sleep(10); end; end; |
Notice that you would probably do this from a seperate thread otherwise you will block the main thread. To stop waiting for Events you send a special event:
1 2 | // unblock the waiter WTSWaitSystemEvent(FServerHandle, WTS_EVENT_FLUSH, EventFlag); |
Please note that there are at least 2 issues with this API, one with Windows 2000 and one with Windows Vista. On Windows 2000 events are reported twice for each actual event. Microsoft’s resolution?
The application should expect the event twice, and filter out the second occurrence.
Now how do we solve this? I would suggest introducing a small delay after an event trigger, this way you will probably not receive the duplicate event.
On Windows Vista there’s another issue: After you set the value of the EventMask parameter to WTS_EVENT_FLUSH in the WTSWaitSystemEvent function, no pending calls to the function return on a Windows Vista-based computer. Now what does this mean? It means that after sending WTS_EVENT_FLUSH the thread never responds! So there’s actually no nice way to end the thread, the only escape is a call to TerminateThread.
Microsoft does offer a hotfix, so my suggestion is a check on startup that will notify the user that he/she needs to install the hotfix. A version check can be done on winsta.dll, the version before the fix is 6.0.6000.16386. Hotfix version is 6.0.6000.20664. According to this article the fix will be included in Vista SP1.
References:
9 Dec // php the_time('Y') ?>
Several of Microsoft’s Terminal Server tools use undocumented API’s from Utildll.dll. For instance Terminal Server Admin uses it to get a localised connect state string and to format time strings like idle time, logon time etc.
Functions below seems to be the most usefull ones, I will add those to the JwaWinsta unit:
Citrix has it’s own version of this DLL called CUtildll.dll which is similar but uses (Citrix) MUI for localising strings.
24 Nov // php the_time('Y') ?>
When an application is not Terminal Server aware (also known as a legacy application), Terminal Server makes certain modifications to the legacy application to make it work properly in a multiuser environment. For example, Terminal Server will create a virtual Windows folder, such that each user gets a Windows folder instead of getting the system’s Windows directory. This gives users access to their own INI files. In addition, Terminal Server makes some adjustments to the registry for a legacy application. These modifications slow the loading of the legacy application on Terminal Server and require up to 8 MegaBytes extra memory. This behaviour can be avoided if the TSAware flag is present in the PE header of an executable as can be read here at MSDN.
But how do we set this property in Delphi?
In Windows.pas we can see that the constant is defined:
1 | IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = $8000; |
But how to use this in your application?
Add the line
1 | {$SetPEOptFlags IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE} |
somewhere below the uses clause and we’re done!
Offcourse you are now responsible for making your application Terminal Server compliant which according to Microsoft means: If an application is Terminal Server aware, it must neither rely on INI files nor write to the HKEY_CURRENT_USER registry during setup.
9 Nov // php the_time('Y') ?>
I added some more undocumented API’s to my Jwawinsta unit, the unit is now becoming a collection of the undocumented API’s in winsta.dll.
These are the functions I added:
I also added some more parts of the undocumented structure returned by WinStationQueryInformationW, it now contains:
8 Nov // php the_time('Y') ?>
A little while ago I wrote an article on launching a process in another Terminal Session (https://www.remkoweijnen.nl/blog/2007/10/20/how-to-launch-a-process-in-a-terminal-session/).
The article didn’t have a demo app yet so I’ve attached it here. (more…)