C++ 运行 SHELLEXECUTEINFO 和附加功能名称
C++ Running SHELLEXECUTEINFO and appending feature name
我不懂 C++,我在尝试将字符串名称附加到 SHELLEXECUTEINFO 的 lpFile 时遇到问题。我从 Installshield 得到一个分号分隔的字符串,然后将它拆分并循环遍历它。然后,我试图附加从字符串中拆分出来的每个 'features',并通过 shell 执行将其发送到 dism.exe。
它在 swprintf_s(buf, _T("Dism.exe /Online /Enable-Feature /FeatureName:%s"), sizeof(buf), wc);
处失败如果我发表评论那么它至少会触发 dll 并抛出一个 dism.exe 错误,所以我知道调用工作正常。
template<typename Out>
void split(const std::string &s, char delim, Out result) {
std::stringstream ss;
ss.str(s);
std::string item;
while (std::getline(ss, item, delim)) {
*(result++) = item;
}
}
HRESULT __stdcall SplitString(IDispatch *pAction) {
// Create a pointer to the IsSuiteExtension COM interface
CComQIPtr<ISuiteExtension2> spSuiteExtension2 = pAction;
// Turn on notifications for both the progress bar(epfProgressValid) and the ui message(epfMessageValid).
EnumProgressFlags pf = EnumProgressFlags(EnumProgressFlags::epfMessageValid | EnumProgressFlags::epfProgressValid);
BSTR bstrFeatureList(_T("ENABLE_FEATURES")); // Property name to get. This should be a semi-colon delimeted list of features to enable for windows.
BSTR FeatureList = ::SysAllocStringLen(NULL, 2 * 38); // Where to store the property value
HRESULT hRet = spSuiteExtension2->get_Property(bstrFeatureList, &FeatureList); // Get the property value and store it in the 'FeatureList' variable
CW2A pszConverted(FeatureList);
using namespace std;
string strConverted(pszConverted);
vector<string> tokens;
split(strConverted, ';', back_inserter(tokens));
int numTokens = tokens.size();
for (int i = 0; i < numTokens; i++)
{
string t = tokens.at(i);
TCHAR buf[1024];
SHELLEXECUTEINFO ShExecInfo = { 0 };
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = NULL;
// Convert ANSI to Unicode
ATL::CA2W wc(tokens.at(i).c_str());
swprintf_s(buf, _T("Dism.exe /Online /Enable-Feature /FeatureName:%s"), sizeof(buf), wc); // HAVING ISSUES HERE. NEED TO APPEND tokens.at(i) AT %S
ShExecInfo.lpFile = buf;
ShExecInfo.lpParameters = _T("");
ShExecInfo.lpDirectory = _T("C:\Windows\SysNative");
ShExecInfo.nShow = SW_SHOW;
ShExecInfo.hInstApp = NULL;
ShellExecuteEx(&ShExecInfo);
WaitForSingleObject(ShExecInfo.hProcess, INFINITE);
}
//MessageBoxA(NULL, strConverted.c_str(), "testx", MB_OK);
return ERROR_SUCCESS;
}
由于您的代码使用 TCHAR
字符串,您应该使用 _stprintf_s()
而不是 swprintf_s()
。正如 Adrian McCarthy 所说,您以错误的顺序传递了它的参数,因此您的代码甚至不应该编译开始。
但更重要的是,根本不需要涉及ANSI或TCHAR
。你有 Unicode 输入,STL 和 ShellExecuteEx()
都支持 Unicode,所以只保留 Unicode 中的所有内容。
您还泄露了分配给 FeatureList
的 BSTR
,以及 ShellExecuteEx()
returns.[=23 的进程 HANDLE
=]
尝试更像这样的东西:
template <typename Out>
void split(const std::wstring &s, wchar_t delim, Out result) {
std::wistringstream iss(s);
std::wstring item;
while (std::getline(iss, item, delim)) {
*(result++) = item;
}
}
HRESULT __stdcall SplitString(IDispatch *pAction) {
// Create a pointer to the IsSuiteExtension COM interface
CComQIPtr<ISuiteExtension2> spSuiteExtension2 = pAction;
// Turn on notifications for both the progress bar(epfProgressValid) and the ui message(epfMessageValid).
EnumProgressFlags pf = EnumProgressFlags(EnumProgressFlags::epfMessageValid | EnumProgressFlags::epfProgressValid);
CComBSTR FeatureList;
HRESULT hRet = spSuiteExtension2->get_Property(CComBSTR(L"ENABLE_FEATURES"), &FeatureList); // Get the property value and store it in the 'FeatureList' variable
std::vector<std::wstring> tokens;
split(static_cast<BSTR>(FeatureList), L';', std::back_inserter(tokens));
int numTokens = tokens.size();
for (int i = 0; i < numTokens; i++)
{
std::wstring params = L"/Online /Enable-Feature /FeatureName:" + tokens.at(i);
SHELLEXECUTEINFOW ShExecInfo = { 0 };
ShExecInfo.cbSize = sizeof(ShExecInfo);
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.lpFile = L"Dism.exe";
ShExecInfo.lpParameters = params.c_str();
ShExecInfo.lpDirectory = L"C:\Windows\SysNative";
ShExecInfo.nShow = SW_SHOW;
if (ShellExecuteExW(&ShExecInfo))
{
WaitForSingleObject(ShExecInfo.hProcess, INFINITE);
CloseHandle(ShExecInfo.hProcess);
}
}
//MessageBoxW(NULL, FeatureList, L"testx", MB_OK);
return ERROR_SUCCESS;
}
也就是说,启动 EXE 时,您应该直接使用 CreateProcess()
而不是 ShellExecuteEx()
:
for (int i = 0; i < numTokens; i++)
{
std::wstring cmd = L"Dism.exe /Online /Enable-Feature /FeatureName:" + tokens.at(i);
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
if (CreateProcessW(NULL, &cmd[0], NULL, NULL, FALSE, 0, NULL, L"C:\Windows\SysNative", &si, &pi))
{
CloseHandle(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
}
}
您似乎调换了 swprintf_s 调用中参数的顺序。缓冲区大小应紧跟在缓冲区之后和格式字符串之前。
来自MSDN:
int swprintf_s(
wchar_t *buffer,
size_t sizeOfBuffer,
const wchar_t *format,
...
);
来自您的代码:
swprintf_s(buf, _T("Dism.exe /Online /Enable-Feature /FeatureName:%s"), sizeof(buf), wc);
我完全不知道你是怎么编译的。应该是这样的:
swprintf_s(buf, sizeof(buf), _T("Dism.exe /Online /Enable-Feature /FeatureName:%s"), wc);
更新: 这就是它使用错误顺序编译参数的原因:
除了 MSDN 文档中描述的 swprintf_s 之外,还有一个带有如下签名的模板化版本:
template <std::size_t N>
int swprintf_s(wchar_t (&Buffer)[N], const wchar_t * format, ...);
由于 buf
参数确实是一个固定长度的数组(与指向缓冲区的指针相反),因此选择了此签名版本(N
从 [= 的长度推导出来15=]),因此 sizeof(buf)
参数被视为对应于 %s
的字符串,而忽略实际的字符串参数 (wc
)。
您忽略或抑制了编译器警告。遗憾的是,在这种情况下,VC++ 发出的(非常好的)诊断没有被视为错误:
warning C4477: 'swprintf_s' : format string '%s' requires an argument of type 'wchar_t *', but variadic argument 1 has type 'unsigned int'
warning C4474: 'swprintf_s' : too many arguments passed for format string
note: placeholders and their parameters expect 1 variadic arguments, but 2 were provided
我不懂 C++,我在尝试将字符串名称附加到 SHELLEXECUTEINFO 的 lpFile 时遇到问题。我从 Installshield 得到一个分号分隔的字符串,然后将它拆分并循环遍历它。然后,我试图附加从字符串中拆分出来的每个 'features',并通过 shell 执行将其发送到 dism.exe。
它在 swprintf_s(buf, _T("Dism.exe /Online /Enable-Feature /FeatureName:%s"), sizeof(buf), wc);
处失败如果我发表评论那么它至少会触发 dll 并抛出一个 dism.exe 错误,所以我知道调用工作正常。
template<typename Out>
void split(const std::string &s, char delim, Out result) {
std::stringstream ss;
ss.str(s);
std::string item;
while (std::getline(ss, item, delim)) {
*(result++) = item;
}
}
HRESULT __stdcall SplitString(IDispatch *pAction) {
// Create a pointer to the IsSuiteExtension COM interface
CComQIPtr<ISuiteExtension2> spSuiteExtension2 = pAction;
// Turn on notifications for both the progress bar(epfProgressValid) and the ui message(epfMessageValid).
EnumProgressFlags pf = EnumProgressFlags(EnumProgressFlags::epfMessageValid | EnumProgressFlags::epfProgressValid);
BSTR bstrFeatureList(_T("ENABLE_FEATURES")); // Property name to get. This should be a semi-colon delimeted list of features to enable for windows.
BSTR FeatureList = ::SysAllocStringLen(NULL, 2 * 38); // Where to store the property value
HRESULT hRet = spSuiteExtension2->get_Property(bstrFeatureList, &FeatureList); // Get the property value and store it in the 'FeatureList' variable
CW2A pszConverted(FeatureList);
using namespace std;
string strConverted(pszConverted);
vector<string> tokens;
split(strConverted, ';', back_inserter(tokens));
int numTokens = tokens.size();
for (int i = 0; i < numTokens; i++)
{
string t = tokens.at(i);
TCHAR buf[1024];
SHELLEXECUTEINFO ShExecInfo = { 0 };
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = NULL;
// Convert ANSI to Unicode
ATL::CA2W wc(tokens.at(i).c_str());
swprintf_s(buf, _T("Dism.exe /Online /Enable-Feature /FeatureName:%s"), sizeof(buf), wc); // HAVING ISSUES HERE. NEED TO APPEND tokens.at(i) AT %S
ShExecInfo.lpFile = buf;
ShExecInfo.lpParameters = _T("");
ShExecInfo.lpDirectory = _T("C:\Windows\SysNative");
ShExecInfo.nShow = SW_SHOW;
ShExecInfo.hInstApp = NULL;
ShellExecuteEx(&ShExecInfo);
WaitForSingleObject(ShExecInfo.hProcess, INFINITE);
}
//MessageBoxA(NULL, strConverted.c_str(), "testx", MB_OK);
return ERROR_SUCCESS;
}
由于您的代码使用 TCHAR
字符串,您应该使用 _stprintf_s()
而不是 swprintf_s()
。正如 Adrian McCarthy 所说,您以错误的顺序传递了它的参数,因此您的代码甚至不应该编译开始。
但更重要的是,根本不需要涉及ANSI或TCHAR
。你有 Unicode 输入,STL 和 ShellExecuteEx()
都支持 Unicode,所以只保留 Unicode 中的所有内容。
您还泄露了分配给 FeatureList
的 BSTR
,以及 ShellExecuteEx()
returns.[=23 的进程 HANDLE
=]
尝试更像这样的东西:
template <typename Out>
void split(const std::wstring &s, wchar_t delim, Out result) {
std::wistringstream iss(s);
std::wstring item;
while (std::getline(iss, item, delim)) {
*(result++) = item;
}
}
HRESULT __stdcall SplitString(IDispatch *pAction) {
// Create a pointer to the IsSuiteExtension COM interface
CComQIPtr<ISuiteExtension2> spSuiteExtension2 = pAction;
// Turn on notifications for both the progress bar(epfProgressValid) and the ui message(epfMessageValid).
EnumProgressFlags pf = EnumProgressFlags(EnumProgressFlags::epfMessageValid | EnumProgressFlags::epfProgressValid);
CComBSTR FeatureList;
HRESULT hRet = spSuiteExtension2->get_Property(CComBSTR(L"ENABLE_FEATURES"), &FeatureList); // Get the property value and store it in the 'FeatureList' variable
std::vector<std::wstring> tokens;
split(static_cast<BSTR>(FeatureList), L';', std::back_inserter(tokens));
int numTokens = tokens.size();
for (int i = 0; i < numTokens; i++)
{
std::wstring params = L"/Online /Enable-Feature /FeatureName:" + tokens.at(i);
SHELLEXECUTEINFOW ShExecInfo = { 0 };
ShExecInfo.cbSize = sizeof(ShExecInfo);
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.lpFile = L"Dism.exe";
ShExecInfo.lpParameters = params.c_str();
ShExecInfo.lpDirectory = L"C:\Windows\SysNative";
ShExecInfo.nShow = SW_SHOW;
if (ShellExecuteExW(&ShExecInfo))
{
WaitForSingleObject(ShExecInfo.hProcess, INFINITE);
CloseHandle(ShExecInfo.hProcess);
}
}
//MessageBoxW(NULL, FeatureList, L"testx", MB_OK);
return ERROR_SUCCESS;
}
也就是说,启动 EXE 时,您应该直接使用 CreateProcess()
而不是 ShellExecuteEx()
:
for (int i = 0; i < numTokens; i++)
{
std::wstring cmd = L"Dism.exe /Online /Enable-Feature /FeatureName:" + tokens.at(i);
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
if (CreateProcessW(NULL, &cmd[0], NULL, NULL, FALSE, 0, NULL, L"C:\Windows\SysNative", &si, &pi))
{
CloseHandle(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
}
}
您似乎调换了 swprintf_s 调用中参数的顺序。缓冲区大小应紧跟在缓冲区之后和格式字符串之前。
来自MSDN:
int swprintf_s(
wchar_t *buffer,
size_t sizeOfBuffer,
const wchar_t *format,
...
);
来自您的代码:
swprintf_s(buf, _T("Dism.exe /Online /Enable-Feature /FeatureName:%s"), sizeof(buf), wc);
我完全不知道你是怎么编译的。应该是这样的:
swprintf_s(buf, sizeof(buf), _T("Dism.exe /Online /Enable-Feature /FeatureName:%s"), wc);
更新: 这就是它使用错误顺序编译参数的原因:
除了 MSDN 文档中描述的 swprintf_s 之外,还有一个带有如下签名的模板化版本:
template <std::size_t N>
int swprintf_s(wchar_t (&Buffer)[N], const wchar_t * format, ...);
由于 buf
参数确实是一个固定长度的数组(与指向缓冲区的指针相反),因此选择了此签名版本(N
从 [= 的长度推导出来15=]),因此 sizeof(buf)
参数被视为对应于 %s
的字符串,而忽略实际的字符串参数 (wc
)。
您忽略或抑制了编译器警告。遗憾的是,在这种情况下,VC++ 发出的(非常好的)诊断没有被视为错误:
warning C4477: 'swprintf_s' : format string '%s' requires an argument of type 'wchar_t *', but variadic argument 1 has type 'unsigned int'
warning C4474: 'swprintf_s' : too many arguments passed for format string
note: placeholders and their parameters expect 1 variadic arguments, but 2 were provided