LoginSignup
2
1

More than 3 years have passed since last update.

Windows Installer手引書 Part.16 C言語によるカスタムアクション1

Last updated at Posted at 2020-01-13

前の記事へ  目次へ  次の記事へ

即時実行カスタムアクション

即時実行に指定したカスタムアクションは、インストール先環境に変更を加えない処理のために利用します。カスタムアクションを即時実行する場合は、後で説明する遅延実行する場合に比べて、比較的に自由に情報の入出力が行えるようになっています。カスタムアクションをC言語で記述したとき、C言語の関数に情報を入力する場合も、処理結果を出力する場合も、プロパティを使用します。

ここでは例として、特定のファイルへのフルパスを与えると、ファイルのディレクトリ部分と、ファイル名部分に分割するアクションを作成します。ここで入力のプロパティは_inputPath、出力のプロパティは、_DirName_BaseNameになります。

CustomAction.cpp
#pragma comment(lib, "msi.lib")
#include <windows.h>
#include <msiquery.h>
#include <string>

extern "C" __declspec(dllexport) UINT expandPath(MSIHANDLE hInstall){
    TCHAR inStr[MAX_PATH];
    DWORD inStrLen = MAX_PATH;
    // プロパティ経由でフルパスを受け取る
    MsiGetProperty(hInstall, "_inputPath", inStr, &inStrLen);
    // ファイル名直前の\の位置を取得し
    std::string manipulateStr = inStr;
    std::string::size_type discripterPos = manipulateStr.find_last_of("\\");
    // プロパティ経由で結果を返す
    MsiSetProperty(hInstall, "_DirName", manipulateStr.substr(0, discripterPos).c_str());
    MsiSetProperty(hInstall, "_BaseName", manipulateStr.substr(discripterPos + 1).c_str());
    return ERROR_SUCCESS;
}

ビルドするには、下記のコマンドをx86 Native Tools Command PromptやVisual Studioのメイクファイルから実行します。この結果、CustomAction.dllを得ることができます。

cl /LD CustomAction.cpp

ここから、C言語のソースの詳細について説明していきます。カスタムアクションの関数プロトタイプは本例の様に記述しておけば、インストーラーから呼ぶことができます。この書き方なら、関数をDLL外部から参照するための.defファイルが不要になるため、ビルドが簡単になります。

カスタムアクションのプロトタイプ宣言
extern "C" __declspec(dllexport) UINT 関数名(MSIHANDLE hInstall)

また、カスタムアクションのソースにあるように、#pragmaを使ってリンクするライブラリを指定すれば、ビルドコマンドを変更する必要もなくなり、メンテナンス対象はC言語のソースだけになり、便利です。

関数の入出力に使われるプロパティのアクセスは、読み込みはMsiGetProperty()関数を、書き出しはMsiSetProperty()関数を使います。ここで使用するプロパティの名称には、特に制限がなく、プロパティの一般的な命名規則にのっとっていれば、どのような名前でも問題ありません。

カスタムアクションの関数は、Custom Action Return Valuesで説明されている以下の値を返すことができます。

戻り値 説明
ERROR_FUNCTION_NOT_CALLED アクションは実行されなかった。
ERROR_SUCCESS アクションは成功した。
ERROR_INSTALL_USEREXIT ユーザーが中断した。
ERROR_INSTALL_FAILURE 修復できないエラーが発生した。
ERROR_NO_MORE_ITEMS エラーではないが、何らかの処理がスキップされた。

気を付けなければならないのは、カスタムアクションがエラーを返すと、ロールバックが実行され元に戻される、ということです。特にアンインストール時にエラーが発生すると、製品がアンインストールできなくなってしまいます。アンインストール時のカスタムアクションの実行時エラーには十分気を付ける必要1があります。

以下に、このカスタムアクションの使用例を示します。

Product.wxs
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Product Id="28123AA8-D6A7-424D-8495-7F5AE87C05AB" Name="Part16_01" Language="1033" Version="1.0.0" Manufacturer="tohshima" UpgradeCode="a5a0b62e-b912-4d21-ab16-1f23c79a5ab3">
        <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

        <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
        <MediaTemplate />

        <Binary Id="CA" SourceFile="CA\CustomAction.dll"/>
        <Property Id="_inputPath" Value="C:\Windows\System32\calc.exe" />
        <CustomAction Id="aExpandPath" BinaryKey="CA" DllEntry="expandPath" Execute="immediate" Return="check"/>
        <CustomAction Id="aPrExpanded" Script="vbscript" Execute="immediate" Return="ignore">
            <![CDATA[
MsgBox("_DirName : "+Session.Property("_DirName")+Chr(13)+"_BaseName : "+Session.Property("_BaseName"))
            ]]>
        </CustomAction>
        <InstallExecuteSequence>
            <Custom Action="aExpandPath" After="InstallInitialize">not Installed</Custom>
            <Custom Action="aPrExpanded" After="aExpandPath">not Installed</Custom>
        </InstallExecuteSequence>

        <Feature Id="ProductFeature" Title="Part16_01" Level="1">
            <ComponentGroupRef Id="ProductComponents" />
        </Feature>
    </Product>

    <Fragment>
        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="ProgramFilesFolder">
                <Directory Id="INSTALLFOLDER" Name="Part16_01" />
            </Directory>
        </Directory>
    </Fragment>

    <Fragment>
        <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
            <Component Id="cExe" Guid="{46BEB206-817D-43DD-B86D-49A1C2406D4F}">
                <File Id="fExe" Source="SimpleMsgBox.exe" KeyPath="yes"/>
            </Component>
        </ComponentGroup>
    </Fragment>
</Wix>

BinaryエレメントからInstallExecuteSequenceエレメントまでに着目してください。以下のような処理を実行しています。

  • Propertyエレメントで_inputPathプロパティに電卓アプリへのフルパスを格納
  • さきに作成したexpandPathカスタムアクションを実行(aExpandPathアクション)
  • これにより、_DirNameプロパティに電卓アプリが存在するパスが、_BaseNameプロパティに電卓アプリのファイル名が格納される
  • _DirNameプロパティと_BaseNameプロパティの内容をダイアログボックスに表示(aPrExpandedアクション)

遅延実行カスタムアクション

インストール先環境に変更を加えるカスタムアクションは、遅延実行する必要があります。遅延実行は、別のプロセスが起動され、そのうえで実行されるため、情報の受け渡しに大きな制限があります。カスタムアクションの入力として利用できるプロパティは、1つのカスタムアクションあたり1つのプロパティだけ。プロパティに値を出力することはできません。

ここでは例として、次回OS起動時に1回だけ実行されるRunOnceレジストリにプログラムを登録するアクションを作成します。

CustomAction.cpp
#pragma comment(lib, "msi.lib")
#pragma comment(lib, "Advapi32.lib")
#include <windows.h>
#include <msiquery.h>

#define KEY_RUNONCE "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"
#define STR_MAX 512

extern "C" __declspec(dllexport) UINT setRunOnce(MSIHANDLE hInstall){
    TCHAR inStr[MAX_PATH];
    DWORD inStrLen = MAX_PATH;
    HKEY hKey;
    // プロパティ経由でフルパスを受け取る
    MsiGetProperty(hInstall, "CustomActionData", inStr, &inStrLen);
    // レジストリに書き込む
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, KEY_RUNONCE, 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
        return ERROR_INSTALL_FAILURE;
    if (RegSetValueEx(hKey, "tohshima Products", 0, REG_SZ, reinterpret_cast<const BYTE*>(inStr), strnlen(inStr, inStrLen)) != ERROR_SUCCESS)
        return ERROR_INSTALL_FAILURE;
    if (RegCloseKey(hKey) != ERROR_SUCCESS)
        return ERROR_INSTALL_FAILURE;
    return ERROR_SUCCESS;
}

MsiGetProperty()関数に注目してください。遅延実行時には、カスタムアクションに渡されるプロパティは、常にCustomActionDataというプロパティ名に変換され渡されます。ここで、「変換」と書いたのは、プロパティに値を格納するときは、別のプロパティ名だからです。

以下に、このカスタムアクションの使用例を示します。このインストーラーでインストールしているSimpleMsgBox.exeは、Part.15の下準備で作成したプログラムです。このプログラムを、RunOnceレジストリに登録します。インストール後にOSを再起動すると、「This is test!」というメッセージが表示されます。

Product.wxs
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Product Id="1097FE92-F1AD-4910-BC6E-E3A8CF9E667D" Name="Part16_02" Language="1033" Version="1.0.0" Manufacturer="tohshima" UpgradeCode="79571ef1-b65b-4c7f-9227-030216080610">
        <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

        <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
        <MediaTemplate />

        <Binary Id="CA" SourceFile="CA\CustomAction.dll"/>
        <CustomAction Id="aSetRunOnceArg" Property="aSetRunOnceExe" Value="&quot;[#fExe]&quot; &quot;This is test!&quot;" Execute="immediate"/>
        <CustomAction Id="aSetRunOnceExe" BinaryKey="CA" DllEntry="setRunOnce" Execute="deferred" Impersonate="no"/>
        <InstallExecuteSequence>
            <Custom Action="aSetRunOnceArg" After="InstallInitialize">NOT Installed</Custom>
            <Custom Action="aSetRunOnceExe" After="aSetRunOnceArg">NOT Installed</Custom>
        </InstallExecuteSequence>

        <Feature Id="ProductFeature" Title="Part16_02" Level="1">
            <ComponentGroupRef Id="ProductComponents" />
        </Feature>
    </Product>

    <Fragment>
        <Directory Id="TARGETDIR" Name="SourceDir">
            <Directory Id="ProgramFilesFolder">
                <Directory Id="INSTALLFOLDER" Name="Part16_02" />
            </Directory>
        </Directory>
    </Fragment>

    <Fragment>
        <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
            <Component Id="cExe" Guid="{2C4BA3D4-6F7A-4523-A232-5858713CBE8D}">
                <File Id="fExe" Source="SimpleMsgBox.exe" KeyPath="yes" />
            </Component>
        </ComponentGroup>
    </Fragment>
</Wix>

BinaryエレメントからInstallExecuteSequenceエレメントまでに着目してください。カスタムアクションに渡すデータをプロパティに格納しているのは、aSetRunOnceArgというIdのカスタムアクションですが、ここではaSetRunOnceExeという名称のプロパティに値を格納しています。この名前は、C言語で書かれたカスタムアクションのIdと同じです。遅延実行するカスタムアクションでは、そのIdと同名のプロパティに値を設定すれば、値を渡すことができるのです。

以上見てきたように、即時実行されるカスタムアクションと遅延実行されるカスタムアクションは、引数の受け渡し方法が異なります。自作したカスタムアクションは、どちらで使用することを想定しているのか、しっかり管理しておく必要があります。

前の記事へ  目次へ  次の記事へ


  1. アンインストール時はカスタムアクションはエラーを返さない、などの配慮が必要になってきます。テスト環境で発生しないエラーも、ほかの環境では起きるかもしれません。カスタムアクションが利用する機能について、十分なエラー処理を検討してください。 

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1