Inno Setup Windows DLL 函數調用,帶有指向結構的指針 (Inno Setup Windows DLL function call with pointer to structure)


問題描述

Inno Setup Windows DLL 函數調用,帶有指向結構的指針 (Inno Setup Windows DLL function call with pointer to structure)

I am trying to set a service's failure actions using Inno Setup's Pascal scripting language. I receive the classic "access violation at address..." error. Seems that it is impossible because the language don't have any support to pointers. Any ideas? Here is the code snippet:

<pre class="lang-pascal prettyprint-override">type
  TScAction = record
    aType1 : Longword;
    Delay1 : Longword;
    aType2 : Longword;
    Delay2 : Longword;
    aType3 : Longword;
    Delay3 : Longword;
  end;

type
  TServiceFailureActionsA = record
    dwResetPeriod : DWORD;
    pRebootMsg : String;
    pCommand : String;
    cActions : DWORD;
    saActions : TScAction;
  end;

function ChangeServiceConfig2(hService: Longword; dwInfoLevel: Longword; lpInfo: TServiceFailureActionsA): BOOL;
  external 'ChangeServiceConfig2A@advapi32.dll stdcall';

procedure SimpleChangeServiceConfig(AService: string);
var
  SCMHandle: Longword;
  ServiceHandle: Longword;
  sfActions: TServiceFailureActionsA;
  sActions: TScAction;
begin
  try
    SCMHandle := OpenSCManager('', '', SC_MANAGER_ALL_ACCESS);
    if SCMHandle = 0 then
      RaiseException('SimpleChangeServiceConfig@OpenSCManager: ' + AService + ' ' + 
        SysErrorMessage(DLLGetLastError));
    try
      ServiceHandle := OpenService(SCMHandle, AService, SERVICE_ALL_ACCESS);
      if ServiceHandle = 0 then
        RaiseException('SimpleChangeServiceConfig@OpenService: ' + AService + ' ' + 
          SysErrorMessage(DLLGetLastError));
      try

        sActions.aType1 := SC_ACTION_RESTART;
        sActions.Delay1 := 60000;               // First.nDelay: in milliseconds, MMC displayed in minutes
        sActions.aType2 := SC_ACTION_RESTART; 
        sActions.Delay2 := 60000;
        sActions.aType3 := SC_ACTION_RESTART; 
        sActions.Delay3 := 60000;

        sfActions.dwResetPeriod := 1;           // in seconds, MMC displayes in days
        //sfActions.pRebootMsg := null;         // reboot message unchanged
        //sfActions.pCommand := null;           // command line unchanged
        sfActions.cActions := 3;                // first, second and subsequent failures
        sfActions.saActions := sActions;        

        if not ChangeServiceConfig2(
           ServiceHandle,                       // handle to service
           SERVICE_CONFIG_FAILURE_ACTIONS,      // change: description
           sfActions)                           // new description
        then
          RaiseException('SimpleChangeServiceConfig@ChangeServiceConfig2: ' + AService + ' ' + 
            SysErrorMessage(DLLGetLastError));
      finally
        if ServiceHandle <> 0 then
          CloseServiceHandle(ServiceHandle);
      end;
    finally
      if SCMHandle <> 0 then
        CloseServiceHandle(SCMHandle);
    end;
  except
    ShowExceptionMessage;
  end;
end;
</code></pre>


參考解法

方法 1:

You have two problems in your script. Like Deanna suggested you have to use the var keyword in the declaration of the lpInfo parameter.

Also you need to change the TScAction type to an array with two elements.

Here is my script that you can include in your Inno Setup script.

<pre class="lang-pascal prettyprint-override">const
  SERVICE_CONFIG_DELAYED_AUTO_START_INFO  = 3;  //The lpInfo parameter is a pointer to a SERVICE_DELAYED_AUTO_START_INFO structure.
                                                //Windows Server 2003 and Windows XP:  This value is not supported.
  SERVICE_CONFIG_DESCRIPTION              = 1;  //The lpInfo parameter is a pointer to a SERVICE_DESCRIPTION structure.
  SERVICE_CONFIG_FAILURE_ACTIONS          = 2;  //The lpInfo parameter is a pointer to a SERVICE_FAILURE_ACTIONS structure.
                                                //If the service controller handles the SC_ACTION_REBOOT action, the caller must have
                                                // the SE_SHUTDOWN_NAME privilege. For more information, see Running with Special Privileges.
  SERVICE_CONFIG_FAILURE_ACTIONS_FLAG     = 4;  //The lpInfo parameter is a pointer to a SERVICE_FAILURE_ACTIONS_FLAG structure.
                                                //Windows Server 2003 and Windows XP:  This value is not supported.
  SERVICE_CONFIG_PREFERRED_NODE           = 9;  //The lpInfo parameter is a pointer to a SERVICE_PREFERRED_NODE_INFO structure.
                                                //Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP:  This value is not supported.
  SERVICE_CONFIG_PRESHUTDOWN_INFO         = 7;  //The lpInfo parameter is a pointer to a SERVICE_PRESHUTDOWN_INFO structure.
                                                //Windows Server 2003 and Windows XP:  This value is not supported.
  SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO = 6;  //The lpInfo parameter is a pointer to a SERVICE_REQUIRED_PRIVILEGES_INFO structure.
                                                //Windows Server 2003 and Windows XP:  This value is not supported.
  SERVICE_CONFIG_SERVICE_SID_INFO         = 5;  //The lpInfo parameter is a pointer to a SERVICE_SID_INFO structure.
  SERVICE_CONFIG_TRIGGER_INFO             = 8;  //The lpInfo parameter is a pointer to a SERVICE_TRIGGER_INFO structure. 
                                                //This value is not supported by the ANSI version of ChangeServiceConfig2.
                                                //Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP:  This value is not supported until Windows Server 2008 R2.

  SC_ACTION_NONE        = 0; // No action.
  SC_ACTION_REBOOT      = 2; // Reboot the computer.
  SC_ACTION_RESTART     = 1; // Restart the service.
  SC_ACTION_RUN_COMMAND = 3; // Run a command.

type
  TScAction = record
    aType1 : Longword;
    Delay1 : Longword;
  end;

type
  TServiceFailureActionsA = record
    dwResetPeriod : DWORD;
    pRebootMsg : String;
    pCommand : String;
    cActions : DWORD;
    saActions : array of TScAction;
  end;

function ChangeServiceConfig2(
  hService: Longword; 
  dwInfoLevel: Longword;
  var lpInfo: TServiceFailureActionsA): BOOL;
  external 'ChangeServiceConfig2A@advapi32.dll stdcall';

procedure SimpleChangeServiceConfig(AService: string);
var
  SCMHandle: Longword;
  ServiceHandle: Longword;
  sfActions: TServiceFailureActionsA;
  sActions: array of TScAction;
begin
  SetArrayLength(sActions ,3);
  try
    SCMHandle := OpenSCManager('', '', SC_MANAGER_ALL_ACCESS);
    if SCMHandle = 0 then
      RaiseException('SimpleChangeServiceConfig@OpenSCManager: ' + AService + ' ' + 
        SysErrorMessage(DLLGetLastError));
    try
      ServiceHandle := OpenService(SCMHandle, AService, SERVICE_ALL_ACCESS);
      if ServiceHandle = 0 then
        RaiseException('SimpleChangeServiceConfig@OpenService: ' + AService + ' ' + 
          SysErrorMessage(DLLGetLastError));
      try

        sActions[0].aType1 := SC_ACTION_RESTART;
        sActions[0].Delay1 := 60000;               // First.nDelay: in milliseconds, MMC displayed in minutes
        sActions[1].aType1 := SC_ACTION_RESTART; 
        sActions[1].Delay1 := 60000;
        sActions[2].aType1 := SC_ACTION_NONE; 
        sActions[2].Delay1 := 60000;

        sfActions.dwResetPeriod := 1;           // in seconds, MMC displayes in days
        //sfActions.pRebootMsg := null;         // reboot message unchanged
        //sfActions.pCommand := null;           // command line unchanged
        sfActions.cActions := 3;                // first, second and subsequent failures
        sfActions.saActions := sActions;        

        if not ChangeServiceConfig2(
           ServiceHandle,             // handle to service
           SERVICE_CONFIG_FAILURE_ACTIONS, // change: description
           sfActions)       // new description
        then
          RaiseException('SimpleChangeServiceConfig@ChangeServiceConfig2: ' + AService + ' ' + 
            SysErrorMessage(DLLGetLastError));
      finally
        if ServiceHandle <> 0 then
          CloseServiceHandle(ServiceHandle);
      end;
    finally
      if SCMHandle <> 0 then
        CloseServiceHandle(SCMHandle);
    end;
  except
    ShowExceptionMessage;
  end;
end;     
</code></pre>

方法 2:

Try using the var keyword in the declaration for the lpInfo parameter to specify that it's to pass a pointer to the structure to the function.

(by Charlie BrownstapelDeanna)

參考文件

  1. Inno Setup Windows DLL function call with pointer to structure (CC BY-SA 3.0/4.0)

#inno-setup #pointers






相關問題

如何從 InitializeWizard 中更改嚮導頁面的名稱和描述? (How do I change the name and description of a wizard page from within InitializeWizard?)

如何使用 Inno Setup 根據註冊表項選擇在文件夾中安裝插件/文件? (How do I use Inno Setup to optionally install a plugin/file in a folder based on a registry entry?)

Inno Setup - 透明閃屏 (Inno Setup - Transparent Splash Screen)

Instal versi Firebird yang benar (32bit atau 64bit) dengan Inno Setup (Install correct version of Firebird (32bit or 64bit) with Inno Setup)

Inno Setup - 防止提取文件將進度條設置為 100% (Inno Setup - Prevent extraction of files from setting progress bar to 100%)

在安裝期間將 .INI 文件從 UTF-8 編碼轉換為 ANSI (To convert a .INI file from UTF-8 encoding to ANSI during installation)

將 Inno Setup 安裝程序包裝在 MSI 中以便通過 AD 進行分發是否可行/明智? (Is it feasible/sensible to wrap an Inno Setup installer inside an MSI for easier distribution via AD?)

在沒有 ClickOnce 的情況下從安裝程序下載並安裝 .net framework 4.0? (Download and install .net framework 4.0 from installer without ClickOnce?)

如何在 Inno Setup 中設置“典型”和“自定義”安裝選項? (How to make a "Typical" and "Custom" installation option in Inno Setup?)

Inno Setup Windows DLL 函數調用,帶有指向結構的指針 (Inno Setup Windows DLL function call with pointer to structure)

更改 WizardForm.TasksList 偏移量 (Change WizardForm.TasksList offset)

如何在 Inno Setup 中修改錯誤信息? (How to modify error message in Inno Setup?)







留言討論