用于创建文件的 Wix C++ 自定义操作
Wix C++ Custom Action for Creating File
之前我编写了一个 C# 自定义操作,它应该将 Properties
的 Values
传递给安装程序并创建一些配置文件。由于 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。
之前我编写了一个 C# 自定义操作,它应该将 Properties
的 Values
传递给安装程序并创建一些配置文件。由于 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。