该 WMI 实现不支持与ISupportErrorInfo
. 检查错误设施,如果是接口错误(FACILITY_ITF
),则调用GetErrorInfo
和 QI That IErrorInfo
to 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