Thread overview
[Windows]Need an example: How to read and list names of USB devices via Windows API without using Utilities
May 27, 2020
BoQsc
May 27, 2020
Mihail Lorenko
May 29, 2020
WebFreak001
May 29, 2020
WebFreak001
May 27, 2020
I always wanted to know if there is any proven example on how to interface with USB devices by using Windows operating system. Any explanations, snippets in relation to topic would help.

What I expect:
Being able to detect if a new USB device is connected.
Being able to read USB device name.
Being able to read USB device partition/s name/s.
Being able to list all USB devices.

What is required and are there any critical problems with achieving any of this?
May 27, 2020
On Wednesday, 27 May 2020 at 14:16:56 UTC, BoQsc wrote:
> I always wanted to know if there is any proven example on how to interface with USB devices by using Windows operating system. Any explanations, snippets in relation to topic would help.
>
> What I expect:
> Being able to detect if a new USB device is connected.
> Being able to read USB device name.
> Being able to read USB device partition/s name/s.
> Being able to list all USB devices.
>
> What is required and are there any critical problems with achieving any of this?

Hello!
I advise you to see the following until others have answered you:
[1]: https://code.dlang.org/packages/libusb-d
[2]: https://code.dlang.org/packages/serialport

I can not say that these links satisfy your needs.
May 29, 2020
On Wednesday, 27 May 2020 at 14:16:56 UTC, BoQsc wrote:
> I always wanted to know if there is any proven example on how to interface with USB devices by using Windows operating system. Any explanations, snippets in relation to topic would help.
>
> What I expect:
> Being able to detect if a new USB device is connected.
> Being able to read USB device name.
> Being able to read USB device partition/s name/s.
> Being able to list all USB devices.
>
> What is required and are there any critical problems with achieving any of this?

use WMI like in C++: https://docs.microsoft.com/en-us/windows/win32/wmisdk/creating-a-wmi-application-using-c-

If you make a port of the IDL files, check out https://github.com/WebFreak001/dwinrt/tree/master/generator for a converter converting IDL files to D definitions. However that project is pretty much only working for directx and WinRT, so you will have to adjust things if you want to use it.

If you manually port over the IDL definitions, remember that D interfaces & classes are reference types, so if the IDL file uses `IWbemServices** ppNamespace` for example, you will just change it to `IWbemServices* ppNamespace` (pp meaning it's an output parameter, for interfaces with pp prefix you should only have one *, for interfaces with p prefix you should have no *)

Example:

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();

		VARIANT vtProp;
		clsobj.Get("Name", 0, &vtProp, null, null);
		// TODO: do what you want with the listed devices here
		printf("Name: %ls\n", vtProp.bstrVal);
		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;

May 29, 2020
On Friday, 29 May 2020 at 09:04:30 UTC, WebFreak001 wrote:
> [...]

I realized it may be useful to have a much more complete example, so replace the while (true) loop in my first code with this to get much more information dumped:


	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);
			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);
		}

		clsobj.Get("Name", 0, &vtProp, null, null);
		if (vtProp.vt == VARENUM.VT_BSTR)
			printf("\tName: %ls\n", vtProp.bstrVal);
	}