If you’re application is running in a remote session (Terminal Services or RDP) and you need to get the name of the remote client you can use the attached class to call WTSQuerySessionInformation to get the information.
A couple of caveats, this code dynamically loads WTS32API.DLL which contains WTSQuerySessionInformation. This API wasn’t introduced until Windows Vista and Windows Server 2008.
The class uses @julesVerne LoadLib class to dynamically load the DLL. If you do not have access to that class then you will need to re-work the LoadLibrary calls. Alternatively, if you know your application will always run on newer operating systems and you can replace the dynamic loading with traditional statically linked API calls.
Seconds, this class uses my String Class. You can find that on ClarionMag or replace calls with StringTheory. UP_System_TS.inc (2.3 KB) UP_System_TS.clw (4.2 KB)
If you’re basing that on the MSDN page often it is updated when support is dropped for an OS.
IIRC I used it in Win 5. A quick Google search for “WTSQuerySessionInformation Windows 2000” turned up pages confirming so unless you need NT 4 you’re fine in 2k, Server 2000, 2003+, XP
I know I wrote it for Windows 2000 that had the first WTS server. I agree its safe to static link. But then you need to ship a LIB with your Class so from source control POV easier to LoadLib. Be nice to have a way specify the LIB in code like Pragma('MakeLib UpWTS.LIB; WTSAPI32.DLL; WTSQuerySessionInformationA; WTSFreeMemory'). Also add Delay Loading
I looked at my code for this and it’s a bit different. As you know WTS returns a Memory Address and Length of the Client Name. Your code used a NEW CString and MemCpy() to make a Clarion CString to read that address.
An alternative is to Reference assign the &CString &= (Address) &':'& Size. The Size is not required but it makes a safer reference that will not overrun the Size.
UP_System_TS.GetClientName PROCEDURE() !,STRING
!orig! ClientName UP_StringClass
ClientName STRING(32) !Max Computer Name is 15
BufferPtr long
BufferLength ULONG
BufferRef &CSTRING
code
if fpWTSQuerySessionInformation
if upapi_WTSQuerySessionInformation(upapi_WTS_CURRENT_SERVER_HANDLE,upapi_WTS_CURRENT_SESSION,upapi_WTSClientName,BufferPtr,BufferLength) |
and BufferPtr <> 0 | !Check for bad pointer returned
and BufferLength > 1 THEN !Have note from 2010 saw Length of 1 returned that was bad so check > 1 ??
!orig! BufferRef &= new(cstring(BufferLength+1))
!orig! memcpy(address(BufferRef), BufferPtr, BufferLength)
!orig! ClientName.Assign(BufferRef)
!orig! dispose(BufferRef)
BufferRef &= (BufferPtr) &':'& BufferLength !9/4 edit remove " +1 " !<-- &CString &= Address
ClientName = BufferRef
upapi_WTSFreeMemory(BufferPtr)
end
end
return CLIP(ClientName) !orig! ClientName.Get()
I can’t find &= (Address) documented but I’m sure I’ve seen it shown at DevCon by DAB and also by Alexey in comp.lang.
The returned Client Name is defined as a null terminated string. Typically Windows functions that return (or *out) a byte count (string length) do Not include the trailing null; otherwise, an empty string would return 1. I see nothing in the remarks.
I don’t have a WTS server to test with, maybe @Rick_UpperPark can test.
9/4 Edit:
After I replied I was troubled by the parameter being called “Bytes Returned” and not “String Length”. Also the description on MSDN seems clear it’s size not length. Jeff is right the count included the Null byte.
MSDN
ppBuffer
A pointer to a variable that receives a pointer to the requested information. The format and contents of the data depend on the information class specified in the WTSInfoClass parameter.
pBytesReturned
A pointer to a variable that receives the size, in bytes, of the data returned in ppBuffer .
That doesn’t always mean that there actually IS space allocated beyond that buffer for an extra null. There’s no guarantee what exists beyond the length that you are provided by an API.
I do like the &= technique and use it a lot, but you have to tread carefully with buffers that aren’t your own.
Very interesting discussion.
I agree with Jeff, you should not make any assumptions about the memory contents past the buffer length returned by any API.
Additionally, the call to WTSQuerySessionInformation does not just return the address of null terminated strings in BufferRef. It can be the offset of structures like WTSClient, WTSInfo, WTSConfigInfo, etc.
Turns out there is a bug in my original code. The BufferLength returned by the call includes the trailing NULL character. My workstation name is DIMHOLT which is 7 characters. BufferLength is returning 8 when I call to get the client name. No harm in the bug, other than allocating an extra byte of memory for a few milliseconds.
I thought it might be interesting to show how I examined the memory returned by the API call to answer the question of the buffer length and NULL character. Here’s an explanation of the techniques.
My code ran on 1000’s of systems and worked. I have a note from 2010 that I had one site where the Buffer Size returned was 1. I did not note the String value but I would guess it was CHR(0).
Since Clarion does not allow a CSTRING(1) it would be best for your code to only process the Client Name IF Bytes Returned > 1.