在 WIX 代码中,我们声明了延迟的自定义操作,该操作将保护凭证和 辅助自定义操作,为其设置 CustomActionData 值,如下所示:
<CustomAction Id="SetUserNamePasswordForDefferedAction" Property="HandleAccountInfoCustomAction" Value="[ACCOUNT_USERNAME],[ACCOUNT_PASSWORD],[INSTALLLOCATION],[SERVICEACCOUNT]" HideTarget="no" Return="check" />
<CustomAction Id="HandleAccountInfoCustomAction" DllEntry="handle_account_info" BinaryKey="service_account_dll" Execute="deferred" HideTarget="no" Impersonate="no" Return="check"/>
<Binary Id="service_account_dll" SourceFile="service_account.dll" />
<InstallExecuteSequence>
<Custom Action="HandleAccountInfoCustomAction" Before="InstallFinalize">NOT Installed</Custom>
<Custom Action="SetUserNamePasswordForDefferedAction" Before="HandleAccountInfoCustomAction">NOT Installed</Custom>
</InstallExecuteSequence>
service_account_dll
在 Rust 中实现,但以 C 格式导出入口点 handle_account_info
。读取自定义操作数据的 Rust 代码如下:
unsafe fn retrieve_property_from_msi_db(
h_install: MSIHANDLE,
property_name_str: &str,
) -> StdResult<String, u32> {
let property_name = get_ptr_to_cstring(property_name_str);
custom_action_log(&format!("Retrieve {property_name_str} from MSI db"));
let mut msi_get_property_call_count = 0usize;
let mut res;
let mut req_status;
let mut buff = [0u8; BUFFER_SIZE];
res = 0u32;
req_status = 234u32; // ERROR_MORE_DATA
while (WIN32_ERROR(req_status) == ERROR_MORE_DATA || res == 0) && msi_get_property_call_count < 5 {
res = buff.len() as u32;
req_status = MsiGetPropertyA(
h_install,
PCSTR::from_raw(property_name),
PSTR::from_raw(buff.as_mut_ptr()),
Some(&mut res as *mut u32),
);
if msi_get_property_call_count > 0 && WIN32_ERROR(req_status) == ERROR_MORE_DATA {
// Register the occurrence of a known race condition in MSI
custom_action_log(format!("WARN: Multiple calls to MsiGetPropertyA ({msi_get_property_call_count}) returned ERROR_MORE_DATA.").as_str());
}
if res == 0 {
custom_action_log(format!("WARN: MsiGetPropertyA returned 0 bytes for property {property_name_str}.").as_str());
std::thread::sleep(Duration::from_millis(10));
}
msi_get_property_call_count += 1;
if res as usize > BUFFER_SIZE {
custom_action_log(format!("Custom action buffer size is too small: Needed {res}/available {BUFFER_SIZE}.").as_str());
return Err(ERROR_INSTALL_FAILED.0);
}
}
if ERROR_SUCCESS != WIN32_ERROR(req_status) {
custom_action_log(format!("Failed to retrieve user name from MSI:WIN_32ERROR {:?}, req_status: {req_status}", WIN32_ERROR(req_status)).as_str());
return Err(ERROR_INSTALL_FAILED.0);
}
let property = std::str::from_utf8_unchecked(&buff[..res as usize]);
Ok(property.to_string())
}
十分之二或三我在自定义操作日志中收到以下内容:
2023-11-14 13:29:47 - Retrieve CustomActionData from MSI db
2023-11-14 13:29:47 - WARN: MsiGetPropertyA returned 0 bytes for property CustomActionData.
2023-11-14 13:29:47 - WARN: MsiGetPropertyA returned 0 bytes for property CustomActionData.
2023-11-14 13:29:47 - WARN: MsiGetPropertyA returned 0 bytes for property CustomActionData.
2023-11-14 13:29:47 - WARN: MsiGetPropertyA returned 0 bytes for property CustomActionData.
2023-11-14 13:29:47 - WARN: MsiGetPropertyA returned 0 bytes for property CustomActionData.
2023-11-14 13:29:47 - service_account CustomActionData data retrieved from MSI db.
2023-11-14 13:29:47 - TROUBLESHOOTING: CustomActionData:
2023-11-14 13:29:47 - CustomActionData property is not in the expected format
成功运行会产生以下日志:
2023-11-14 13:28:48 - Retrieve CustomActionData from MSI db
2023-11-14 13:28:48 - service_account CustomActionData data retrieved from MSI db.
2023-11-14 13:28:48 - TROUBLESHOOTING: CustomActionData: ,,C:\MyApp,NT Authority\Local Service
请帮忙:-)
我通过改变策略解决了自己的问题。长话短说,自定义操作现在是一个独立的可执行文件,其中参数通过命令行传递,而不是在 DLL 中实现并由 wix 安装程序应用程序加载的自定义操作。 Wix 支持在不记录命令行参数的情况下执行此操作,因此不会记录密码。基本上,我遵循了这篇文档文章,特别是WixSilentExec风格。
我对为什么从 Wix 直接访问 dll 变得不稳定的理解是,通过 MSI 数据库中的 CustomActionData 属性传递参数值的机制很容易出现竞争条件。