Windows bindings

Rumbu rumbu at rumbu.ro
Sat Feb 13 00:32:30 UTC 2021


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]

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'.

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

For the Optional attribute I have no idea except that put null as 
default:
- void foo(int* val) becomes void foo(int* val = null)
But this will work only for terminal parameters, if the optional 
parameter is in the middle of the list, there is no way to do 
that.


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


3) On enums

None of the enums are anonymous, every old Windows constant now 
is part of an enum which results in very long names difficult to 
write. Example:

enum FILE_NOTIFY_CHANGE
{
     FILE_NOTIFY_CHANGE_FILE_NAME = 1,
     FILE_NOTIFY_CHANGE_DIR_NAME = 2,
     FILE_NOTIFY_CHANGE_ATTRIBUTES = 4,
     FILE_NOTIFY_CHANGE_SIZE = 8,
     FILE_NOTIFY_CHANGE_LAST_WRITE = 16,
     FILE_NOTIFY_CHANGE_LAST_ACCESS = 32,
     FILE_NOTIFY_CHANGE_CREATION = 64,
     FILE_NOTIFY_CHANGE_SECURITY = 256,
}

A first option will be to drop the enum name and come back to the 
old windows constants.

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.

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.


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.


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.
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
{
....
}

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;

---

Enough food for thought, I will come back with new questions as 
long as the project evolves.


[1] https://github.com/rumbu13/windows-d
[2] https://github.com/microsoft/win32metadata
[3] https://github.com/rumbu13/windows-d/tree/master/out/windows


More information about the Digitalmars-d mailing list