C ++运行SHELLEXECUTEINFO并附加功能名称

时间:2017-06-01 20:21:12

标签: c++ installshield shellexecute

我不知道c ++和我在尝试将字符串名称附加到SHELLEXECUTEINFO的lpFile时遇到问题。我从Installshield获得一个以分号分隔的字符串,然后将其拆分并循环遍历它。我试图然后附加从字符串中拆分的每个“功能”,并通过shell执行将其发送到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; } 失败如果我评论outh然后它将至少触发dll并抛出dism.exe错误,所以我知道呼叫正常工作。

$ perl -e'CORE::say "100" =~ s/1*/\*/rg'
**0*0*

2 个答案:

答案 0 :(得分:2)

由于您的代码使用的是TCHAR个字符串,因此您应该使用_stprintf_s()代替swprintf_s()。正如Adrian McCarthy所说,你的参数传递顺序错误,所以你的代码甚至不应该编译开始。

但更重要的是,根本不需要ANSI或TCHAR。你有Unicode输入,STL和ShellExecuteEx()都支持Unicode,所以只保留所有的Unicode。

您还泄露了为BSTR分配的FeatureList,以及HANDLE返回的流程ShellExecuteEx()

尝试更像这样的东西:

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);
    }
}

答案 1 :(得分:1)

您好像已经改变了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);

UPDATE:这就是为什么用错误的顺序编译参数的原因:

除了MSDN文档中描述的swprintf_s之外,还有一个带有如下签名的模板化版本:

template <std::size_t N>
int swprintf_s(wchar_t (&Buffer)[N], const wchar_t * format, ...);

由于buf参数确实是固定长度数组(与指向缓冲区的指针相对),因此选择此签名版本(N从{{1}的长度推导出来}),因此buf参数被视为与sizeof(buf)对应的字符串,并忽略实际的字符串参数(%s)。

您要么忽略或禁止编译器警告。遗憾的是,VC ++在这种情况下发出的(非常好的)诊断不会被视为错误:

wc