이 소스는 윈도우의 이벤트로그를 분석하는 프로그램중 일부입니다.
아래의 소스만으로 이벤트 뷰가 가능한 핵심 Core 소스입니다.
다만 메모리 누수가 발생되어 해당원인을 파악하기 어려워서 공개하고 도움을 얻고자 합니다.
일단 제가 확인 하기로는 FormatMessage 근처에서 누수가 일어나는 듯 합니다.
그리고 TEventLogRecord는 함수 호출자에서 삭제 합니다.
///////////////////////////////////////////////////////////////////////////////////////////////////
// EventMessageFile를 처리하여야 한다.
///////////////////////////////////////////////////////////////////////////////////////////////////
이전까지는 이상이 없던것으로 확인 했거든요
<!--CodeS-->
EventLogRecord.free;
dwBytesRead := dwBytesRead - TEventLog(lpBuffer^).Length;
lpBuffer := PEventLog(DWORD(lpBuffer) + TEventLog(lpBuffer^).Length);
Continue;
<!--CodeE-->
위 구문을 중간에 삽입해서 메모리누수 되는지 확인 하고 있습니다.
고수분들의 내공좀 부탁드립니다.
잠시 로그보는 것은 이상 없으나 장시간 Notify Event할 경우 문제가 심각합니다.
이거 500라인 구현하는데 한달이상 걸린듯 ㅋㅋ 정보가 빈약해서 쩝 ㅠㅠ
<!--CodeS-->
InsertStringrecord = record
id : string;
ClientIP : string;
ClientName : string;
ServerIP : string;
ServerName : string;
RuleId : string;
RuleType : string;
RulePriority : string;
UserId : string;
WriteTime : string;
EventID : string;
EventType : string;
BlaFileName : string;
ClientMacAddr : string;
LogonID : string;
ProcessID : string;
BuildProcessID : string;
end;
PEventLog = ^TEventLog;
TEventLog = record
Length : DWord;
Reserved : DWord;
RecordNumber : DWord;
TimeGenerated : DWord;
TimeWritten : DWord;
EventID : DWord;
EventType : Word;
NumStrings : Word;
EventCategory : Word;
ReservedFlags : Word;
ClosingRecordNumber: DWord;
StringOffset : DWord;
UserSIDLength : DWord;
UserSIDOffset : DWord;
DataLength : DWord;
DataOffset : DWord;
end;
TEventLogRecord = class
EVENTLOGREC : TEventLog;
ComputerName: string;
DescriptionRec : InsertStringrecord;
Description: string;
EventCategory: word;
EventID: DWORD;
EventType: word;
RecordNumber: DWORD;
SourceName: string;
TimeGenerated: TDateTime;
UserName: String;
end;
.....
function DisplayEntry(Source : string; OutPutList : TList; CompareTime : LongInt; var LastRecordNum : Cardinal) : LongInt;
var
hModule : THandle;
hEventLog : THandle;
EventLogPtr : pointer;
lpBuffer : PEventLog;
dwEventLogRecords : DWORD;
dwBytesRead : DWORD;
dwBytesNeed : DWORD;
dwBufSize : DWORD;
OutputStr : array[0..255] of char;
ParameterMessageFile : PChar;
argumentsToDelCount : integer;
EventMessageFile : PChar;
NextEventMessageFile : PChar;
FirstArgument : PChar;
NextArgument : PChar;
EventLogRecord : TEventLogRecord;
i, j, iNumStrings : integer;
ArgsBuffer : array of string;
tempstring, valuestring : string;
FlagstringCon : integer;
lpMsgBuf : PChar;
dwArgumentOffset : DWORD;
FlagParameter : integer; //디버그용
isFirst : boolean;
mLastRec : Cardinal;
begin
dwBytesRead := 0;
dwBytesNeed := 0;
Result := CompareTime;
hEventLog := OpenEventLog(nil, PChar(Source));
if hEventLog = 0 then
raise Exception.Create(SysErrorMessage(GetLastError));
if not GetNumberOfEventLogRecords(hEventLog, dwEventLogRecords) then
dwEventLogRecords := 512;
dwBufSize := 64*1024;
isFirst := True;
GetMem(EventLogPtr, dwBufSize);
lpBuffer := EventLogPtr;
mLastRec := LastRecordNum;
try
while ReadEventLog(hEventLog, EVENTLOG_BACKWARDS_READ or EVENTLOG_SEQUENTIAL_READ, dwEventLogRecords, EventLogPtr, dwBufSize, dwBytesRead, dwBytesNeed) do
begin
while (dwBytesRead > 0) And (lpBuffer^.TimeGenerated >= CompareTime) do
begin
with TEventLog(lpBuffer^) do
begin
if LastRecordNum = TEventLog(lpBuffer^).RecordNumber then
break;
if isFirst then
begin
mLastRec := TEventLog(lpBuffer^).RecordNumber;
isFirst := false;
end;
if mLastRec < TEventLog(lpBuffer^).RecordNumber then
mLastRec := TEventLog(lpBuffer^).RecordNumber;
FlagParameter := 0;
Result := TimeGenerated;
EventLogRecord := TEventLogRecord.Create;
EventLogRecord.EVENTLOGREC := TEventLog(lpBuffer^);
EventLogRecord.EventType := EventType;
EventLogRecord.TimeGenerated := UnixDateTimeToDelphiDateTime(TimeGenerated);
EventLogRecord.SourceName := PChar(DWORD(lpBuffer)+DWORD(SizeOf(TEventLog)));
EventLogRecord.EventCategory := EventCategory;
EventLogRecord.EventID := EventID;
EventLogRecord.Description := '';
if TEventLog(lpBuffer^).UserSIDLength > 0 then
EventLogRecord.UserName := GetAccountName(PSID(DWORD(lpBuffer) + DWORD(TEventLog(lpBuffer^).UserSIDOffset)))
else
EventLogRecord.UserName := '(blank)';
EventLogRecord.ComputerName := PChar(DWORD(lpBuffer)+DWORD(SizeOf(TEventLog)+System.Length(EventLogRecord.SourceName)+1));
FirstArgument := PChar(DWORD(lpBuffer)+DWORD(StringOffset));
iNumStrings := TEventLog(lpBuffer^).NumStrings;
SetLength(ArgsBuffer, iNumStrings);
if iNumStrings <= 0 then
begin
SetLength(ArgsBuffer, 1);
ArgsBuffer[0] := '';
end
else
begin
dwArgumentOffset := DWORD(StrLen(FirstArgument)+1);
if High(ArgsBuffer) >= 0 then
ArgsBuffer[0] := FirstArgument;
for i := 1 to iNumStrings-1 do
begin
NextArgument := PChar(DWORD(lpBuffer)+DWORD(StringOffset)+dwArgumentOffset);
ArgsBuffer[i] := NextArgument;
dwArgumentOffset := dwArgumentOffset + StrLen(NextArgument) + 1;
end;
end;
///////////////////////////////////////////////////////////////////////////////////////////////////
// ParameterMessage를 처리하여야 한다.
///////////////////////////////////////////////////////////////////////////////////////////////////
ParameterMessageFile := PChar(GetRegValue(HKEY_LOCAL_MACHINE,
'SYSTEM\CurrentControlSet\Services\EventLog\' + Source + '\' + EventLogRecord.SourceName, 'ParameterMessageFile'));
FillChar(OutputStr, SizeOf(OutputStr), 0);
ExpandEnvironmentStrings(ParameterMessageFile, OutputStr, SizeOf(OutputStr));
if OutputStr <> ParameterMessageFile then
ParameterMessageFile := OutputStr;
argumentsToDelCount := 0;
if ParameterMessageFile <> nil then
begin
// 필요한 포인터가 몇개일지 결정한다.
for i := 0 to iNumStrings-1 do
begin
if System.Length(ArgsBuffer[i]) > 2 then
begin
if (ArgsBuffer[i][1] = '%') And
(ArgsBuffer[i][2] = '%') Then
inc(argumentsToDelCount);
end;
end;
//////////////////////////////////////////////////////////////////////////////////////////////
// ParameterMessageFile은 여러개일 가능성이 있습니다.
//////////////////////////////////////////////////////////////////////////////////////////////
if argumentsToDelCount > 0 then
begin
hModule := LoadLibraryEx(PChar(ParameterMessageFile), 0, DONT_RESOLVE_DLL_REFERENCES);
if hModule <> 0 then
begin
for i := 0 to iNumStrings-1 do
begin
tempstring := '';
if System.Length(ArgsBuffer[i]) > 2 then
begin
for j := 1 to System.length(ArgsBuffer[i]) do
begin
if (ArgsBuffer[i][j] = '%') then
begin
valuestring := '';
inc(FlagstringCon);
if FlagstringCon > 2 then
FlagstringCon := 2;
Continue;
end;
if FlagstringCon = 2 then
begin
if (j = System.length(ArgsBuffer[i])) Then
begin
if (ArgsBuffer[i][j] in ['0'..'9']) then
valuestring := valuestring + ArgsBuffer[i][j];
FlagstringCon := 3
end
else if not (ArgsBuffer[i][j + 1] in ['0'..'9']) then
begin
if (ArgsBuffer[i][j] in ['0'..'9']) then
valuestring := valuestring + ArgsBuffer[i][j];
FlagstringCon := 3
end
else
begin
valuestring := valuestring + ArgsBuffer[i][j];
Continue;
end;
end;
if FlagstringCon = 3 then
begin
try
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER or
FORMAT_MESSAGE_FROM_HMODULE or
FORMAT_MESSAGE_FROM_SYSTEM or
FORMAT_MESSAGE_ARGUMENT_ARRAY,
Pointer(hModule),
StrToInt64(valuestring), // Argment 를 Integer로 변환
0,
PChar(@lpMsgBuf), SizeOf(lpMsgBuf), nil);
tempstring := tempstring + strpas(lpMsgBuf);
inc(FlagParameter);
except
end;
FlagstringCon := 0;
end
else
tempstring := tempstring + ArgsBuffer[i][j];
end;
ArgsBuffer[i] := tempstring;
end;
end;
FreeLibrary(hModule);
end;
end;
end;
///////////////////////////////////////////////////////////////////////////////////////////////////
// EventMessageFile를 처리하여야 한다.
///////////////////////////////////////////////////////////////////////////////////////////////////
EventMessageFile := PChar(GetRegValue(HKEY_LOCAL_MACHINE,
'SYSTEM\CurrentControlSet\Services\EventLog\' + Source + '\' + EventLogRecord.SourceName, 'EventMessageFile'));
FillChar(OutputStr, SizeOf(OutputStr), 0);
ExpandEnvironmentStrings(EventMessageFile, OutputStr, SizeOf(OutputStr));
if OutputStr <> EventMessageFile then
EventMessageFile := OutputStr;
//////////////////////////////////////////////////////////////////////////////////////////////
// EventMessageFile은 여러개일 가능성이 있습니다.
//////////////////////////////////////////////////////////////////////////////////////////////
NextEventMessageFile := EventMessageFile;
i := 0;
While NextEventMessageFile <> nil do
begin
if (EventMessageFile[i] = ';') or (EventMessageFile[i] = #0) then
begin
hModule := 0;
if EventMessageFile[i] = ';' then
begin
EventMessageFile[i] := #0;
hModule := LoadLibraryEx(PChar(NextEventMessageFile), 0, DONT_RESOLVE_DLL_REFERENCES);
NextEventMessageFile := @EventMessageFile[i + 1];
end
else
begin
hModule := LoadLibraryEx(PChar(NextEventMessageFile), 0, DONT_RESOLVE_DLL_REFERENCES);
NextEventMessageFile := nil;
end;
FillChar(lpMsgBuf, SizeOf(lpMsgBuf), 0);
if hModule <> 0 then
try
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER or
FORMAT_MESSAGE_FROM_HMODULE or
FORMAT_MESSAGE_FROM_SYSTEM or
FORMAT_MESSAGE_ARGUMENT_ARRAY,
Pointer(hModule),
EventLogRecord.EventID,
0,
PChar(@lpMsgBuf), SizeOf(lpMsgBuf), ArgsBuffer);
finally
FreeLibrary(hModule);
ArgsBuffer := nil;
end;
EventLogRecord.Description := EventLogRecord.Description + strpas(lpMsgBuf);
end;
inc(i);
end;
lpMsgBuf := nil;
if System.Length(EventLogRecord.Description) > 4 then
begin
tempstring := '';
i := 1;
while i <= System.Length(EventLogRecord.Description) do
begin
if (EventLogRecord.Description[i] + EventLogRecord.Description[i + 1] +
EventLogRecord.Description[i + 2] + EventLogRecord.Description[i + 3]) =
(chr(13) + chr(10) + chr(13) + chr(10)) then
begin
tempstring := tempstring + chr(13) + chr(10);
i := i + 4;
continue;
end;
tempstring := tempstring + EventLogRecord.Description[i];
inc(i);
end;
EventLogRecord.Description := tempstring;
end;
EventLogRecord.RecordNumber := RecordNumber;
OutPutList.Add(EventLogRecord);
end;
dwBytesRead := dwBytesRead - TEventLog(lpBuffer^).Length;
lpBuffer := PEventLog(DWORD(lpBuffer) + TEventLog(lpBuffer^).Length);
end;
lpBuffer := EventLogPtr;
end;
finally
LastRecordNum := mLastRec;
FreeMem(EventLogPtr, dwBufSize);
CloseEventLog(hEventLog);
end;
end;
<!--CodeE-->
LocalFree로 해결이 되었네요
이 소스의 원본 링크는 다음과 같으며 아래 원본 소스도 메모리누수 문제가 있습니다.
http://www.dragonsoftru.com/articles/EventLog.html
http://bdn.borland.com/article/0,1410,32435,00.html