1

私の製品の以前のインストールは、展開プロジェクト (vdproj) として Visual Studio 2008 で行われました。ユーザーには、ユーザーごと (私だけ) またはマシンごと (全員) にインストールするオプションがありました。WiX で新規インストールを行っています。マシンごとに製品をインストールした場合、新規インストールによって以前のバージョンがアンインストールされます。ただし、%ProgramFiles% でユーザーごとに製品をインストールした場合、新しいインストールでは以前のものがアンインストールされず、同じフォルダーに 2 つのインストールが混乱してしまいます。

この状況でインストールを検出して停止したい。自動アンインストールはなく、検出して停止するだけです。ソリューションは、あるユーザーが以前にユーザーごとのインストールを行い、別のユーザーがマシンごとに新しいインストールを行っている場合に対処する必要があります。DirectorySearch要素を使用できると思いFileSearchましたが、UI でターゲット フォルダーが決定される前に実行されます。

WiX コードが含まれている場合は、小さな例を提供してください。そこから学びます。

編集: AppSearch 機能DirectorySearchとを使用してプロパティを追加し、要素FileSearchで条件をチェックするとCondition、インストールの起動時に条件がチェックされます。これは私が望むものではありません。ユーザーはターゲット フォルダーを変更する可能性があるため、ターゲット フォルダーを変更できなくなった後、いくつかのステップでターゲット フォルダー内のファイルの存在を確認する必要があります。

私の素朴さで、前の段落で述べたプロパティの値をカスタムアクションで読み取ろうとしましSetPropertyたが、それも機能しません。値は読み取れますが、セットアップ開始時に決定された値です。

適切な解決策には、おそらく別の質問に対する回答が含まれているはずです。WiXトリックコミュニティwikiで与えられたヒントに従って、私は現在JScriptをいじっています。Rob Mensching のブログ投稿で言及されているように、ウイルス対策の介入を避けるために、おそらく .dll にします。

4

2 に答える 2

3

とにかく、自動アンインストールは不可能です。メジャー アップグレードは、ユーザーごとからインストールごとに移行できず、ミューテックスの考慮事項とスコープの考慮事項 (ユーザーごとのインストールは別のユーザーである可能性があります) により、インストール中に単純にアンインストールを呼び出すことはできません。

インストールは別のユーザー コンテキストで実行された可能性があるため、メタ状態をチェックするための MSI API 呼び出し ( ComponentSearch など) は機能しません。

これは、ファイルを探すための AppSearch だけが残っていることを意味します。次のようなスニペットは、プロパティ FOUNDMYFILE に、MyFile.dll が見つかった場合のフル パスを設定します。

<Property Id="FOUNDMYFILE">
  <DirectorySearch Id="FindMyFile" Path="[ProgramFilesFolder]MyCompany\MyProduct">
    <FileSearch Name="MyFile.dll"/>
  </DirectorySearch>
</Property>

次に行うことは、MyFile.dll が見つかり、製品がまだインストールされていない場合、インストールをブロックすることです。

<Condition Message="[ProductName] cannot install due to prexisting
per-user installation.">FOUNDMYFILE and Not Installed</Condition>

インストールされていなくても DLL が見つかった場合、この状態によりインストールが妨げられます。その後の修復とアンインストールを妨げることはありません。

ただし、これはマイナー アップグレードでのみ機能します。メジャー アップグレードの場合、ProductCode が変更され、インストーラーの観点からは、以前のバージョンがインストールされていてもインストールされません。その場合は、次のように言う必要があります。

<Condition Message="[ProductName] cannot install due to prexisting
 per-user installation.">FOUNDMYFILE and Not MAJORUPGRADEPROPERTY and
 Not Installed</Condition>
于 2012-12-03T13:53:40.843 に答える
1

これが私が最終的にそれをした方法です。後にキューに入れられたカスタムアクションを呼び出していますRemoveExistingProducts

<Property Id='ERRORIFFILESFOUND_MESSAGE'>!(loc.OlderVersionNotRemoved)</Property>

<CustomAction Id="CA.ErrorIfFilesFound"
  BinaryKey="SetupSupport.dll" DllEntry="ErrorIfFilesFound"
  Execute="immediate" Return="check" />

<CustomAction Id='CA.ErrorIfFilesFound.SetDirectory' 
  Property='ERRORIFFILESFOUND_DIRECTORY' Value='[INSTALLDIR]' />

<InstallExecuteSequence>
  <Custom Action='CA.ErrorIfFilesFound.SetDirectory' After='RemoveExistingProducts' />
  <Custom Action='CA.ErrorIfFilesFound' After='CA.ErrorIfFilesFound.SetDirectory'>
    Not MAJORUPGRADEPROPERTY and Not Installed
  </Custom>
</InstallExecuteSequence>

この時点以降、可能であれば古いインストールが削除されるため、ターゲットフォルダーにファイルが残っている場合は、問題が発生しています。この瞬間はかなり遅くなりますが、既存のファイルがアンインストールされるかどうかを検出する方法がわかりません。カスタムアクションのコードは、ターゲットフォルダーが存在し、空でないかどうかをチェックし、空でない場合は終了します(ExitOnFailure簡潔にするためにエラー処理は削除されています)。

UINT WINAPI ErrorIfFilesFound(MSIHANDLE hInstall)
{
    HRESULT hr = S_OK;
    LPWSTR pwszDirectory = NULL;
    LPWSTR pwszMessage = NULL;

    hr = ::WcaInitialize(hInstall, "ErrorIfFindFile");

    hr = ::WcaGetProperty(L"ERRORIFFILESFOUND_DIRECTORY", &pwszDirectory);

    if (PathIsDirectory(pwszDirectory) && !PathIsDirectoryEmpty(pwszDirectory))
    {
        hr = ::WcaGetProperty(L"ERRORIFFILESFOUND_MESSAGE", &pwszMessage);

        ProcessInstallMessage(hInstall,
            INSTALLMESSAGE_ERROR, pwszMessage, pwszDirectory);

        hr = -1;
    }

LExit:
    ReleaseStr(pwszMessage);
    ReleaseStr(pwszDirectory);

    UINT err = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
    return ::WcaFinalize(err);
}

void ProcessInstallMessage(MSIHANDLE hInstall,
    INSTALLMESSAGE type, TCHAR* lpszMessage, TCHAR* lpszParam)
{
    UINT rc;
    MSIHANDLE hMsg;
    UINT uiFieldNumber = lpszParam == NULL ? 0 : 1;

    hMsg = MsiCreateRecord(uiFieldNumber);

    if (hMsg != 0)
    {
        if ((rc = MsiRecordSetString(hMsg, 0, lpszMessage)) != ERROR_SUCCESS)
        {}
        else if (lpszParam != NULL &&
            (rc = MsiRecordSetString(hMsg, 1, lpszParam)) != ERROR_SUCCESS)
        {}
        else
        {
            rc = MsiProcessMessage(hInstall, type, hMsg);
        }

        rc = MsiCloseHandle(hMsg);
    }
}
于 2012-12-12T16:09:18.687 に答える