我创建了非常简单的Windows服务应用程序,使用Delphi按时间顺序更新一些数据文件。服务应用程序编译,并且运行良好,但我对最终的exe文件大小不满意。它超过900K。服务本身不使用Forms,Dialogs,但我看到SvcMgr正在引用Forms和其他我没有使用的大型垃圾。
Name Size Group Package
------------ ------ ----- -------
Controls 80,224 CODE
Forms 61,204 CODE
Classes 46,081 CODE
Graphics 37,054 CODE
有没有办法让服务应用更小?或者我是否可以使用其他服务模板而不使用表格等?
答案 0 :(得分:20)
以下是我用于创建基于纯API的非常小的服务的代码。 exe的大小只有50K。可能更小,我使用了一些其他可以省略的单位。使用的编译器是Delphi 7.新编译器可能会更大,但我没有检查。
代码很旧,我没有检查。我几年前写的。所以以它为例,请不要复制粘贴。
{
NT Service model based completely on API calls. Version 0.1
Inspired by NT service skeleton from Aphex
Adapted by Runner
}
program PureAPIService;
{$APPTYPE CONSOLE}
{$IF CompilerVersion > 20}
{$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
{$WEAKLINKRTTI ON}
{$IFEND}
uses
Windows,
WinSvc;
const
ServiceName = 'PureAPIService';
DisplayName = 'Pure Windows API Service';
NUM_OF_SERVICES = 2;
var
ServiceStatus : TServiceStatus;
StatusHandle : SERVICE_STATUS_HANDLE;
ServiceTable : array [0..NUM_OF_SERVICES] of TServiceTableEntry;
Stopped : Boolean;
Paused : Boolean;
var
ghSvcStopEvent: Cardinal;
procedure OnServiceCreate;
begin
// do your stuff here;
end;
procedure AfterUninstall;
begin
// do your stuff here;
end;
procedure ReportSvcStatus(dwCurrentState, dwWin32ExitCode, dwWaitHint: DWORD);
begin
// fill in the SERVICE_STATUS structure.
ServiceStatus.dwCurrentState := dwCurrentState;
ServiceStatus.dwWin32ExitCode := dwWin32ExitCode;
ServiceStatus.dwWaitHint := dwWaitHint;
case dwCurrentState of
SERVICE_START_PENDING: ServiceStatus.dwControlsAccepted := 0;
else
ServiceStatus.dwControlsAccepted := SERVICE_ACCEPT_STOP;
end;
case (dwCurrentState = SERVICE_RUNNING) or (dwCurrentState = SERVICE_STOPPED) of
True: ServiceStatus.dwCheckPoint := 0;
False: ServiceStatus.dwCheckPoint := 1;
end;
// Report the status of the service to the SCM.
SetServiceStatus(StatusHandle, ServiceStatus);
end;
procedure MainProc;
begin
// we have to do something or service will stop
ghSvcStopEvent := CreateEvent(nil, True, False, nil);
if ghSvcStopEvent = 0 then
begin
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
Exit;
end;
// Report running status when initialization is complete.
ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );
// Perform work until service stops.
while True do
begin
// Check whether to stop the service.
WaitForSingleObject(ghSvcStopEvent, INFINITE);
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
Exit;
end;
end;
procedure ServiceCtrlHandler(Control: DWORD); stdcall;
begin
case Control of
SERVICE_CONTROL_STOP:
begin
Stopped := True;
SetEvent(ghSvcStopEvent);
ServiceStatus.dwCurrentState := SERVICE_STOP_PENDING;
SetServiceStatus(StatusHandle, ServiceStatus);
end;
SERVICE_CONTROL_PAUSE:
begin
Paused := True;
ServiceStatus.dwcurrentstate := SERVICE_PAUSED;
SetServiceStatus(StatusHandle, ServiceStatus);
end;
SERVICE_CONTROL_CONTINUE:
begin
Paused := False;
ServiceStatus.dwCurrentState := SERVICE_RUNNING;
SetServiceStatus(StatusHandle, ServiceStatus);
end;
SERVICE_CONTROL_INTERROGATE: SetServiceStatus(StatusHandle, ServiceStatus);
SERVICE_CONTROL_SHUTDOWN: Stopped := True;
end;
end;
procedure RegisterService(dwArgc: DWORD; var lpszArgv: PChar); stdcall;
begin
ServiceStatus.dwServiceType := SERVICE_WIN32_OWN_PROCESS;
ServiceStatus.dwCurrentState := SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted := SERVICE_ACCEPT_STOP or SERVICE_ACCEPT_PAUSE_CONTINUE;
ServiceStatus.dwServiceSpecificExitCode := 0;
ServiceStatus.dwWin32ExitCode := 0;
ServiceStatus.dwCheckPoint := 0;
ServiceStatus.dwWaitHint := 0;
StatusHandle := RegisterServiceCtrlHandler(ServiceName, @ServiceCtrlHandler);
if StatusHandle <> 0 then
begin
ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
try
Stopped := False;
Paused := False;
MainProc;
finally
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
end;
end;
end;
procedure UninstallService(const ServiceName: PChar; const Silent: Boolean);
const
cRemoveMsg = 'Your service was removed sucesfuly!';
var
SCManager: SC_HANDLE;
Service: SC_HANDLE;
begin
SCManager := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
if SCManager = 0 then
Exit;
try
Service := OpenService(SCManager, ServiceName, SERVICE_ALL_ACCESS);
ControlService(Service, SERVICE_CONTROL_STOP, ServiceStatus);
DeleteService(Service);
CloseServiceHandle(Service);
if not Silent then
MessageBox(0, cRemoveMsg, ServiceName, MB_ICONINFORMATION or MB_OK or MB_TASKMODAL or MB_TOPMOST);
finally
CloseServiceHandle(SCManager);
AfterUninstall;
end;
end;
procedure InstallService(const ServiceName, DisplayName, LoadOrder: PChar;
const FileName: string; const Silent: Boolean);
const
cInstallMsg = 'Your service was Installed sucesfuly!';
cSCMError = 'Error trying to open SC Manager';
var
SCMHandle : SC_HANDLE;
SvHandle : SC_HANDLE;
begin
SCMHandle := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS);
if SCMHandle = 0 then
begin
MessageBox(0, cSCMError, ServiceName, MB_ICONERROR or MB_OK or MB_TASKMODAL or MB_TOPMOST);
Exit;
end;
try
SvHandle := CreateService(SCMHandle,
ServiceName,
DisplayName,
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START,
SERVICE_ERROR_IGNORE,
pchar(FileName),
LoadOrder,
nil,
nil,
nil,
nil);
CloseServiceHandle(SvHandle);
if not Silent then
MessageBox(0, cInstallMsg, ServiceName, MB_ICONINFORMATION or MB_OK or MB_TASKMODAL or MB_TOPMOST);
finally
CloseServiceHandle(SCMHandle);
end;
end;
procedure WriteHelpContent;
begin
WriteLn('To install your service please type <service name> /install');
WriteLn('To uninstall your service please type <service name> /remove');
WriteLn('For help please type <service name> /? or /h');
end;
begin
if (ParamStr(1) = '/h') or (ParamStr(1) = '/?') then
WriteHelpContent
else if ParamStr(1) = '/install' then
InstallService(ServiceName, DisplayName, 'System Reserved', ParamStr(0), ParamStr(2) = '/s')
else if ParamStr(1) = '/remove' then
UninstallService(ServiceName, ParamStr(2) = '/s')
else if ParamCount = 0 then
begin
OnServiceCreate;
ServiceTable[0].lpServiceName := ServiceName;
ServiceTable[0].lpServiceProc := @RegisterService;
ServiceTable[1].lpServiceName := nil;
ServiceTable[1].lpServiceProc := nil;
StartServiceCtrlDispatcher(ServiceTable[0]);
end
else
WriteLn('Wrong argument!');
end.
修改强>
我在没有资源和SysUtils的情况下编译了上面的代码。我在Delphi XE下获得了32KB可执行文件,在Delphi 2006下获得了22KB可执行文件。在XE下我删除了RTTI信息。我会写博客,因为它很有趣。我想知道C ++可执行文件的大小。
<强> EDIT2:强>
我更新了代码。现在是一个有效的代码。大多数较大的错误都应该消失。它仍然不是生产质量。
答案 1 :(得分:9)
你可以不用“大垃圾”。但是你必须自己与windows API交谈。看一下线索的来源。
“大垃圾”可以让您更轻松地编码。为了增加代码大小,它减少了设计时间。这只是你认为重要的问题。
此外,您编译时没有调试信息吗?调试信息会大大增加exe大小。
答案 2 :(得分:4)
如果您使用的是Delphi 6或7,请查看our LVCL open source libraries。
你会在这里找到一些标准VCL单元的替代品,代码重量要少得多。它具有基本的GUI组件(TLabel / TEdit等),只有创建安装程序所需的内容。但它的设计是在没有任何GUI的情况下使用。
即使您仅使用SysUtils和Classes单位,可执行文件大小也会小于标准VCL单元。对于某些操作,它也会比VCL更快(我已经包含了FastCode部分,或者在asm中重写了其他部分)。非常适合后台服务。
要处理后台服务,有SQLite3Service.pas单元,可与LVCL完美配合。它比直接API调用更高级。
这是一个完美的后台服务程序:
/// implements a background Service
program Background_Service;
uses
Windows,
Classes,
SysUtils,
WinSvc,
SQLite3Service;
// define this conditional if you want the GDI messages to be accessible
// from the background service
{$define USEMESSAGES}
type
/// class implementing the background Service
TMyService = class(TService)
public
/// the background Server processing all requests
// - TThread should be replaced by your own process
Server: TThread;
/// event trigerred to start the service
// - e.g. create the Server instance
procedure DoStart(Sender: TService);
/// event trigerred to stop the service
// - e.g. destroy the Server instance
procedure DoStop(Sender: TService);
/// initialize the background Service
constructor Create; reintroduce;
/// release memory
destructor Destroy; override;
end;
const
SERVICENAME = 'MyService';
SERVICEDISPLAYNAME = 'My service';
{ TMyService }
constructor TMyService.Create;
begin
inherited Create(SERVICENAME,SERVICEDISPLAYNAME);
OnStart := DoStart;
OnStop := DoStop;
OnResume := DoStart; // trivial Pause/Resume actions
OnPause := DoStop;
end;
destructor TMyService.Destroy;
begin
FreeAndNil(Server);
inherited;
end;
procedure TMyService.DoStart(Sender: TService);
begin
if Server<>nil then
DoStop(nil); // should never happen
Server := TThread.Create(false);
end;
procedure TMyService.DoStop(Sender: TService);
begin
FreeAndNil(Server);
end;
procedure CheckParameters;
var i: integer;
param: string;
begin
with TServiceController.CreateOpenService('','',SERVICENAME) do
// allow to control the service
try
if State<>ssErrorRetrievingState then
for i := 1 to ParamCount do begin
param := paramstr(i);
if param='/install' then
TServiceController.CreateNewService('','',SERVICENAME,
SERVICEDISPLAYNAME, paramstr(0),'','','','',
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS
{$ifdef USEMESSAGES}or SERVICE_INTERACTIVE_PROCESS{$endif},
SERVICE_AUTO_START). // auto start at every boot
Free else
if param='/remove' then begin
Stop;
Delete;
end else
if param='/stop' then
Stop else
if param='/start' then
Start([]);
end;
finally
Free;
end;
end;
var Service: TMyService;
begin
if ParamCount<>0 then
CheckParameters else begin
Service := TMyService.Create;
try
// launches the registered Services execution = do all the magic
ServicesRun;
finally
Service.Free;
end;
end;
end.
如果您愿意,可以发布其他问题on our forum。
答案 3 :(得分:0)
您始终可以使用Visual Studio服务模板创建一个小型服务主机,该主机将编译为DLL的Delphi代码调用。稍微不整洁但可能是从你所在地开始减小尺寸的最简单方法。简单的无所事事服务是91KB使用静态链接或36KB使用动态链接到C运行时。