gioma 19 Posted December 28, 2023 Hi, I have a big problem. I have a service that monitors sessions on a PC/Server. If I develop the application in 32 bit and it works, but if I develop the application in 64 bit it goes into error (but not always!!): Error 87 : The Parameter is incorrect This is my code: Unit Wtsapi32: unit UN_Wtsapi32; interface uses Windows; const WINSTATIONNAME_LENGTH = 32; DOMAIN_LENGTH = 17; USERNAME_LENGTH = 20; CLIENTNAME_LENGTH = 20; CLIENTADDRESS_LENGTH = 30; type WTS_INFO_CLASS = ( WTSInitialProgram, WTSApplicationName, WTSWorkingDirectory, WTSOEMId, WTSSessionId, WTSUserName, WTSWinStationName, WTSDomainName, WTSConnectState, WTSClientBuildNumber, WTSClientName, WTSClientDirectory, WTSClientProductId, WTSClientHardwareId, WTSClientAddress, WTSClientDisplay, WTSClientProtocolType, WTSIdleTime, WTSLogonTime, WTSIncomingBytes, WTSOutgoingBytes, WTSIncomingFrames, WTSOutgoingFrames, WTSClientInfo, WTSSessionInfo, WTSSessionInfoEx, WTSConfigInfo, WTSValidationInfo, WTSSessionAddressV4, WTSIsRemoteSession ); WTS_CONNECTSTATE_CLASS = ( WTSActive, WTSConnected, WTSConnectQuery, WTSShadow, WTSDisconnected, WTSIdle, WTSListen, WTSReset, WTSDown, WTSInit ); {$IFDEF UNICODE} PWTS_INFO = ^WTS_INFO; WTS_INFO = record State:WTS_CONNECTSTATE_CLASS ; SessionId: DWORD; IncomingBytes :DWORD; OutgoingBytes :DWORD; IncomingFrames:DWORD; OutgoingFrames:DWORD; IncomingCompressedBytes:DWORD; OutgoingCompressedBytes:DWORD; WinStationName: array[0..WINSTATIONNAME_LENGTH - 1] of WCHAR; Domain: array[0..DOMAIN_LENGTH - 1] of WCHAR; UserName:array[0..USERNAME_LENGTH - 1] of WCHAR; ConnectTime:LARGE_INTEGER; DisconnectTime:LARGE_INTEGER; LastInputTime:LARGE_INTEGER; LogonTime:LARGE_INTEGER; CurrentTime:LARGE_INTEGER; end; {$ENDIF} function WTSEnumerateSessions( hServer: THandle; Reserved: DWORD; Version: DWORD; var ppSessionInfo:PWTS_SESSION_INFO; var pCount: DWORD ): BOOL; stdcall; external 'Wtsapi32.dll' name {$IFDEF UNICODE}'WTSEnumerateSessionsW'{$ELSE}'WTSEnumerateSessionsA'{$ENDIF}; function WTSQuerySessionInformation( hServer: NativeUInt; SessionId: DWORD; WTSInfoClass: WTS_INFO_CLASS; var ppBuffer: PLPWSTR; var pBytesReturned: PDWORD): BOOL; stdcall; external 'Wtsapi32.dll' name {$IFDEF UNICODE}'WTSQuerySessionInformationW'{$ELSE}'WTSQuerySessionInformationA'{$ENDIF}; unit test: //... function GetSessionInfo(id:DWORD):TSessionInfo; var wpSessionInfo:PWTS_INFO; pSessionInfo:PLPWSTR; pBytesReturned:PDWORD; iderr:integer; begin Result.ID:=-1; if id >0 then begin try if (WTSQuerySessionInformation ( WTS_CURRENT_SERVER_HANDLE, id, WTS_INFO_CLASS.WTSSessionInfo, pSessionInfo, pBytesReturned) ) then begin Result.ID := id; wpSessionInfo:=PWTS_INFO(pSessionInfo); //user name Result.user:= wpSessionInfo.UserName; //station name Result.StationName:= wpSessionInfo.WinStationName; //domain name Result.domain:=wpSessionInfo.Domain; //connection status Result.ConnectState:=wpSessionInfo.State; end else begin idErr:=GetLastError; GV_TxtFileLog.Log('[GetSessionInfo] id '+intTostr(id)+' ERR : '+ intToStr(idErr)+' '+SysErrorMessage(idErr),true); end; except on e:exception do GV_TxtFileLog.Log('[GetSessionInfo] id '+intTostr(id)+' EX : '+ e.Message,true); end; end; end; function SF_GetOpenedSessions(var codeErr:integer) :TSessionsInfo; var Sessions, Session: PWTS_SESSION_INFO; NumSessions:DWORD; I:DWORD; SessionInfo:TSessionInfo; pSessionInfo:PLPWSTR; pBytesReturned:PDWORD; wpSessionInfo:PWTS_INFO; id:DWORD; begin SetLength(result,0); codeErr:=0; if WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, Sessions, NumSessions) then begin try if NumSessions > 0 then begin Session := Sessions; for i := 0 to NumSessions do begin id:=Session.SessionId; if id > 0 then begin GV_TxtFileLog.Log('[SF_GetOpenedSessions] GetSessionInfo '+ intToStr(id) ); SessionInfo := GetSessionInfo(id); if SessionInfo.ID>0 then begin SetLength(result, length(Result)+1); Result[length(Result)-1]:= SessionInfo; end; end; Inc(Session); end; end; except codeErr:=GetLastError; GV_TxtFileLog.Log('[SF_GetOpenedSessions] WTSQuerySessionInformation err : '+IntToStr(codeErr)+' '+SysErrorMessage(codeErr), true); end; WTSFreeMemory(Sessions); end else begin codeErr:=GetLastError; GV_TxtFileLog.Log('[SF_GetOpenedSessions] WTSEnumerateSessions err : '+IntToStr(codeErr)+' '+SysErrorMessage(codeErr), true); end; end; When I call the "SF_GetOpenedSessions" function I get error 87 when this function then calls the "GetSessionInfo" function. While if I use "GetSessionInfo" passing it the SID of the program it works perfectly... does anyone have an idea why? (I'm going crazy about it!) Compiling in 32 Bit, however, I have no problem. Share this post Link to post
DelphiUdIT 176 Posted December 28, 2023 3 hours ago, gioma said: While if I use "GetSessionInfo" passing it the SID of the program it works perfectly... does anyone have an idea why? (I'm going crazy about it!) None about this, but I want suggest to use the WinApi.Wtsapi32 (It is the wrapper distributed with Delphi 12). It's a little bit different from yours. 1 Share this post Link to post
Remy Lebeau 1394 Posted December 28, 2023 (edited) Your declaration of WTSQuerySessionInformation() is wrong. It should be like this instead: function WTSQuerySessionInformation( hServer: THandle; SessionId: DWORD; WTSInfoClass: WTS_INFO_CLASS; var ppBuffer: LPWSTR; var pBytesReturned: DWORD): BOOL; stdcall; external 'Wtsapi32.dll' name {$IFDEF UNICODE}'WTSQuerySessionInformationW'{$ELSE}'WTSQuerySessionInformationA'{$ENDIF}; And technically, both WTSEnumerateSessions() and WTSQuerySessionInformation() should be using 'out' parameters instead of 'var' parameters. But that is not important here. You are also leaking the memory that WTSQuerySessionInformation() returns. Try this instead: function GetSessionInfo(id: DWORD; var codeErr: Integer): TSessionInfo; var wpSessionInfo: PWTS_INFO; SessionInfo: LPWSTR; BytesReturned: DWORD; idErr: Integer; begin Result.ID := -1; try if WTSQuerySessionInformation ( WTS_CURRENT_SERVER_HANDLE, id, WTS_INFO_CLASS.WTSSessionInfo, SessionInfo, BytesReturned) then begin try Result.ID := id; wpSessionInfo := PWTS_INFO(SessionInfo); //user name Result.user := wpSessionInfo.UserName; //station name Result.StationName := wpSessionInfo.WinStationName; //domain name Result.domain := wpSessionInfo.Domain; //connection status Result.ConnectState := wpSessionInfo.State; finally WTSFreeMemory(SessionInfo); end; end else begin idErr := GetLastError; GV_TxtFileLog.Log('[GetSessionInfo] id ' + IntToStr(id) + ' ERR : ' + intToStr(idErr) + ' ' + SysErrorMessage(idErr), True); end; except on e: Exception do GV_TxtFileLog.Log('[GetSessionInfo] id ' + IntToStr(id) + ' EX : ' + e.Message, True); end; end; function SF_GetOpenedSessions(var codeErr: Integer): TSessionsInfo; var Sessions, Session: PWTS_SESSION_INFO; NumSessions, I: DWORD; SessionInfo: TSessionInfo; SessionCount: Integer; begin Result := nil; codeErr := 0; try if WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, Sessions, NumSessions) then begin try if NumSessions > 0 then begin SessionCount := 0; SetLength(Result, NumSessions); try Session := Sessions; for i := 0 to NumSessions-1 do begin if Session.SessionId <> 0 then begin GV_TxtFileLog.Log('[SF_GetOpenedSessions] GetSessionInfo '+ intToStr(Session.SessionId)); SessionInfo := GetSessionInfo(Session.SessionId); if SessionInfo.ID <> -1 then begin Result[SessionCount] := SessionInfo; Inc(SessionCount); end; end; Inc(Session); end; finally SetLength(Result, SessionCount); end; end; finally WTSFreeMemory(Sessions); end; end else begin codeErr := GetLastError; GV_TxtFileLog.Log('[SF_GetOpenedSessions] WTSEnumerateSessions err : ' + IntToStr(codeErr) + ' ' + SysErrorMessage(codeErr), True); end; except on e: Exception do GV_TxtFileLog.Log('[SF_GetOpenedSessions] EX : ' + e.Message, True); end; end; Edited December 28, 2023 by Remy Lebeau Share this post Link to post
gioma 19 Posted December 29, 2023 10 hours ago, Remy Lebeau said: Your declaration of WTSQuerySessionInformation() is wrong. It should be like this instead: function WTSQuerySessionInformation( hServer: THandle; SessionId: DWORD; WTSInfoClass: WTS_INFO_CLASS; var ppBuffer: LPWSTR; var pBytesReturned: DWORD): BOOL; stdcall; external 'Wtsapi32.dll' name {$IFDEF UNICODE}'WTSQuerySessionInformationW'{$ELSE}'WTSQuerySessionInformationA'{$ENDIF}; And technically, both WTSEnumerateSessions() and WTSQuerySessionInformation() should be using 'out' parameters instead of 'var' parameters. But that is not important here. Hi Remy, I tried but I always get error code 87. I also tried using this version: function WTSQuerySessionInformation( hServer: THandle; SessionId: DWORD; WTSInfoClass: WTS_INFO_CLASS; var ppBuffer: Pointer; var pBytesReturned: Pointer): BOOL; stdcall; external 'Wtsapi32.dll' name {$IFDEF UNICODE}'WTSQuerySessionInformationW'{$ELSE}'WTSQuerySessionInformationA'{$ENDIF}; obviously it doesn't work! 😭 The strangest thing is that sometimes it works (with the first version), but then suddenly it stops working and there is no way to make it work anymore. What I'm wondering is because compiling in 32 bit always works, while compiling in 64 bit doesn't! Is the structure not large enough for the result? But Microsoft's documentation indicates to use those data types. There is no documentation online on the use of this API for 64-bit applications! 😭😭 Share this post Link to post
DelphiUdIT 176 Posted December 29, 2023 This appears to be an issue related to "overwriting" of memory areas. Another tip: try disabling the "support high-entropy 64 bit address ..." item in the project options (Delphi compiler / Linking). P.S.: Do not attempt to redefine the methods, they are already defined in the indicated unit that comes with Delphi. Share this post Link to post
gioma 19 Posted December 29, 2023 11 minutes ago, DelphiUdIT said: P.S.: Do not attempt to redefine the methods, they are already defined in the indicated unit that comes with Delphi. Do you mean the "WTSQuerySessionInformation()" function? What is the unit in Delphi where it is already defined? 11 minutes ago, DelphiUdIT said: Another tip: try disabling the "support high-entropy 64 bit address ..." item in the project options (Delphi compiler / Linking I tried, unfortunately nothing changes. Share this post Link to post
DelphiUdIT 176 Posted December 29, 2023 4 hours ago, gioma said: Do you mean the "WTSQuerySessionInformation()" function? What is the unit in Delphi where it is already defined? 1 Share this post Link to post
gioma 19 Posted December 29, 2023 (edited) Edited December 29, 2023 by gioma Share this post Link to post
gioma 19 Posted December 29, 2023 6 minutes ago, gioma said: you have solved my problem! I used the unit WinApi.Wtsapi32 and the error disappeared! Honestly, I couldn't find this unit so I thought it didn't exist! Thanks thanks thanks. Share this post Link to post
gioma 19 Posted December 29, 2023 Now I also understand why I couldn't find it, winapi.wtsapi32 isn't there on delphi 11! I started the project with Delphi 10, then I switched to Delphi 11 and now I've switched to Delphi 12! Share this post Link to post