用于创建文件的 Wix C++ 自定义操作

Wix C++ Custom Action for Creating File

之前我编写了一个 C# 自定义操作,它应该将 PropertiesValues 传递给安装程序并创建一些配置文件。由于 C# 自定义操作已完成,我的安装程序也依赖于 .Net。删除 .Net 依赖项的唯一方法是在 C++.

中编写自定义操作

我试图将其转换为 C++ 的之前的 C# 代码如下:

session.Log("Entering WriteFileToDisk");
string ipAddress = session["IPADDRESS"];
string productCode = session["ProductCode"];
string config_path = "C:\SomeFolderToInstall\";
string compression = session["COMPRESSION"];
string ssl = session["SSL"];
if (string.IsNullOrEmpty(compression))
{
      compression = "True"; //default true
}
if (string.IsNullOrEmpty(ssl))
{
      ssl = "False"; //default false
}
if (string.IsNullOrEmpty(ipAddress))
{
      ipAddress = "127.0.0.1";
}
string temp = @"
{{
      ""logpoint_ip"" : ""{0}"",
      ""compression"" : ""{1}"",
      ""ssl"": ""{2}"" 
}}";
string filePath = "C:\SomeFolderToInstall\lpa.config";
System.IO.FileInfo file = new System.IO.FileInfo(filePath);
file.Directory.Create(); // If the directory already exists, this method does nothing.
System.IO.File.WriteAllText(file.FullName, config);
System.IO.File.WriteAllText(config_path + "productcode.txt", productCode);
session.Log("Confile file is written");
session.Log("Product Code file is written");
return ActionResult.Success;

以上C#代码的Visual C++代码如下:

#include "stdafx.h"    
UINT __stdcall WriteFileToDisk(MSIHANDLE hInstall)
{
    HRESULT hr = S_OK;
    UINT er = ERROR_SUCCESS;

    hr = WcaInitialize(hInstall, "WriteFileToDisk");
    ExitOnFailure(hr, "Failed to initialize");

    LPWSTR ip = NULL;
    hr = WcaGetProperty(L"IPADDRESS",&ip);
    ExitOnFailure(hr, "Failure in IPADDRESS");

    if(ip == L"" || ip == NULL)
    {
        ip = L"127.0.0.1";
    }
    WcaLog(LOGMSG_STANDARD, (PCSTR)ip);
    LPWSTR ssl = NULL;
    hr = WcaGetProperty(L"SSL",&ssl);
    ExitOnFailure(hr, "Failure in SSL");

    if(ssl == L"" || ssl == NULL)
    {
        ssl = L"False";
    }
    LPWSTR comp = NULL;
    hr = WcaGetProperty(L"COMPRESSION",&comp);
    ExitOnFailure(hr, "Failure in COMPRESSION");

    if(comp == L"" || comp == NULL)
    {
        comp = L"True";
    }

    WcaLog(LOGMSG_STANDARD, "Got the Parameters");
    char buffer[150];
    sprintf(buffer, "{\n\"ipaddress\": \"%s\",\n\"ssl\": \"%s\",\n\"compression\":\"%s\"\n}",ip,ssl,comp);
    WcaLog(LOGMSG_STANDARD, "Config Generated is : ");
    WcaLog(LOGMSG_STANDARD, buffer);

    HANDLE hFile;
    hFile = CreateFile(L"C://LogPointAgent//some_config.config",                // name of the write
                       GENERIC_WRITE,          // open for writing
                       0,                      // do not share
                       NULL,                   // default security
                       CREATE_NEW,             // create new file only
                       FILE_ATTRIBUTE_NORMAL,  // normal file
                       NULL);

     if (hFile == INVALID_HANDLE_VALUE) 
    { 
        WcaLog(LOGMSG_STANDARD, "Invalid Handle for Config File");
        ExitFunction();
    }
     BOOL bErrorFlag;
     DWORD dwBytesToWrite = (DWORD)strlen(buffer);
     DWORD dwBytesWritten = 0;
     bErrorFlag = WriteFile( 
                    hFile,           // open file handle
                    buffer,      // start of data to write
                    dwBytesToWrite,  // number of bytes to write
                    &dwBytesWritten, // number of bytes that were written
                    NULL);            // no overlapped structure


     if (FALSE == bErrorFlag)
    {
        WcaLog(LOGMSG_STANDARD, "Terminal failure: Unable to write to file.\n");
    }
    else
    {
        if (dwBytesWritten != dwBytesToWrite)
        {
            // This is an error because a synchronous write that results in
            // success (WriteFile returns TRUE) should write all data as
            // requested. This would not necessarily be the case for
            // asynchronous writes.
            WcaLog(LOGMSG_STANDARD, "Error: dwBytesWritten != dwBytesToWrite\n");
        }
        else
        {
            WcaLog(LOGMSG_STANDARD, "Wrote Config file Successfully");
        }
    }

      CloseHandle(hFile);
LExit:
    er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
    return WcaFinalize(er);
}

// DllMain - Initialize and cleanup WiX custom action utils.
extern "C" BOOL WINAPI DllMain(
    __in HINSTANCE hInst,
    __in ULONG ulReason,
    __in LPVOID
    )
{
    switch(ulReason)
    {
    case DLL_PROCESS_ATTACH:
        WcaGlobalInitialize(hInst);
        break;

    case DLL_PROCESS_DETACH:
        WcaGlobalFinalize();
        break;
    }

    return TRUE;
}

下面是 Wix 文件代码,用于从 C++ 自定义操作生成的 dll 中调用函数 WriteFileToDisk

<Binary Id="SetupCA"  SourceFile="..\WixCustomActionCPP\bin\Release\WixCustomActionCPP.dll"/>
<CustomAction Id="WRITEFILETODISK" Execute="immediate" BinaryKey="SetupCA" DllEntry="WriteFileToDisk" />
<InstallExecuteSequence>
  <Custom Action="WRITEFILETODISK" Before="InstallFinalize">NOT Installed</Custom>
</InstallExecuteSequence>

当使用命令 msiexec /i installer.msi /l*v out.txt IPADDRESS="192.168.2.208" 安装安装程序时,它应该生成配置为:

{
"ip":"192.168.2.208",
"ssl":"False",
"compression":"True"
}

但它现在生成的是:

{
"ipaddress": "1",
"ssl": "",
"compression":""
}

这里的C++代码有什么问题?任何帮助,将不胜感激。谢谢。

看看这个答案:

C++ Custom Action returns empty string

您似乎在使用 Ansi 格式来获取 Unicode 字符串。

另外,您似乎也在 sprintf 中混淆了 Unicode 和 Ansi。该代码应使用 WCHAR 和其他 Unicode 结构。忘记 Ansi,忘记 char 和 sprintf(将 wsprintf 用于 Unicode),我假设 VC++ 构建无论如何都是 Unicode。