I am writing a program that will ideally work on a server in the background without closing, so it is important that memory leaks do not exist. My program includes obtaining information about a real session using the Windows Terminal Services APIs (wtsapi32.dll), and since the information must be live, the function runs every few seconds, I found that a call to the WTSEnumerateSessionsEx function led to a rather significant leak memory. It seems that calling WTSFreeMemoryEx , as indicated in the MSDN documentation, seems to have no effect, but I am not getting error messages from any call.
To summarize: the problem is not the execution of WTSEnumerateSessionsEx , since valid data is correct; the memory is simply not freed, and this leads to problems when continuing to work for long periods of time.
Currently, the short-term solution is to restart the process when the used memory exceeds the threshold, however this does not seem to be a satisfactory solution, and fixing this leak would be most desirable.
Enumeration types were taken directly from the Microsoft MSDN documentation.
The attached file is the corresponding source file.
unit WtsAPI32; interface uses Windows, Classes, Dialogs, SysUtils, StrUtils; const WTS_CURRENT_SERVER_HANDLE = 0; type WTS_CONNECTSTATE_CLASS = (WTSActive, WTSConnected, WTSConnectQuery, WTSShadow, WTSDisconnected, WTSIdle, WTSListen, WTSReset, WTSDown, WTSInit); type WTS_TYPE_CLASS = (WTSTypeProcessInfoLevel0, WTSTypeProcessInfoLevel1, WTSTypeSessionInfoLevel1); type WTS_SESSION_INFO_1 = record ExecEnvId: DWord; State: WTS_CONNECTSTATE_CLASS; SessionId: DWord; pSessionName: LPtStr; pHostName: LPtStr; pUserName: LPtStr; pDomainName: LPtStr; pFarmName: LPtStr; end; type TSessionInfoEx = record ExecEnvId: DWord; State: WTS_CONNECTSTATE_CLASS; SessionId: DWord; pSessionName: string; pHostName: string; pUserName: string; pDomainName: string; pFarmName: string; end; TSessions = array of TSessionInfoEx; function FreeMemoryEx(WTSTypeClass: WTS_TYPE_CLASS; pMemory: Pointer; NumberOfEntries: Integer): BOOL; stdcall; external 'wtsapi32.dll' name 'WTSFreeMemoryExW'; function FreeMemory(pMemory: Pointer): DWord; stdcall; external 'wtsapi32.dll' name 'WTSFreeMemory'; function EnumerateSessionsEx(hServer: THandle; var pLevel: DWord; Filter: DWord; var ppSessionInfo: Pointer; var pCount: DWord): BOOL; stdcall; external 'wtsapi32.dll' name 'WTSEnumerateSessionsExW'; function EnumerateSessions(var Sessions: TSessions): Boolean; implementation function EnumerateSessions(var Sessions: TSessions): Boolean; type TSessionInfoExArr = array[0..2000 div SizeOf(WTS_SESSION_INFO_1)] of WTS_SESSION_INFO_1; var ppSessionInfo: Pointer; pCount: DWord; hServer: THandle; level: DWord; i: Integer; ErrCode: Integer; Return: DWord; begin pCount := 0; level := 1; hServer := WTS_CURRENT_SERVER_HANDLE; ppSessionInfo := NIL; if not EnumerateSessionsEx(hServer, level, 0, ppSessionInfo, pCount) then begin ErrCode := GetLastError; ShowMessage('Error in EnumerateSessionsEx - Code: ' + IntToStr(ErrCode) + ' Message: ' + SysErrorMessage(ErrCode)); en else begin SetLength(Sessions, pCount); for i := 0 to pCount - 1 do begin Sessions[i].ExecEnvId := TSessionInfoExArr(ppSessionInfo^)[i].ExecEnvId; Sessions[i].State := TSessionInfoExArr(ppSessionInfo^)[i].State; Sessions[i].SessionId := TSessionInfoExArr(ppSessionInfo^)[i].SessionId; Sessions[i].pSessionName := WideCharToString (TSessionInfoExArr(ppSessionInfo^)[i].pSessionName); Sessions[i].pHostName := WideCharToString (TSessionInfoExArr(ppSessionInfo^)[i].pHostName); Sessions[i].pUserName := WideCharToString (TSessionInfoExArr(ppSessionInfo^)[i].pUserName); Sessions[i].pDomainName := WideCharToString (TSessionInfoExArr(ppSessionInfo^)[i].pDomainName); Sessions[i].pFarmName := WideCharToString (TSessionInfoExArr(ppSessionInfo^)[i].pFarmName); end; if not FreeBufferEx(WTSTypeSessionInfoLevel1, ppSessionInfo, pCount); begin ErrCode := GetLastError; ShowMessage('Error in EnumerateSessionsEx - Code: ' + IntToStr(ErrCode) + ' Message: ' + SysErrorMessage(ErrCode)); end; ppSessionInfo := nil; end; end; end.
Here is a minimal SSCCE that demonstrates the problem. When this program is executed, it will run out of available memory in a short time.
program SO17839270; {$APPTYPE CONSOLE} uses SysUtils, Windows; const WTS_CURRENT_SERVER_HANDLE = 0; type WTS_TYPE_CLASS = (WTSTypeProcessInfoLevel0, WTSTypeProcessInfoLevel1, WTSTypeSessionInfoLevel1); function WTSEnumerateSessionsEx(hServer: THandle; var pLevel: DWORD; Filter: DWORD; var ppSessionInfo: Pointer; var pCount: DWORD): BOOL; stdcall; external 'wtsapi32.dll' name 'WTSEnumerateSessionsExW'; function WTSFreeMemoryEx(WTSTypeClass: WTS_TYPE_CLASS; pMemory: Pointer; NumberOfEntries: Integer): BOOL; stdcall; external 'wtsapi32.dll' name 'WTSFreeMemoryExW'; procedure EnumerateSessionsEx; var ppSessionInfo: Pointer; pCount: DWORD; level: DWORD; begin level := 1; if not WTSEnumerateSessionsEx(WTS_CURRENT_SERVER_HANDLE, level, 0, ppSessionInfo, pCount) then RaiseLastOSError; if not WTSFreeMemoryEx(WTSTypeSessionInfoLevel1, ppSessionInfo, pCount) then RaiseLastOSError; end; begin while True do EnumerateSessionsEx; end.