如何通过C++读取WQL查询获取的WMI日志文件所有字段?
Hey there! Let's figure out why only a couple of fields are returning correct values in your WMI query results, and how to fix the rest. The core issue here is that you're not accounting for the different data types returned by WMI properties—each field uses a specific VARIANT type, and accessing the wrong VARIANT member leads to garbage values or memory addresses instead of actual data.
Key Problems in Your Current Code
- No type checking for VARIANTs: You're directly accessing members like
plValorbstrValwithout verifying what type of data the VARIANT holds. For example,Idis an unsigned integer, not a pointer or string. - Missing error checks: You don't verify if
pclsobj->Get()succeeds before reading the VARIANT. If the call fails, you're reading uninitialized memory. - Incorrect handling of special types: Fields like
TimeCreatedare datetime values, which can't be read directly with standard string/int members.
Step-by-Step Fixes
1. Always Check HRESULT for Get() Calls
Before accessing any VARIANT data, confirm that the Get() method succeeded. This avoids reading invalid memory if the property doesn't exist or can't be retrieved.
2. Match VARIANT Type to WMI Property
Each Win32_NTLogEvent property has a defined data type. Here's how to handle the fields you're struggling with:
Example: Correctly Read Id
Id is a uint32 type, which maps to VT_UI4 in VARIANT. Access the ulVal member instead of plVal:
VARIANT vtProp; HRESULT hr = pclsobj->Get(L"Id", 0, &vtProp, 0, 0); if (SUCCEEDED(hr)) { if (vtProp.vt == VT_UI4) { wcout << L"Id : " << vtProp.ulVal << endl; } else { wcout << L"Id: Unexpected data type (VT=" << vtProp.vt << L")" << endl; } VariantClear(&vtProp); } else { wcout << L"Failed to retrieve Id (HRESULT: " << hex << hr << L")" << endl; }
Example: Correctly Read ProviderName
ProviderName is a string (VT_BSTR), but ensure you only access bstrVal if the type matches:
hr = pclsobj->Get(L"ProviderName", 0, &vtProp, 0, 0); if (SUCCEEDED(hr)) { if (vtProp.vt == VT_BSTR) { wcout << L"ProviderName : " << vtProp.bstrVal << endl; } else { wcout << L"ProviderName: Unexpected data type (VT=" << vtProp.vt << L")" << endl; } VariantClear(&vtProp); } else { wcout << L"Failed to retrieve ProviderName (HRESULT: " << hex << hr << L")" << endl; }
Example: Correctly Read Level
Like Id, Level is a uint32 (VT_UI4):
hr = pclsobj->Get(L"Level", 0, &vtProp, 0, 0); if (SUCCEEDED(hr)) { if (vtProp.vt == VT_UI4) { wcout << L"Level : " << vtProp.ulVal << endl; } else { wcout << L"Level: Unexpected data type (VT=" << vtProp.vt << L")" << endl; } VariantClear(&vtProp); } else { wcout << L"Failed to retrieve Level (HRESULT: " << hex << hr << L")" << endl; }
Example: Handle TimeCreated (Datetime Type)
TimeCreated is a datetime type (VT_DATE). Convert it to a readable string using VariantTimeToSystemTime:
hr = pclsobj->Get(L"TimeCreated", 0, &vtProp, 0, 0); if (SUCCEEDED(hr)) { if (vtProp.vt == VT_DATE) { SYSTEMTIME st; if (VariantTimeToSystemTime(vtProp.date, &st)) { wchar_t timeBuffer[100]; swprintf_s(timeBuffer, L"%02d-%02d-%04d %02d:%02d:%02d", st.wDay, st.wMonth, st.wYear, st.wHour, st.wMinute, st.wSecond); wcout << L"TimeCreated : " << timeBuffer << endl; } else { wcout << L"Failed to convert datetime value" << endl; } } else { wcout << L"TimeCreated: Unexpected data type (VT=" << vtProp.vt << L")" << endl; } VariantClear(&vtProp); } else { wcout << L"Failed to retrieve TimeCreated (HRESULT: " << hex << hr << L")" << endl; }
3. Handle Null/Empty Values
Some fields (like Version in your CSV) may be empty. In these cases, the VARIANT's vt will be VT_NULL. Add a check to handle this gracefully:
if (vtProp.vt == VT_NULL) { wcout << L"[Field Name]: (empty)" << endl; }
Full Modified Loop Example
Here's how your data retrieval loop should look with these fixes:
IWbemClassObject *pclsobj = NULL ; ULONG uReturn = 0 ; while (pEnumerator) { HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsobj, &uReturn); if( 0 == uReturn ) { cout << "loop broke" << endl ; break; } // Message (string) VARIANT vtProp; hr = pclsobj->Get(L"Message", 0, &vtProp, 0, 0); if (SUCCEEDED(hr)) { if (vtProp.vt == VT_BSTR) { wcout << L"Message : " << vtProp.bstrVal << endl ; } else if (vtProp.vt == VT_NULL) { wcout << L"Message : (empty)" << endl; } else { wcout << L"Message: Unexpected type" << endl; } VariantClear(&vtProp); } // ComputerName (string) hr = pclsobj->Get(L"ComputerName", 0, &vtProp, 0, 0); if (SUCCEEDED(hr)) { if (vtProp.vt == VT_BSTR) { wcout << L"ComputerName : " << vtProp.bstrVal << endl; } VariantClear(&vtProp); } // Id (uint32) hr = pclsobj->Get(L"Id", 0, &vtProp, 0, 0); if (SUCCEEDED(hr)) { if (vtProp.vt == VT_UI4) { wcout << L"Id : " << vtProp.ulVal << endl ; } else if (vtProp.vt == VT_NULL) { wcout << L"Id : (empty)" << endl; } VariantClear(&vtProp); } // Add other fields using the same pattern... pclsobj->Release(); }
Additional Notes
- For complex fields like
PropertiesorKeywordsDisplayNames(arrays/collections), you'll need to iterate over the VARIANT's array data (usingparraymember andSafeArrayfunctions). - Always call
VariantClear()after using a VARIANT to avoid memory leaks.
内容的提问来源于stack exchange,提问作者D vignesh

