将Windows事件日志改装到Delphi 5应用程序中

4
我正在寻找一种(相对无痛的)方法,将Windows应用程序事件日志支持添加到一个小型遗留的Delphi 5应用程序中。我们只想在启动、关闭、无法连接到数据库等情况下记录日志。
我看到了几个解决方案/组件,它们似乎都建议我们需要制作一个资源DLL,当Windows事件日志查看器尝试读取我们的“条目”时,将链接到该DLL。虽然这似乎不太困难,但如果/当我们将来进一步开发应用程序时,这是需要记住的另一件事——我们需要保持此DLL的最新状态。
在将来的某个时候,我们将希望将该应用程序转换为服务,可能是使用D2007编写的。
那么,有人可以推荐适合在D5中添加事件到事件日志的途径吗?我寻求特定的“我们使用过这个,效果还不错”的评论,而不是谷歌搜索(我自己可以做!)。免费或付费,真的无所谓——但能够迁移到D2007非常重要。
4个回答

6

摘要:使用Delphi写入Windows事件日志


如果您正在编写一个 Windows 服务并需要写入本地计算机的 Windows 事件日志,则可以调用 TService.LogMessage,如 此处 所述。
//TMyTestService = class(TService)

procedure TMyTestService.ServiceStart(Sender: TService; var Started: Boolean);
begin
  LogMessage('This is an error.');
  LogMessage('This is another error.', EVENTLOG_ERROR_TYPE);
  LogMessage('This is information.', EVENTLOG_INFORMATION_TYPE);
  LogMessage('This is a warning.', EVENTLOG_WARNING_TYPE);
end;

对于其他类型的应用程序,您可以使用SvcMgr.TEventLogger 未记录 帮助类来为TService编写本地计算机的Windows事件日志,如此处此处此处所述。

uses
  SvcMgr;

procedure TForm1.EventLoggerExampleButtonClick(Sender: TObject);
begin
  with TEventLogger.Create('My Test App Name') do
  begin
    try
      LogMessage('This is an error.');
      LogMessage('This is another error.', EVENTLOG_ERROR_TYPE);
      LogMessage('This is information.', EVENTLOG_INFORMATION_TYPE);
      LogMessage('This is a warning.', EVENTLOG_WARNING_TYPE);
    finally
      Free;
    end;
  end;
end;

您还可以使用Windows API ReportEvent函数,如此处此处所述。

我创建了一个简单的类来使它更容易,它在GitHub上可用

//----------------- EXAMPLE USAGE: ---------------------------------

uses
  EventLog;

procedure TForm1.EventLogExampleButtonClick(Sender: TObject);
begin
  TEventLog.Source := 'My Test App Name';

  TEventLog.WriteError('This is an error.');
  TEventLog.WriteInfo('This is information.');
  TEventLog.WriteWarning('This is a warning.');
end;

//------------------------------------------------------------------


unit EventLog;

interface

type
  TEventLog = class
  private
    class procedure CheckEventLogHandle;
    class procedure Write(AEntryType: Word; AEventId: Cardinal; AMessage: string); static;
  public
    class var Source: string;
    class destructor Destroy;

    class procedure WriteInfo(AMessage: string); static;
    class procedure WriteWarning(AMessage: string); static;
    class procedure WriteError(AMessage: string); static;

    class procedure AddEventSourceToRegistry; static;
  end;

threadvar EventLogHandle: THandle;

implementation

uses Windows, Registry, SysUtils;

class destructor TEventLog.Destroy;
begin
  if EventLogHandle > 0 then
  begin
    DeregisterEventSource(EventLogHandle);
  end;
end;

class procedure TEventLog.WriteInfo(AMessage: string);
begin
  Write(EVENTLOG_INFORMATION_TYPE, 2, AMessage);
end;

class procedure TEventLog.WriteWarning(AMessage: string);
begin
  Write(EVENTLOG_WARNING_TYPE, 3, AMessage);
end;

class procedure TEventLog.WriteError(AMessage: string);
begin
  Write(EVENTLOG_ERROR_TYPE, 4, AMessage);
end;

class procedure TEventLog.CheckEventLogHandle;
begin
  if EventLogHandle = 0 then
  begin
   EventLogHandle := RegisterEventSource(nil, PChar(Source));
  end;
  if EventLogHandle <= 0 then
  begin
    raise Exception.Create('Could not obtain Event Log handle.');
  end;
end;

class procedure TEventLog.Write(AEntryType: Word; AEventId: Cardinal; AMessage: string);
begin
  CheckEventLogHandle;
  ReportEvent(EventLogHandle, AEntryType, 0, AEventId, nil, 1, 0, @AMessage, nil);
end;

// This requires admin rights. Typically called once-off during the application's installation
class procedure TEventLog.AddEventSourceToRegistry;
var
  reg: TRegistry;
begin
  reg := TRegistry.Create;
  try
    reg.RootKey := HKEY_LOCAL_MACHINE;
    if reg.OpenKey('\SYSTEM\CurrentControlSet\Services\Eventlog\Application\' + Source, True) then
    begin
      reg.WriteString('EventMessageFile', ParamStr(0)); // The application exe's path
      reg.WriteInteger('TypesSupported', 7);
      reg.CloseKey;
    end
    else
    begin
      raise Exception.Create('Error updating the registry. This action requires administrative rights.');
    end;
  finally
    reg.Free;
  end;
end;

initialization

TEventLog.Source := 'My Application Name';

end.

ReportEvent支持将日志条目写入本地或远程计算机的事件日志。有关远程示例,请参见John Kaster的EDN文章


注意,否则您的所有日志消息都将以以下内容开头:

请注意,您还需要创建消息文件注册事件源

无法找到源自xxxx的事件ID xxx的说明。引发此事件的组件未安装在本地计算机上,或者安装已损坏。您可以在本地计算机上安装或修复组件。

如果事件起源于另一台计算机,则必须使用事件保存显示信息。

事件中包含以下信息:

1、如需了解如何创建消息文件的更多信息,请参见Finn Tolderlund的教程Michael Hex的文章,或者您可以使用现有的MC和GitHub项目中包含的RES文件
2、将MessageFile.res包含在您的DPR文件中,将RES文件嵌入到您的应用程序中。或者,您可以为消息创建一个dll。
program MyTestApp;

uses
  Forms,
  FormMain in 'FormMain.pas' {MainForm},
  EventLog in 'EventLog.pas';

{$R *.res}
{$R MessageFile\MessageFile.res}

begin
  Application.Initialize;

3、一次性注册需要管理员权限写入注册表,因此通常作为应用程序安装过程的一部分完成。

//For example
AddEventSourceToRegistry('My Application Name', ParamStr(0));
//or
AddEventSourceToRegistry('My Application Name', 'C:\Program Files\MyApp\Messages.dll');

//--------------------------------------------------

procedure AddEventSourceToRegistry(ASource, AFilename: string);
var
  reg: TRegistry;
begin
  reg := TRegistry.Create;
  try
    reg.RootKey := HKEY_LOCAL_MACHINE;
    if reg.OpenKey('\SYSTEM\CurrentControlSet\Services\Eventlog\Application\' + ASource, True) then
    begin
      reg.WriteString('EventMessageFile', AFilename);
      reg.WriteInteger('TypesSupported', 7);
      reg.CloseKey;
    end
    else
    begin
      raise Exception.Create('Error updating the registry. This action requires administrative rights.');
    end;
  finally
    reg.Free;
  end;
end;

如果您需要Windows事件记录和其他日志记录要求,您还可以使用日志框架,如log4dTraceTool

如果您想要在 Delphi IDE 中写入事件日志窗口,请单击此处


4

为了在D5中进行简单的事件记录,我使用以下代码将消息添加到应用程序日志中。

  • Add "SvcMgr" to the uses clause
  • Use this code to add your text message and an ID number (last parameter on LogMessage lines)

    with TEventLogger.create('My Application Name') do
    begin
      try
        LogMessage('Information Message!', EVENTLOG_INFORMATION_TYPE, 0, 1);
        LogMessage('Error Message!', EVENTLOG_ERROR_TYPE, 0, 2);
        LogMessage('Warning Message!', EVENTLOG_WARNING_TYPE, 0, 3);
        LogMessage('Audit Success Message!', EVENTLOG_AUDIT_SUCCESS, 0, 4);
        LogMessage('Audit Failure Message!', EVENTLOG_AUDIT_FAILURE, 0, 5);
      finally
        free;
      end;
    end;
    

谢谢Peter - 起作用了。我正在理清需要如何处理注册表和资源DLL,使其整洁。干杯,Rob - robsoft
请问这些函数在哪个Windows库中?我真的怀疑添加整个单元只为了使用其中的几个函数是否明智...是advapi32.dll还是winnt.dll...或者是其他哪个库呢?提前感谢您的回答。 - HX_unbanned

3

我在Delphi 6中使用标准VCL,我无法告诉您这在Delphi 5中是否可用。请自行尝试并告诉我们D5中是否有此功能。

  1. 声明一个类型为TEventLogger的全局/表单变量。该类型在SvcMgr单元中声明,因此需要将此单元添加到您的uses列表中。如果这是一个普通应用程序(即不是服务),则确保在Forms单元之后添加SvcMgr。

    MyEventLog: TEventLogger;

  2. 创建一个记录器实例。

    MyEventLog := TEventLogger.Create('MyApplication');

  3. 要写入事件日志:

    MyEventLog.LogMessage('MyApplication started.'), EVENTLOG_INFORMATION_TYPE);

  4. 别忘了在最后释放它:

    MyEventLog.Free;

还有其他一些事情需要做,以便使用Windows事件日志注册应用程序,以便消息出现时不会显示前面的内容:

在源(Microsoft Internet Explorer)中,事件ID(1000)的说明找不到。本地计算机可能没有必要的注册表信息或消息DLL文件来显示来自远程计算机的消息。以下信息是事件的一部分:


谢谢 - 在这个和Peter McMinn的回答之间,我已经开始了。一旦我把所有东西都搞定了,我会回来发布一些有关设置注册表/资源DLL的信息更新。再次感谢! - robsoft

1
感谢 J 和 Peter 的回应,我很快就能把我的代码写进事件日志中了。还有一点需要做的事情,特别是如果你希望你的事件在事件日志中显示得“好看”一些,而不是出现一个关于找不到描述的标准 Windows 消息(如 J 帖子底部所述)。
我按照 这里 的提示制作了一个适当的 DLL,并将其输入到注册表中,很快就解决了所有问题。
这都是在 Delphi5 中完成的,如同问题描述一样,但我没有看到任何让我觉得它不适用于 D2007 的地方。

我接受了这个作为我的答案,只是因为它总结了J和Peter的回答,并链接到了其他你需要做的部分。我不会从中获得任何声望 :-) - robsoft

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接