3

我在返回 HRESULTS 的 WMI/WBEM 接口上调用方法。我想向用户显示这些错误代码的有意义的错误消息。但是,当我查看 HRESULT 的错误消息时,我只会得到像“IDispatch error #3598”这样的字符串。

我是否可以找到解释其含义的这些 IDispatch 错误代码的列表?

可能出现错误的示例代码:

IWbemLocator *pLocator = NULL;
IWbemServices *pNamespace = NULL;
hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLocator);
if (FAILED(hr))
   return hr;

hr = pLocator->ConnectServer(wPath, NULL, NULL, NULL, 0, NULL, NULL, &pNamespace);
if(FAILED(hr))
   return hr;

错误查找:

CString sMessage = _com_error(nError).ErrorMessage();

// sMessage now contains a string like "IDispatch error #3598"

注意:没有帮助 - 它不包含我得到的 HRESULTS。它们也不包含在 winerror.h 中。

4

2 回答 2

6

COM 服务器可以生成自己的 HRESULT 错误代码。IErrorInfo 接口帮助客户端获取错误描述。你没有给 _com_error 类做这项工作的机会,你没有将 IErrorInfo 接口指针传递给构造函数。

首先检查 ISupportErrorInfo 的接口并调用其 InterfaceSupportsErrorInfo() 方法来验证是否支持错误报告。接下来调用 GetErrorInfo() 以获取 IErrorInfo 接口指针。MSDN 文档在这里

于 2011-02-05T17:51:43.023 回答
0

该 WMI 实现不支持与ISupportErrorInfo. 检查错误设施,如果是接口错误(FACILITY_ITF),则调用GetErrorInfo和 QI That IErrorInfoto IWbemClassObject。从该对象中,您可以获得Get()一些详细信息,但它可能不会描述错误原因。

  string Description;
  string Operation;
  string ParameterInfo;
  string ProviderName;
  uint32 StatusCode;

https://docs.microsoft.com/en-us/windows/win32/wmisdk/retrieving-an-error-code#handling-an-error-using-c

要获得有意义的消息,您可以调用FormatMessage并指定C:\Windows\System32\wbem\wmiutils.dll为消息模块。 https://docs.microsoft.com/en-us/windows/win32/wmisdk/wmi-error-constants

或者

使用IWbemStatusCodeText::GetErrorCodeText/GetFacilityCodeText. 该界面应该返回相同的消息,但我不确定。

这是代码

void CheckErrorWMI(HRESULT orig_hres/*, IUnknown* unk, REFIID iid*/)
{
    if (SUCCEEDED(orig_hres))
        return;

    if (HRESULT_FACILITY(orig_hres) != FACILITY_ITF)
    {
        // Common error handling

        // does the magic ISupportErrorInfo dance for us and raise _com_error
        //_com_issue_errorex(orig_hres, unk, iid);
        // but it's not interface error so, it won't help much
        // -OR-
        // use stdlib (msvc STL will pass orig_hres to FormatMessage)
        throw std::system_error(orig_hres, std::system_category(), "some text");
    }
    else
    {
        // WMI specific error handling

        std::string str = "HRESULT = ";
        str += std::to_string(orig_hres); // TODO: as hex
        str += '\n';

        HRESULT hres;

        if (IErrorInfoPtr errInfo; SUCCEEDED(hres = GetErrorInfo(0, &errInfo))
            && errInfo && reinterpret_cast<intptr_t>(errInfo.GetInterfacePtr()) != -1)
            if (IWbemClassObjectPtr errObj; SUCCEEDED(errInfo.QueryInterface(IID_IWbemClassObject, &errObj)))
            {
                SAFEARRAY* sfArray{};

                hres = errObj->GetNames(nullptr, WBEM_FLAG_NONSYSTEM_ONLY/*WBEM_FLAG_ALWAYS*/, nullptr, &sfArray);
                if (SUCCEEDED(hres))
                {
                    DEFER{ SafeArrayDestroy(sfArray); };

                    LONG lstart, lend;
                    SafeArrayGetLBound(sfArray, 1, &lstart);
                    SafeArrayGetUBound(sfArray, 1, &lend);

                    BSTR* pbstr{};
                    hres = SafeArrayAccessData(sfArray, reinterpret_cast<void**>(&pbstr));
                    if (SUCCEEDED(hres))
                    {
                        DEFER{ SafeArrayUnaccessData(sfArray); };

                        VARIANT var;

                        for (LONG nIdx = lstart; nIdx <= lend; nIdx++)
                        {
                            hres = errObj->Get(pbstr[nIdx], 0, &var, nullptr, nullptr);

                            if (FAILED(hres))
                                continue;

                            DEFER{ VariantClear(&var); };

                            if (!str.empty())
                                str += '\n';
                            str += utf8_encode(pbstr[nIdx]);
                            str += " = ";
                            if (V_VT(&var) == VT_NULL)
                            {
                                str += "<null>";
                                continue;
                            }

                            hres = V_VT(&var) == VT_BSTR ? S_OK : VariantChangeType(&var, &var, VARIANT_ALPHABOOL, VT_BSTR);
                            if (hres != S_OK)
                                continue;

                            if (BSTR ss = V_BSTR(&var))
                                str += utf8_encode(ss);
                        }
                    }
                }
            }

        if (IWbemStatusCodeTextPtr sct; SUCCEEDED(sct.CreateInstance(CLSID_WbemStatusCodeText, nullptr, CLSCTX_INPROC_SERVER)))
        {
            _bstr_t msg;
            // https://referencesource.microsoft.com/#System.Management/managementexception.cs,747 GetMessage(ManagementStatus errorCode)
            hres = sct->GetErrorCodeText(orig_hres, 0, 1, msg.GetAddress()); // pass 1 to lFlags to not append \r\n (from .Net interop impl)
            if (hres != WBEM_S_NO_ERROR) // Just in case it didn't like the flag=1, try it again with flag=0.
                hres = sct->GetErrorCodeText(orig_hres, 0, 0, msg.GetAddress());
            if (SUCCEEDED(hres))
            {
                if (!str.empty())
                    str += "\n\n";
                str += "Message: ";
                str += utf8_encode(msg.GetBSTR());
            }

            hres = sct->GetFacilityCodeText(orig_hres, 0, 0, msg.GetAddress());
            if (SUCCEEDED(hres))
            {
                str += "Facility: ";
                str += utf8_encode(msg.GetBSTR());
            }
        }
        if (str.size() <= 22) // just "HRESULT = 123", probably not WMI error
            _com_raise_error(orig_hres); // raise _com_error

        throw std::runtime_error(str);
    }
}

DEFER只是 ScopeGuard 宏(如 Boost.ScopeExit 等)

输出:

HRESULT = -2147217385

Description = <null>
Operation = ExecQuery
ParameterInfo = SELECT 1+8 FROM Win32_PnPAllocatedResource
ProviderName = WinMgmt
StatusCode = <null>

Message: Query was not syntactically valid. Facility: WMI
于 2021-09-13T17:51:46.070 回答