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