DELPHI 编写服务程序的几点总结。
最近的项目又需要编写一些服务项应用了。由于很久没写代码了,有些东西忘了。不得不花点时间重新去找回。现把一些有用的贴出来,以便以后再用时方便。其中有些是网上找的。有些是自己心得。
TService
属性介绍
AllowPause 表明服务是否允许暂停。True则SCP(服务控制面板)上的暂停按钮时可用的,False则是不可用的
AllowStop 表明服务是否允许停止。True则SCP(服务控制面板)上的停止按钮时可用的,False则是不可用的
Dependecies 用于列出所有要依赖的服务
DisplayName 显示在SCP上的服务名称
ErrCode 指定一个错误代码。当遇到错误或提供状态信息时,就返回这个代码。如果ErrCode的值为0,则使用Win32ErrorCode属性。
ErrorSeverity 表明如果启动服务时遇到错误,如何处理
Interactive 表明是否可以显示一个对话框。只适用于Win32服务
Name 服务的名称,即服务在SCM中的名称。如果要用SC.EXE或Net.exe来控制一个服务,必须指定服务的名称,而不是DisplayName指定的名称。
Param 启动时的参数列表。用SC.exe来启动服务后,即可在SCP中指定参数,也可以从命令行中设置参数。
ParamCount 传递个服务的参数个数
Password 用于设置口令。只适合于不使用LoaclSystem账号的服务
ServiceStartName 用于设置服务的账号名称,格式:域名/用户名
ServiceThread 这是服务内部的线程,用于处理命令和请求
ServiceType 服务的类型,可以设为:stWin32(Win32服务),stDevice(设备驱动程序)或stFileSystem(文件系统服务)
Status 服务的当前状况(running,stopped,paused,stop pending等)
Terminated 表明内部的线程是否终止
WaitHint 服务等待控制命令或状态请求的时间。如果在规定的时间内没有响应,则SCM认为服务出错
Win32ErrCode 当发生错误或ErrCode属性的值为0时,包含一个系统定义的错误代码
事件介绍:
AfterInstall:安装服务之后调用的方法;
AfterUninstall:服务卸载之后调用的方法;
BeforeInstall:服务安装之前调用的方法;
BeforeUninstall:服务卸载之前调用的方法;
OnContinue:服务暂停继续调用的方法;
OnExecute:执行服务开始调用的方法;
OnPause:暂停服务调用的方法;
OnShutDown:关闭时调用的方法;
OnStart:启动服务调用的方法;
OnStop:停止服务调用的方法;
LogMessage()函数 用于发送一个消息到NT的事件日志种。
ReportStatus()函数 用于发送服务的状态信息到SCM.
如何限制系统服务和桌面程序只运行一个
源文: http://hi.baidu.com/sqldebug/blog/item/58a764624a44d74eeaf8f863.html
program FleetReportSvr;
uses
SvcMgr, Forms, SysUtils, Windows,
SvrMain in 'SvrMain.pas' {FleetReportService: TService},
AppMain in 'AppMain.pas' {FmFleetReport};
{$R *.RES}
const
CSMutexName = 'Global\Services_Application_Mutex';
var
OneInstanceMutex: THandle;
SecMem: SECURITY_ATTRIBUTES;
aSD: SECURITY_DESCRIPTOR;
begin
InitializeSecurityDescriptor(@aSD, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(@aSD, True, nil, False);
SecMem.nLength := SizeOf(SECURITY_ATTRIBUTES);
SecMem.lpSecurityDescriptor := @aSD;
SecMem.bInheritHandle := False;
OneInstanceMutex := CreateMutex(@SecMem, False, CSMutexName);
if (GetLastError = ERROR_ALREADY_EXISTS)then
begin
DlgError('Error, Program or service already running!');
Exit;
end;
if FindCmdLineSwitch('svc', True) or
FindCmdLineSwitch('install', True) or
FindCmdLineSwitch('uninstall', True) then
begin
SvcMgr.Application.Initialize;
SvcMgr.Application.CreateForm(TSvSvrMain, SvSvrMain);
SvcMgr.Application.Run;
end
else
begin
Forms.Application.Initialize;
Forms.Application.CreateForm(TFmFmMain, FmMain);
Forms.Application.Run;
end;
end.
在系统服务和桌面程序之间共享内存
源文: http://hi.baidu.com/sqldebug/blog/item/58a764624a44d74eeaf8f863.html
用于创建内核对象的函数几乎都有一个指向SECURITY_ATTRIBUTES结构的指针作为其参数,在使用CreateFileMapping函数的时候,通常只是为该参数传递NULL,这样就可以创建带有默认安全性的内核对象。
默认安全性意味着对象的管理小组的任何成员和对象的创建者都拥有对该对象的全部访问权,而其他所有人均无权访问该对象。可以指定一个ECURITY_ATTRIBUTES结构,对它进行初始化,并为该参数传递该结构的地址。
它包含的与安全性有关的成员实际上只有一个,即lpSecurityDescriptor。当你想要获得对相应的一个内核对象的访问权(而不是创建一个新对象)时,必须设定要对该对象执行什么操作。如果想要访问一个现有的文件映射内核对象,以便读取它的数据,那么调用OpenfileMapping函数:通过将FILE_MAP_READ作为第一个参数传递给OpenFileMapping,指明打算在获得对该文件映象的访问权后读取该文件, 该函数在返回一个有效的句柄值之前,首先
执行一次安全检查。如果(已登录用户)被允许访问现有的文件映射内核对象,就返回一个有效的句柄。但是,如果被拒绝访问该对象,将返回NULL。
var
SecMem: SECURITY_ATTRIBUTES;
aSD: SECURITY_DESCRIPTOR;
begin
inherited Create;
{ 创建一个任何用户都可以访问的内核对象访问权 }
InitializeSecurityDescriptor(@aSD, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(@aSD, True, nil, False);
SecMem.nLength := SizeOf(SECURITY_ATTRIBUTES);
SecMem.lpSecurityDescriptor := @aSD;
SecMem.bInheritHandle := False;
FMapFile := CreateFileMapping($FFFFFFFF, @SecMem, PAGE_READWRITE, 0, CSharedMemSize, CSharedMemName);
FMapFile := OpenFileMapping(File_Map_All_Access, False, CSharedMemName);
if (FMapFile = 0) then
begin
raise Exception.Create(SysErrorMessage(GetLastError));
OutputDebugString(PChar(SysErrorMessage(GetLastError)));
end
else
begin // 成功
FShareMem := MapViewOfFile(FMapFile, File_Map_All_Access, 0, 0, CSharedMemSize);
OutputDebugString(PChar(SysErrorMessage(GetLastError) + ',Handle=' + IntToStr(Handle)));
end;
end;
destructor TPublicVars.Destroy;
begin
UnmapViewOfFile(FShareMem);
CloseHandle(FMapFile);
inherited;
end;
var
SecMem: SECURITY_ATTRIBUTES;
aSD: SECURITY_DESCRIPTOR;
begin
inherited Create;
{ 创建一个任何用户都可以访问的内核对象访问权 }
InitializeSecurityDescriptor(@aSD, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(@aSD, True, nil, False);
SecMem.nLength := SizeOf(SECURITY_ATTRIBUTES);
SecMem.lpSecurityDescriptor := @aSD;
SecMem.bInheritHandle := False;
FMapFile := CreateFileMapping($FFFFFFFF, @SecMem, PAGE_READWRITE, 0, CSharedMemSize, CSharedMemName);
FMapFile := OpenFileMapping(File_Map_All_Access, False, CSharedMemName);
if (FMapFile = 0) then
begin
raise Exception.Create(SysErrorMessage(GetLastError));
OutputDebugString(PChar(SysErrorMessage(GetLastError)));
end
else
begin //成功
FShareMem := MapViewOfFile(FMapFile, File_Map_All_Access, 0, 0, CSharedMemSize);
OutputDebugString(PChar(SysErrorMessage(GetLastError) + ',Handle=' + IntToStr(Handle)));
end;
end;
destructor TPublicVars.Destroy;
begin
UnmapViewOfFile(FShareMem);
CloseHandle(FMapFile);
inherited
在服务中使用COM组件
在服务中调用COM组件不能像在桌面程序中直接创建,在每次创建之前先调用CoInitialize(nil),释放的时候调用CoUninitialize。例如:调用ADO组件
Qry: TADOQuery;
begin
CoInitialize(nil);
Qry := TADOQuery.Create(nil);
try
...
finally
Qry.Free;
CoUninitialize;
end;
end;