Last active
May 29, 2020 09:37
-
-
Save WebFreak001/7d2a31c2b8e7425c7db9c971d1a18ddc to your computer and use it in GitHub Desktop.
querying WMI in dlang over COM
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://forum.dlang.org/post/[email protected] | |
// | |
// warning: this is not what you would consider good code, you want | |
// to make a proper library for the COM classes and types and make sure | |
// really all resources are freed. I mostly try to do so with all the | |
// scope(exit) expressions here but for example the VARIANT usage is | |
// actually wrong and should use proper types. | |
import core.stdc.config; | |
import core.stdc.stdio; | |
import core.sys.windows.com; | |
import core.sys.windows.oaidl; | |
import core.sys.windows.objidl; | |
import core.sys.windows.windows; | |
import core.sys.windows.wtypes; | |
void main(string[] args) | |
{ | |
// port of https://docs.microsoft.com/en-us/windows/win32/wmisdk/example-creating-a-wmi-application | |
CoInitializeEx(null, COINIT.COINIT_MULTITHREADED) | |
.validateHResult("CoInitializeEx"); | |
scope (exit) | |
CoUninitialize(); | |
CoInitializeSecurity(null, // security descriptor | |
-1, // COM negotiates service | |
null, // Authentication services | |
null, // Reserved | |
RPC_C_AUTHN_LEVEL_DEFAULT, // authentication | |
RPC_C_IMP_LEVEL_IMPERSONATE, // Impersonation | |
null, // Authentication info | |
EOLE_AUTHENTICATION_CAPABILITIES.EOAC_NONE, // Additional capabilities | |
null // Reserved | |
).validateHResult("CoInitializeSecurity"); | |
IWbemLocator loc; | |
CoCreateInstance(&CLSID_WbemLocator, null, CLSCTX_INPROC_SERVER, | |
&IID_IWbemLocator, cast(void**)&loc).validateHResult( | |
"CoCreateInstance WbemLocator"); | |
scope (exit) | |
loc.Release(); | |
IWbemServices svc; | |
loc.ConnectServer("ROOT\\CIMV2", // WMI namespace | |
null, // username | |
null, // password | |
null, // locale | |
0, // security flags | |
null, // authority | |
null, // context object | |
&svc // IWbemServices proxy | |
).validateHResult("ConnectServer"); | |
scope (exit) | |
svc.Release(); | |
// Set the IWbemServices proxy so that impersonation | |
// of the user (client) occurs. | |
// WARNING V wrong D definition (wants IUnknown* instead of IUnknown which is already a reference type, cast overrides this) | |
CoSetProxyBlanket(cast(IUnknown*) svc, // the proxy to set | |
RPC_C_AUTHN_WINNT, // authentication service | |
RPC_C_AUTHZ_NONE, // authorization service | |
NULL, // Server principal name | |
RPC_C_AUTHN_LEVEL_CALL, // authentication level | |
RPC_C_IMP_LEVEL_IMPERSONATE, // impersonation level | |
NULL, // client identity | |
EOLE_AUTHENTICATION_CAPABILITIES.EOAC_NONE // proxy capabilities | |
).validateHResult("CoSetProxyBlanket"); | |
IEnumWbemClassObject enumerator; | |
svc.ExecQuery("WQL", "SELECT * FROM Win32_PnPEntity", | |
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, | |
null, &enumerator).validateHResult("ExecQuery"); | |
scope (exit) | |
enumerator.Release(); | |
IWbemClassObject clsobj; | |
ULONG ureturn; | |
while (true) | |
{ | |
enumerator.Next(WBEM_INFINITE, | |
1, &clsobj, &ureturn); | |
if (!ureturn) | |
break; | |
scope (exit) | |
clsobj.Release(); | |
BSTR name; | |
VARIANT vtProp; | |
printf("\nEntry:\n"); | |
clsobj.BeginEnumeration(0); | |
while (true) | |
{ | |
enum WBEM_S_NO_MORE_DATA = 0x40005; | |
if (clsobj.Next(0, &name, &vtProp, null, | |
null) == WBEM_S_NO_MORE_DATA) | |
break; | |
printf("\t%ls: ", name); | |
SysFreeString(name); | |
// TODO: variant code suck | |
// check https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-variant | |
// and https://docs.microsoft.com/en-us/windows/win32/api/propidlbase/ns-propidlbase-propvariant | |
// to make this better | |
switch (vtProp.vt) | |
{ | |
case VARENUM.VT_EMPTY: | |
printf("<empty>\n"); | |
break; | |
case VARENUM.VT_NULL: | |
printf("null\n"); | |
break; | |
case VARENUM.VT_VOID: | |
printf("void\n"); | |
break; | |
case VARENUM.VT_I1: | |
printf("byte: %d\n", | |
cast(int) vtProp.bVal); | |
break; | |
case VARENUM.VT_UI1: | |
printf("ubyte: %d\n", | |
cast(int) vtProp.bVal); | |
break; | |
case VARENUM.VT_UI2: | |
printf("ushort: %d\n", | |
cast(int) vtProp.iVal); | |
break; | |
case VARENUM.VT_UI4: | |
case VARENUM.VT_UINT: | |
printf("uint: %u\n", | |
cast(uint) vtProp.intVal); | |
break; | |
case VARENUM.VT_I8: | |
printf("long: %ld\n", | |
cast(long) vtProp.llVal); | |
break; | |
case VARENUM.VT_UI8: | |
printf("ulong: %lu\n", | |
cast(ulong) vtProp.llVal); | |
break; | |
case VARENUM.VT_I2: | |
printf("short: %d\n", | |
cast(int) vtProp.iVal); | |
break; | |
case VARENUM.VT_I4: | |
case VARENUM.VT_INT: | |
printf("int: %d\n", vtProp.intVal); | |
break; | |
case VARENUM.VT_R4: | |
printf("float: %f\n", vtProp.fltVal); | |
break; | |
case VARENUM.VT_R8: | |
printf("double: %f\n", vtProp.dblVal); | |
break; | |
case VARENUM.VT_CY: | |
printf("currency: %ld\n", | |
vtProp.cyVal.int64); | |
break; | |
case VARENUM.VT_DATE: | |
printf("date: %lf\n", vtProp.date); | |
break; | |
case VARENUM.VT_BSTR: | |
printf("bstr: %ls\n", vtProp.bstrVal); | |
break; | |
case VARENUM.VT_BOOL: | |
if (vtProp.boolVal) | |
printf("true\n"); | |
else | |
printf("false\n"); | |
break; | |
default: | |
printf("<type %d>\n", vtProp.vt); | |
break; | |
} | |
VariantClear(&vtProp); | |
} | |
} | |
} | |
// type definitions, might as well move them to a library... | |
HRESULT validateHResult(HRESULT result, string what) | |
{ | |
if (!FAILED(result)) | |
return result; | |
else | |
throw new HResultException(result, what); | |
} | |
class HResultException : Exception | |
{ | |
this(HRESULT hresult, string what, | |
string file = __FILE__, size_t line = __LINE__) | |
{ | |
import std.conv : to; | |
code = hresult; | |
if (hresult == S_OK) | |
{ | |
super("Nothing wrong (S_OK)", file, line); | |
return; | |
} | |
string error = "HRESULT Fail for " ~ what ~ ": "; | |
wstring info = windowsMessage; | |
if (info.length) | |
error ~= info.to!string ~ " "; | |
super(error ~ "Code 0x" ~ hresult.to!string(16), | |
file, line); | |
} | |
wstring windowsMessage() | |
{ | |
import std.string : strip; | |
wchar* message; | |
auto len = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | |
| FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, | |
null, code, MAKELANGID(LANG_NEUTRAL, | |
SUBLANG_DEFAULT), | |
cast(wchar*)&message, 0, null); | |
wstring ret = message[0 .. len].strip.idup; | |
LocalFree(message); | |
return ret; | |
} | |
HRESULT code; | |
} | |
// COM interface defintions (see C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\WbemCli.Idl) | |
// [object, restricted, local, uuid(dc12a687-737f-11cf-884d-00aa004b2e24), | |
// pointer_default(unique)] | |
//dfmt off | |
interface IWbemLocator : IUnknown | |
{ | |
HRESULT ConnectServer( | |
const BSTR strNetworkResource, | |
const BSTR strUser, | |
const BSTR strPassword, | |
const BSTR strLocale, | |
c_long lSecurityFlags, | |
const BSTR strAuthority, | |
IWbemContext pCtx, | |
IWbemServices* ppNamespace | |
); | |
} | |
// [object, restricted, local, uuid(44aca674-e8fc-11d0-a07c-00c04fb68820)] | |
interface IWbemContext : IUnknown | |
{ | |
HRESULT Clone(IWbemContext* ppNewCopy); | |
HRESULT GetNames( | |
c_long lFlags, | |
BSTR* pNames | |
); | |
HRESULT BeginEnumeration(c_long lFlags); | |
HRESULT Next( | |
c_long lFlags, | |
BSTR* pstrName, | |
VARIANT* pValue | |
); | |
HRESULT EndEnumeration(); | |
HRESULT SetValue( | |
LPCWSTR wszName, | |
c_long lFlags, | |
VARIANT* pValue | |
); | |
HRESULT GetValue( | |
LPCWSTR wszName, | |
c_long lFlags, | |
VARIANT* pValue | |
); | |
HRESULT DeleteValue( | |
LPCWSTR wszName, | |
c_long lFlags | |
); | |
HRESULT DeleteAll(); | |
}; | |
// [object, restricted, uuid(9556dc99-828c-11cf-a37e-00aa003240c7), | |
// pointer_default(unique)] | |
interface IWbemServices : IUnknown | |
{ | |
// Context. | |
// ======== | |
HRESULT OpenNamespace( | |
const BSTR strNamespace, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemServices* ppWorkingNamespace, | |
IWbemCallResult* ppResult | |
); | |
HRESULT CancelAsyncCall( | |
IWbemObjectSink pSink | |
); | |
HRESULT QueryObjectSink( | |
c_long lFlags, | |
IWbemObjectSink* ppResponseHandler | |
); | |
// Classes and instances. | |
// ====================== | |
HRESULT GetObject( | |
const BSTR strObjectPath, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemClassObject* ppObject, | |
IWbemCallResult* ppCallResult | |
); | |
HRESULT GetObjectAsync( | |
const BSTR strObjectPath, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemObjectSink pResponseHandler | |
); | |
// Class manipulation. | |
// =================== | |
HRESULT PutClass( | |
IWbemClassObject pObject, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemCallResult* ppCallResult | |
); | |
HRESULT PutClassAsync( | |
IWbemClassObject pObject, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemObjectSink pResponseHandler | |
); | |
HRESULT DeleteClass( | |
const BSTR strClass, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemCallResult* ppCallResult | |
); | |
HRESULT DeleteClassAsync( | |
const BSTR strClass, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemObjectSink pResponseHandler | |
); | |
HRESULT CreateClassEnum( | |
const BSTR strSuperclass, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IEnumWbemClassObject* ppEnum | |
); | |
HRESULT CreateClassEnumAsync( | |
const BSTR strSuperclass, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemObjectSink pResponseHandler | |
); | |
// Instances. | |
// ========== | |
HRESULT PutInstance( | |
IWbemClassObject pInst, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemCallResult* ppCallResult | |
); | |
HRESULT PutInstanceAsync( | |
IWbemClassObject pInst, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemObjectSink pResponseHandler | |
); | |
HRESULT DeleteInstance( | |
const BSTR strObjectPath, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemCallResult* ppCallResult | |
); | |
HRESULT DeleteInstanceAsync( | |
const BSTR strObjectPath, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemObjectSink pResponseHandler | |
); | |
HRESULT CreateInstanceEnum( | |
const BSTR strFilter, // allow more things than a class name | |
c_long lFlags, | |
IWbemContext pCtx, | |
IEnumWbemClassObject* ppEnum | |
); | |
HRESULT CreateInstanceEnumAsync( | |
const BSTR strFilter, // allow more things than a class name | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemObjectSink pResponseHandler | |
); | |
// Queries. | |
// ======== | |
HRESULT ExecQuery( | |
const BSTR strQueryLanguage, | |
const BSTR strQuery, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IEnumWbemClassObject* ppEnum | |
); | |
HRESULT ExecQueryAsync( | |
const BSTR strQueryLanguage, | |
const BSTR strQuery, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemObjectSink pResponseHandler | |
); | |
HRESULT ExecNotificationQuery( | |
const BSTR strQueryLanguage, | |
const BSTR strQuery, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IEnumWbemClassObject* ppEnum | |
); | |
HRESULT ExecNotificationQueryAsync( | |
const BSTR strQueryLanguage, | |
const BSTR strQuery, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemObjectSink pResponseHandler | |
); | |
// Methods | |
// ======= | |
HRESULT ExecMethod( | |
const BSTR strObjectPath, | |
const BSTR strMethodName, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemClassObject pInParams, | |
IWbemClassObject* ppOutParams, | |
IWbemCallResult* ppCallResult | |
); | |
HRESULT ExecMethodAsync( | |
const BSTR strObjectPath, | |
const BSTR strMethodName, | |
c_long lFlags, | |
IWbemContext pCtx, | |
IWbemClassObject pInParams, | |
IWbemObjectSink pResponseHandler | |
); | |
} | |
// [object, restricted, uuid(44aca675-e8fc-11d0-a07c-00c04fb68820)] | |
interface IWbemCallResult : IUnknown | |
{ | |
HRESULT GetResultObject( | |
c_long lTimeout, | |
IWbemClassObject* ppResultObject | |
); | |
HRESULT GetResultString( | |
c_long lTimeout, | |
BSTR* pstrResultString | |
); | |
HRESULT GetResultServices( | |
c_long lTimeout, | |
IWbemServices* ppServices | |
); | |
HRESULT GetCallStatus( | |
c_long lTimeout, | |
c_long* plStatus | |
); | |
} | |
// [object, restricted, uuid(7c857801-7381-11cf-884d-00aa004b2e24)] | |
interface IWbemObjectSink : IUnknown | |
{ | |
HRESULT Indicate( | |
c_long lObjectCount, | |
IWbemClassObject* apObjArray | |
); | |
HRESULT SetStatus( | |
c_long lFlags, | |
HRESULT hResult, | |
BSTR strParam, | |
IWbemClassObject pObjParam | |
); | |
} | |
// [local, restricted, object, uuid(dc12a681-737f-11cf-884d-00aa004b2e24)] | |
interface IWbemClassObject : IUnknown | |
{ | |
HRESULT GetQualifierSet( | |
IWbemQualifierSet* ppQualSet | |
); | |
HRESULT Get( | |
LPCWSTR wszName, | |
c_long lFlags, | |
VARIANT* pVal, | |
CIMTYPE* pType, | |
c_long* plFlavor | |
); | |
HRESULT Put( | |
LPCWSTR wszName, | |
c_long lFlags, | |
VARIANT* pVal, | |
CIMTYPE Type | |
); | |
HRESULT Delete( | |
LPCWSTR wszName | |
); | |
HRESULT GetNames( | |
LPCWSTR wszQualifierName, | |
c_long lFlags, | |
VARIANT* pQualifierVal, | |
BSTR* pNames | |
); | |
HRESULT BeginEnumeration(c_long lEnumFlags); | |
HRESULT Next( | |
c_long lFlags, | |
BSTR* strName, | |
VARIANT* pVal, | |
CIMTYPE* pType, | |
c_long* plFlavor | |
); | |
HRESULT EndEnumeration(); | |
HRESULT GetPropertyQualifierSet( | |
LPCWSTR wszProperty, | |
IWbemQualifierSet* ppQualSet | |
); | |
HRESULT Clone( | |
IWbemClassObject* ppCopy | |
); | |
HRESULT GetObjectText( | |
c_long lFlags, | |
BSTR* pstrObjectText | |
); | |
HRESULT SpawnDerivedClass( | |
c_long lFlags, | |
IWbemClassObject* ppNewClass | |
); | |
HRESULT SpawnInstance( | |
c_long lFlags, | |
IWbemClassObject* ppNewInstance | |
); | |
HRESULT CompareTo( | |
c_long lFlags, | |
IWbemClassObject pCompareTo | |
); | |
HRESULT GetPropertyOrigin( | |
LPCWSTR wszName, | |
BSTR* pstrClassName | |
); | |
HRESULT InheritsFrom( | |
LPCWSTR strAncestor | |
); | |
// Method manipulation. | |
// ==================== | |
HRESULT GetMethod( | |
LPCWSTR wszName, | |
c_long lFlags, | |
IWbemClassObject* ppInSignature, | |
IWbemClassObject* ppOutSignature | |
); | |
HRESULT PutMethod( | |
LPCWSTR wszName, | |
c_long lFlags, | |
IWbemClassObject pInSignature, | |
IWbemClassObject pOutSignature | |
); | |
HRESULT DeleteMethod( | |
LPCWSTR wszName | |
); | |
HRESULT BeginMethodEnumeration(c_long lEnumFlags); | |
HRESULT NextMethod( | |
c_long lFlags, | |
BSTR* pstrName, | |
IWbemClassObject* ppInSignature, | |
IWbemClassObject* ppOutSignature | |
); | |
HRESULT EndMethodEnumeration(); | |
HRESULT GetMethodQualifierSet( | |
LPCWSTR wszMethod, | |
IWbemQualifierSet* ppQualSet | |
); | |
HRESULT GetMethodOrigin( | |
LPCWSTR wszMethodName, | |
BSTR* pstrClassName | |
); | |
} | |
// [object, restricted, uuid(027947e1-d731-11ce-a357-000000000001)] | |
interface IEnumWbemClassObject : IUnknown | |
{ | |
HRESULT Reset(); | |
HRESULT Next( | |
c_long lTimeout, | |
ULONG uCount, | |
IWbemClassObject* apObjects, | |
ULONG* puReturned | |
); | |
HRESULT NextAsync( | |
ULONG uCount, | |
IWbemObjectSink pSink | |
); | |
HRESULT Clone( | |
IEnumWbemClassObject* ppEnum | |
); | |
HRESULT Skip( | |
c_long lTimeout, | |
ULONG nCount | |
); | |
} | |
// [object, restricted, local, uuid(dc12a680-737f-11cf-884d-00aa004b2e24)] | |
interface IWbemQualifierSet : IUnknown | |
{ | |
HRESULT Get( | |
LPCWSTR wszName, | |
c_long lFlags, | |
VARIANT* pVal, | |
c_long* plFlavor | |
); | |
HRESULT Put( | |
LPCWSTR wszName, | |
VARIANT* pVal, | |
c_long lFlavor | |
); | |
HRESULT Delete( | |
LPCWSTR wszName | |
); | |
HRESULT GetNames( | |
c_long lFlags, | |
BSTR* pNames | |
); | |
HRESULT BeginEnumeration( | |
c_long lFlags | |
); | |
HRESULT Next( | |
c_long lFlags, | |
BSTR* pstrName, | |
VARIANT* pVal, | |
c_long* plFlavor | |
); | |
HRESULT EndEnumeration(); | |
} | |
//dfmt on | |
enum CIMTYPE : c_long | |
{ | |
CIM_ILLEGAL = 0xfff, | |
CIM_EMPTY = 0, | |
CIM_SINT8 = 16, | |
CIM_UINT8 = 17, | |
CIM_SINT16 = 2, | |
CIM_UINT16 = 18, | |
CIM_SINT32 = 3, | |
CIM_UINT32 = 19, | |
CIM_SINT64 = 20, | |
CIM_UINT64 = 21, | |
CIM_REAL32 = 4, | |
CIM_REAL64 = 5, | |
CIM_BOOLEAN = 11, | |
CIM_STRING = 8, | |
CIM_DATETIME = 101, | |
CIM_REFERENCE = 102, | |
CIM_CHAR16 = 103, | |
CIM_OBJECT = 13, | |
CIM_FLAG_ARRAY = 0x2000 | |
} | |
enum WBEM_FLAG_RETURN_IMMEDIATELY = 0x10; | |
enum WBEM_FLAG_RETURN_WBEM_COMPLETE = 0; | |
enum WBEM_FLAG_BIDIRECTIONAL = 0; | |
enum WBEM_FLAG_FORWARD_ONLY = 0x20; | |
enum WBEM_FLAG_NO_ERROR_OBJECT = 0x40; | |
enum WBEM_FLAG_RETURN_ERROR_OBJECT = 0; | |
enum WBEM_FLAG_SEND_STATUS = 0x80; | |
enum WBEM_FLAG_DONT_SEND_STATUS = 0; | |
enum WBEM_FLAG_ENSURE_LOCATABLE = 0x100; | |
enum WBEM_FLAG_DIRECT_READ = 0x200; | |
enum WBEM_FLAG_SEND_ONLY_SELECTED = 0; | |
// backward-compatibility | |
enum WBEM_RETURN_WHEN_COMPLETE = 0; | |
enum WBEM_RETURN_IMMEDIATELY = 0x10; | |
// these bits are reserved!! | |
enum WBEM_MASK_RESERVED_FLAGS = 0x1F000; | |
enum WBEM_FLAG_USE_AMENDED_QUALIFIERS = 0x20000; | |
// If used, the context object must have one or more of the following | |
// BOOL "INCLUDE_OWNER" | |
// BOOL "INCLUDE_DACL" | |
// BOOL "INCLUDE_SACL" | |
// BOOL "INCLUDE_GROUP" | |
enum WBEM_FLAG_STRONG_VALIDATION = 0x100000; | |
enum WBEM_NO_WAIT = 0; | |
enum WBEM_INFINITE = 0xFFFFFFFF; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment