Windowsサービスを登録して「説明(Description)」を設定しようとしたのですが、
例えば'test'と指定しても't'で設定されてしまいます。
Delphi3.1だと問題はなかったのですが、Dephi10.1で現象が出ます。
どなたか原因を教えていたけるとありがたいです。
例)
type
TSERVICE_DESCRIPTION = packed record
lpDescription: PChar;
end;
var
schService: SC_HANDLE;
strDescription: string;
lDescription: TSERVICE_DESCRIPTION;
begin
(省略)
strDescription := 'test'; //ローカル変数に文字列を指定していますが実際は関数の引数より渡しています。
lDescription.lpDescription := PChar(strDescription);
ChangeServiceConfig2A(schService, SERVICE_CONFIG_DESCRIPTION, @lDescription);
外しているかも知れませんが
ChangeServiceConfig2Aの引数がポインタのポインタだからではないでしょうか?
lpDescription: string;
lpDescriptionptr: pstring;
と定義して
lpDescription := 'test';
lpDescriptionptr := @lpDescription[1];
で引数を渡すのに
@lpDescriptionptr を使って見てはいかがでしょうか?
参考にしたページ
http://www.delphigroups.info/2/4/326163.html
Delphi 2009以降の文字列はUnicode(UTF-16LE)ですので、ChangeServiceConfig2AではなくChangeServiceConfig2またはChangeServiceConfig2Wを呼び出してください。
# 't'になるのは、UTF-16(String=UnicodeString)で'test'はANSI(AnsiString)で見ると't'+#0+'e'+#0+'s'+#0+'t'+#0になるからでしょう
ちなみにこんなTServiceのヘルパを作って使ってます。参考まで。
unit UServiceHelper;
interface
uses
Winapi.Windows, Winapi.WinSvc,
System.SysUtils,
Vcl.SvcMgr;
type
TServiceHelper = class helper for TService
public
// Set service description (call this function in TService.AfterInstall event)
procedure SetServiceDescription(const Description: String);
// Set service failure actions
procedure SetServiceFailureActions(const Actions: SERVICE_FAILURE_ACTIONS);
end;
implementation
procedure TServiceHelper.SetServiceDescription(const Description: String);
var
SvcMgr: SC_HANDLE;
Svc: SC_HANDLE;
ServiceDescr: SERVICE_DESCRIPTION;
begin
{ Open service control manager }
SvcMgr := OpenSCManager(nil,nil,SC_MANAGER_ALL_ACCESS);
if SvcMgr = 0 then
begin
RaiseLastOSError;
end;
try
{ Open service }
Svc := OpenService(SvcMgr,PChar(Self.Name),STANDARD_RIGHTS_REQUIRED or SERVICE_CHANGE_CONFIG);
if Svc = 0 then
begin
RaiseLastOSError;
end;
try
{ Set service description }
ServiceDescr.lpDescription := PChar(Description);
if ChangeServiceConfig2(Svc,SERVICE_CONFIG_DESCRIPTION,@ServiceDescr) = False then
begin
RaiseLastOSError;
end;
finally
{ Close service handle }
CloseServiceHandle(Svc);
end;
finally
{ Close service control manager handle }
CloseServiceHandle(SvcMgr);
end;
end;
procedure TServiceHelper.SetServiceFailureActions(const Actions: SERVICE_FAILURE_ACTIONS);
var
SvcMgr: SC_HANDLE;
Svc: SC_HANDLE;
Access: DWORD;
I: Integer;
P: LPSC_ACTION;
begin
Access := STANDARD_RIGHTS_REQUIRED or SERVICE_CHANGE_CONFIG;
{ Check actions }
P := Actions.lpsaActions;
if P <> nil then
begin
for I := 1 to Actions.cActions do // FI:W528
begin
if P^.&Type = SC_ACTION_REBOOT then
begin
Access := Access or SERVICE_START;
end;
Inc(P);
end;
end;
{ Open service control manager }
SvcMgr := OpenSCManager(nil,nil,SC_MANAGER_ALL_ACCESS);
if SvcMgr = 0 then
begin
RaiseLastOSError;
end;
try
{ Open service }
Svc := OpenService(SvcMgr,PChar(Self.Name), Access);
if Svc = 0 then
begin
RaiseLastOSError;
end;
try
{ Set service failure actions }
if ChangeServiceConfig2(Svc,SERVICE_CONFIG_FAILURE_ACTIONS,@Actions) = False then
begin
RaiseLastOSError;
end;
finally
{ Close service handle }
CloseServiceHandle(Svc);
end;
finally
{ Close service control manager handle }
CloseServiceHandle(SvcMgr);
end;
end;
end.
HFUKUSHIさんのおっしゃるとおりChangeServiceConfig2に変更したところ、うまくいきました。
大変ありがとうございます。文字コードについて勉強しないとだめですね。
TServiceのヘルパも参考になります。
takaさんの方法は試しておりませんが、
ご回答いただいて、うれしかったです。