Windows bindings

kinke noone at nowhere.com
Sat Feb 13 02:32:16 UTC 2021


On Saturday, 13 February 2021 at 00:32:30 UTC, Rumbu wrote:
> I recently started a new project for Windows bindings [1] based 
> on metadata generated by Windows Metadata Project [2].
>
> I succesfully managed to implement a winmd reader in D (it 
> works also with any DLL file containing CLI metadata) and 
> generated the first bindings [3]

Great!

> Now, during the process of generation, a lot of questions 
> popped out and I'm asking for community help to sort them out.
>
> 1) On In/Out/Optional attributes
>
> Most of the pointer function parameters are decorated with any 
> combination of these 3 attributes and I don't have any idea how 
> to represent them or find them any use.
>
> For the "in" attribute the direct equivalent will be 'const' 
> but usually this kind of parameters are already decorated with 
> another attribute called 'IsConst'.

I don't think you can translate it to `const` because that can 
affect the mangled name of the function. AFAIK, the MSVC 
annotations like _In_, _Out_ etc. 
(https://docs.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2015/code-quality/annotating-function-parameters-and-return-values?view=vs-2015&redirectedfrom=MSDN) are pure annotations and ignored for mangling purposes.

> For the Out attribute I think that an option will be to 
> derefrence the pointer and put a 'out' parameter qualifier 
> instead. Example:
> - void foo(int* val) becomes void foo(out int val)
> The problem appears when I have prototypes like foo(char*) 
> where the poimnter is in 99% of cases a pointer to a null 
> terminated string

And it affects C++ mangling (pointer => ref).

> 2) On ComOutPtr attribute
> I didn't find any use of this attribute, it usually decorates 
> parameters returning COM interfaces. Example, the ppv parameter 
> below is decorated with this attribute:
>
> HRESULT foo(IUnknown* ppv);

Hmm, I thought these would be something like `_COM_Outptr_ 
IUnknown **ppvObject`. Translating these to `out IUnknown object` 
(IUnknown in D is a interface and mangled as C++ `IUnknown*`) in 
D would be awesome but probably not work because of mangling 
differences (ref/pointer).

> 3) On enums
>
> [...]
> A second option, which seems more interesting, is to drop the 
> member prefix when it coresponds to the enum name.
>
> enum FILE_NOTIFY_CHANGE
> {
>     FILE_NAME = 1,
>     DIR_NAME = 2,
>     ATTRIBUTES = 4,
>     ...
> }
>
> Unfortunately this option is not always available because some 
> enums are named with ENUM or other tags at the end or the enum 
> name is completely different compared with the members within.

If mapping them to a nicely named enum, it should IMO go further 
and map the enum name and values to PascalCase or camelCase.

> 4) On RAIFree attribute
>
> Most of the specific handles are decorated with this attribute 
> stating the function meant to free the handle. Example:
>
> [RAIIFree(CloseDC)]
> struct HDC ...
>
> Sincerely, it's interesting concept (to know what function to 
> use in a potential destructor), but I have no idea how can I 
> take advantage on this.

Me neither; perhaps just leave these annotations as source 
comments in D?

> 5) On DllImport attribute
>
> Each function is decorated with a DllImport attribute stating 
> the dll file where it can be found. If a dynamic binding is 
> intended later, this can be useful. All I've done now was to 
> decorate also in D each function with the same attribute. 
> Example:
>
>
> @DllImport("WININET.dll")
> BOOL InternetTimeToSystemTimeA(const(char)* lpszTime, 
> SYSTEMTIME* pst, uint dwReserved);
>
> Maybe with some traits magic, we can obtain a pointer to the 
> function at runtime.

Yes, could come in handy.

> 6) On GUID attribute
>
> There are no IID or CLSID constants declared in the metadata. 
> Instead,
> - each interface is decorated with a GUID attribute 
> corresponding to it
> - for COM classes, an empty struct is declared and decorated 
> with that attribute.
>
> For interfaces I created the corresponding constant named 
> IID_InterfaceName and for classes CLSID_ClassName.

Good, seems to match core.sys.windows.uuid.

> I also left attached as an attribute the Guid, thinking about 
> the fact that some trait magic can extract an interface 
> constant with a syntax like "uuidof!InterfaceName" (i've seen 
> this in some projects).
>
> So finaly an interface looks translated in D code like this:
>
> const GUID IID_ID3D12RootSignature = {0xC54A6B66, 0x72DF, 
> 0x4EE8, [0x8B, 0xE5, 0xA9, 0x46, 0xA1, 0x42, 0x92, 0x14]};
> @GUID(0xC54A6B66, 0x72DF, 0x4EE8, [0x8B, 0xE5, 0xA9, 0x46, 
> 0xA1, 0x42, 0x92, 0x14]);
> interface ID3D12RootSignature : ID3D12DeviceChild
> {
> ....
> }

The UDA might be superfluous if the naming scheme is consistent - 
something like `alias uuidof(T) = mixin("IID_" ~ T.stringof);` 
could do.

> 7) On strongly typed structs
>
> In the old Windows days, handles were simply typedefs to 
> pointers. Now another idea emerged, For example, a HFONT handle 
> which in the past was simply a void*, now is described like 
> this:
>
> struct HFONT
> {
>    ptrdiff_t value;
> }
>
> This will oblige to HFONT related functions to use only  this 
> struct as parameter and thi struct will always have the size of 
> a pointer.
>
> Sincerely I don't have any idea how to translate this to D, 
> currently I simply put an alias HDC = ptrdiff_t;

Oh, I think I remember some reported issue a while ago. IIRC, the 
COM ABI requires each struct to be returned indirectly (hidden 
`sret` pointer argument), regardless how small it is. So a naked 
pointer and a struct wrapping a naked pointer can indeed be 
treated differently ABI-wise and cause problems. So keep these 
structs.


More information about the Digitalmars-d mailing list