关键词:StartService,参数传递,服务/应用间切换,唯一实例,传的参数取不到(附代码,谢谢^_^) (100分)

  • 主题发起人 cornermoss
  • 开始时间
C

cornermoss

Unregistered / Unconfirmed
GUEST, unregistred user!
我想要做到:
1)程序通过服务已经运行,如果再通过应用方式运行程序的话,会先将服务停止,然后再启动自己;
2)如果是通过应用方式启动的程序,在程序退出后,将通过服务方式再次启动程序;(一、二总的说来,就是程序怎样在服务/应用之间切换的问题,程序只能在“服务”中停止)
3)系统中只能同时运行该程序的一个实例。
现在的问题是,第2)步,总是说程序已经在运行中;所以我想如果是应用程序退出的时候,启动服务的话,我就传递一个参数'ByServ',在Initialize过程里,如果传递的参数'ByServ'存在,则“判断是否存在程序实例”,否则不判断(也就是服务启动->应用退出,本来正确的流程应该是应用退出->服务启动)
下面是几个相关的过程
//在主窗体里,在程序(应用状态)退出后,启动服务
function TMain.StartService:Boolean;
var svcmgr,svc:Integer;
bResult:Boolean;
s:pChar;
begin
bResult:=False;
s:='ByServ'; //定义传递的参数
svcmgr:=OpenSCManager(nil,nil,SC_MANAGER_ALL_ACCESS);
if svcmgr <> 0 then
begin
svc:=OpenService(svcmgr,'NoDiskWin98',SERVICE_ALL_ACCESS);
if(svc<>0) then
try begin WinSvc.StartService(svc,1,s); bResult:=True; end;
// ^^^^^ 这样传递参数对吗?
except bResult:=False;
end;
CloseServiceHandle(svc);
end;
CloseServiceHandle(svcmgr);
Result:=bResult;
end;
_________________________
//主窗体里,根据参数FromService、args做相关操作
procedure TMain.Initialize(const FromService: Boolean;const args:String);
begin
FFromService:=FromService;
if FromService then
begin
if ((OpenMutex(MUTEX_ALL_ACCESS,False,'MOServ')<>0) and (args='')) then //这里就是判断实例及传递的参数
begin
MessageBox(0, PChar('无盘服务已经启动,请勿重复启动!'), '提示信息', MB_ICONERROR);
Halt;
end
else
{$ifdef trayicon}
TrayMessage(NIM_DELETE)
{$endif}
end
else StopService; //否则,为 应用 启动时
end;

----------------------------
//在工程文件里
begin
//从 服务 启动
if Installing or IsRunService then
begin
SvcMgr.Application.Initialize;
TCMagicService := TTCMagicService.CreateNew(SvcMgr.Application, 0);
SvcMgr.Application.CreateForm(TMain, Main);
Application.MessageBox(PChar(ParamStr(0)),PChar(ParamStr(1)),MB_OK);
if ParamStr(2)='ByServ' then
Main.Initialize(True,'ByServ') //根据启动方式进行初始化,程序退出后启动服务
else Main.Initialize(True,''); //根据启动方式进行初始化,从服务管理器启动
SvcMgr.Application.Run;
end else //从 应用 启动
begin
CreateMutex(nil, True, 'MOServ');
if GetLastError = ERROR_ALREADY_EXISTS then
begin
MessageBox(0, PChar('无盘服务已经启动,请勿重复启动!'), '提示信息', MB_ICONERROR);
Halt;
end;
Forms.Application.Initialize;
Forms.Application.Title:='NoDisk Win98';
BackGround:=TBackGround.Create(nil);
BackGround.Show;
Forms.Application.CreateForm(TMain, Main);
Main.Initialize(False,''); //根据启动方式进行初始化
Sleep(2000);
BackGround.Close;
BackGround.Free;
Forms.Application.Run;
end;
end.
________________________________
在工程文件里这一句Application.MessageBox(PChar(ParamStr(0)),PChar(ParamStr(1)),MB_OK);
就是显示出我在function TMain.StartService:Boolean; 里传递的参数,没有传递给我的程序啊。现在就是应用程序退出后启动服务,这一点没有弄好~~
请各位大虾帮帮忙啊,不胜感激~~~~~~~~~ ^_^
 
555~~~~~~~~没人来,那我换一个明白一点的问法啊?
服务相关:WinSvc.StartService传递的参数,程序该怎样才能取得该参数啊?_________________
function TMain.StartService:Boolean;
var svcmgr,svc:Integer;
bResult:Boolean;
s:pChar;
begin
bResult:=False;
s:='ByServ';
svcmgr:=OpenSCManager(nil,nil,SC_MANAGER_ALL_ACCESS);
if svcmgr <> 0 then
begin
svc:=OpenService(svcmgr,'NoDiskWin98',SERVICE_ALL_ACCESS);
if(svc<>0) then
try begin WinSvc.StartService(svc,1,s); bResult:=True; end;
// ^^^^^^^^ 这样能传递参数'ByServ'吗?
except bResult:=False;
end;
CloseServiceHandle(svc);
end;
CloseServiceHandle(svcmgr);
Result:=bResult;
end;
_______________
但是我在工程文件里,
Application.MessageBox(PChar(ParamStr(0)),PChar(ParamStr(1)),MB_OK);
只能取到ParamStr(0)为程序路径名称,取不到ParamStr(1)呢?
谢谢各位大虾指点一下啊?
 
s: string;

try begin WinSvc.StartService(svc,1,PChar(s)); bResult:=True; end;
 
to thx1180:谢谢您的问答。但是编译有这个错误,
[Error] MainU.pas(262): Constant object cannot be passed as var parameter
WinSvc.StartService(svc,1,PChar(s)); //PChar(s)这里应该是要变参吧?

BOOL StartService(
SC_HANDLE hService, // handle of service
DWORD dwNumServiceArgs, // number of arguments
LPCTSTR *lpServiceArgVectors //address of array of argument string pointers
);
 
你说的对,第三个参数应该是个PChar类型变量,那这样:
var
s: PChar;
Str: string;
begin
Str := 'ByServ';
S := PChar(Str);
try begin WinSvc.StartService(svc,1,s); bResult:=True; end;
 
to thx1180:谢谢您的问答。编译正确的,但是还是没取到传出的参数。
____________________
其实,说了这么多,我需要考虑的有这几个情况:
1)程序应该知道自己是通过(应用)/(服务管理器启动)/(应用退出后启动服务)这三种
方式中的哪一种启动的;
2)如果在服务管理器中已经启动,再从应用启动的话,程序会先关闭服务,然后启动应用;
3)如果是从应用启动的,此时在“服务管理器”中不能启动服务;应用退出后,启动服务;
4)在一个较短的时间段内,系统可以存在两个程序的实例;一般情况下只能存在一个实例;
(我这样说,是因为在服务->应用和应用->服务的时间段可能会存在两个程序实例,该步用
的是Mutex互斥体)
现在其他方面都弄好了,存在的问题就是3)“应用退出后,启动服务”;我得告诉程序,
它是从应用退出后启动服务的,而不是从服务管理器里启动服务的;所以我用
WinSvc.StartService(svc,1,s) 传出一个参数,然后在工程文件里读取该参数,判断出自己
的启动方式,然后在主窗体里判断是否应该根据Mutex来防止运行两个实例。
______________________
可能这样说得不是很清楚,我看重新做一个Demo,再请您看看?
 
说得很清楚了,关键是如何传参数,PChar类型指针要指向一个null结尾的字符串,试试这样:
Str := 'ByServ' + #0;
s := @Str[1];

取参数是用的ServiceMain函数吧。
 
取参数我是在工程文件里这样的啊:
//这里就总是取不到 PChar(ParamStr(1)
Application.MessageBox(PChar(ParamStr(0)),PChar(ParamStr(1)),MB_OK);

请您下这个Demo看看呢? http://boyzxd.myrice.com/service.rar 10.9KB
谢谢^_^
 
参数是传给服务本身的,不是传给应用程序的,再者ParamStr取不到传给服务的参数,在服务中要用ServiceMain函数去取。
 
请问ServiceMain需要我自己编写吗?我在IDE输入ServiceMain( 怎么没有提示呢?

该是在工程文件里这个函数
//是从 服务 运行
function IsRunService: Boolean;
这里面使用ServiceMain取得参数吗?
VOID WINAPI ServiceMain(
DWORD dwArgc, // number of arguments
LPTSTR *lpszArgv // address of array of argument string pointers
);
它怎么知道我是要对哪个服务取参数呢?谢谢^_^
 
TService类有个Param属性,访问它可以得到。注意Param[0]是服务本身的名称,从Param[1]开始是参数,ParamCount是参数的个数。
 
thx1180:谢谢您的热心帮助。最后我还是放弃传参数了,是在一个显示当前系统时间
的Timer里判断,如果是以服务方式启动、并且存在互斥的Mutex(应用存在)的话,则
结束服务,保留应用。
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
顶部