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 中的所有内容。

您还泄露了分配给 FeatureListBSTR,以及 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