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