Jump to page: 1 2
Thread overview
February 13
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
February 12
On 2/12/21 7:32 PM, 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]

This is amazing! I hope it works out well, I don't usually program with Windows.

Are there docs in the metadata that can be prepended to the bindings so DDoc works?

I wish I could answer your other questions. A good source of information is to maybe look at the existing windows bindings for a questionable function, and see what they do.

-Steve
February 13
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].
>
> [...]

Thanks for doing this! 🥇
February 13
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].

Very cool!

Could this project be viable to replace the manually-maintained bindings we have in Druntime (core.sys.windows)? They would have to retain compatibility (and therefore be a strict upgrade).

If so, then your questions have easy answers - just do what the existing bindings do (i.e. no D-fication, do the same things as the old official C headers) :)

February 13
On Saturday, 13 February 2021 at 02:08:33 UTC, Vladimir Panteleev wrote:
> 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].
>
> Very cool!
>
> Could this project be viable to replace the manually-maintained bindings we have in Druntime (core.sys.windows)? They would have to retain compatibility (and therefore be a strict upgrade).
>
> If so, then your questions have easy answers - just do what the existing bindings do (i.e. no D-fication, do the same things as the old official C headers) :)

I vote for yes for both. If we make them D-ish then we could hit subtle ABI problems in the future.
February 13
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.
February 13
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].
>

Very cool I've been working with this project as well.  I decided to create a project that converts the winmd file to JSON (see https://github.com/marlersoft/win32json).  This makes it easy to search through the data with grep and allows it to be more easily consumed by other tools.

> 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 job on that, this looks like it was a good chunk of work.  Out of curiosity, did you write this from the spec and/or did you reference other implementations?

>
> 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've found that [In] doesn't necessarily mean "const".  There seem to be many in parameters that are marked "[In]" but are not const. That being said, not all parameters that are suppsed to be const are marked correctly, but I believe this is because win32metadata is still pretty new and working out issues like that.  For example, the buffer parameter for WriteFile/WriteFileEx was not marked as "Const", so I submited a PR that fixed it here: https://github.com/microsoft/win32metadata/pull/212

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

For pointer types I think this really means "nullable".  One way you could do it is if a pointer parameter is not marked as "Optional", then you could make it a "ref" type rather than a pointer, but, that might have other unintended consequences.

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

I believe this is marking parameters that are returning COM objects.  Maybe the reason for this is that some functions may return COM objects, but it may return multiple kinds of COM objects, so it can't declare that it's a COM object through it's type?

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

For the bindings I've generated I went with the second option, but I also declared the full constants as well.  So your example becomes:

enum FILE_NOTIFY_CHANGE
{
    FILE_NAME = 1,
    DIR_NAME = 2,
    ATTRIBUTES = 4,
    ...
}
alias FILE_NOTIFY_CHANGE_FILE_NAME = FILE_NOTIFY_CHANGE.FILE_NAME;
alias FILE_NOTIFY_CHANGE_DIR_NAME = FILE_NOTIFY_CHANGE.DIR_NAME;
alias FILE_NOTIFY_CHANGE_ATTRIBUTES = FILE_NOTIFY_CHANGE.ATTRIBUTES;
...

Not sure what to do yet about the weird enums whose values don't match the name of the enum, maybe they need to be addressed on a case by case basis?

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

One thing you could do is define a common method that calls it:

struct HDC
{
    auto free()
    {
        return CloseDC(this.value);
    }
}

struct HANDLE
{
    auto free()
    {
        return CloseHandle(this.value);
    }
}

It's not really a big feature, but it means you can free any type with this attribute the same way.  Could make them easier to use in template code.


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

There is an interesting feature you might be able to implement something with. A while back I worked on my own set of Windows bindings and I developed a pattern where I categorized things by whether or not they required linking to a library.  Here's the modules for kernel32: https://github.com/dragon-lang/mar/tree/master/src/mar/windows/kernel32

The "link.d" module starts with `pragma(lib, "kernel32.lib");`.  I put all the function definitions in link.d so when it gets imported, kernel32.lib automatically gets added to the library link list.  Then I put all the type/const definitions in "nolink.d" because they can be used without having to link to kernel32.lib. This means the libraries you use and don't use get added and removed purely based on what your program is calling.

That being said, win32metadata is not split by library but instead by category, so I'm not sure how this would work since you're generating modules based on the category as well.  Maybe you could sort all the functions into their own empty templates based on which library they are in and put the pragmas in there; something like this?

template kernel32()
{
    pragma(lib, "kernel32.lib");
    void WriteFile(...);
    void WriteFileEx(...);
}
alias WriteFile = kernel32!().WriteFile;
alias WriteFileEx = kernel32!().WriteFileEx;

template user32()
{
    pragma(lib, "user32.lib");
    void SomethingElse(...);
    void AnotherThing(...);
    void AlsoThis(...);
}
alias SomethingElse = user32!().SomethingElse;
alias AnotherThing = user32!().AnotherThing;
alias AlsoThis = user32!().AlsoThis;

This might be overkill, but food for thought.

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


IID_* and CLSID_* are actually supposed to be pointers to GUID's rather than GUIDS themselves.  So you might want to do something like:

immutable GUID IID_ID3D12RootSignature_Value = {0xC54A6B66, 0x72DF, 0x4EE8, [0x8B, 0xE5, 0xA9, 0x46, 0xA1, 0x42, 0x92, 0x14]};
immutable GUID* IID_ID3D12RootSignature = &IID_ID3D12RootSignature_Value;

Or maybe create some sort of template that create storage for a guid and returns a pointer to it.


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

Yeah this one is interesting.  Because some functions like CreateWindow and types like WNDCLASS take something like HMODULE but are typically assigned a value of type HINSTANCE.  So in some cases, each type having their own struct wrapper could be helpful, and in other cases it could just mean alot of boilerplate to cast between types.  I'll have to play with this more myself to see what's the way to go.



February 13
On Saturday, 13 February 2021 at 08:25:21 UTC, Jonathan Marler wrote:
>
> Very cool I've been working with this project as well.  I decided to create a project that converts the winmd file to JSON (see https://github.com/marlersoft/win32json).  This makes it easy to search through the data with grep and allows it to be more easily consumed by other tools.

Very nice, but I set myself as a challenge to use D only and make it cross platform.
It would be too easy to parse assemblies in C# :)

> Great job on that, this looks like it was a good chunk of work.
>  Out of curiosity, did you write this from the spec and/or did you reference other implementations?

There is a story behind it :) Initially I started the project by using Unmanaged Metadata API from Microsoft [1] but the COM classes there do nothing else than enumerating objects in the metadata file, reading and interpreting signatures remains your job. Therefore, I considered the Unmanaged API useless and started from scratch.

The main source of inspiration was cppwin32 project [2], but unfortunately it was too complicated to directly translate it to D because the project is abusing of C++ templates and not in a good way, I think. Hence my posts there:

https://forum.dlang.org/thread/dgaqdrstbmukrixpjedj@forum.dlang.org

Of course, I read the ECMA 335 standard [3] to better understand the guts of CLI metadata format, but I found also helpful some old codeproject articles [4], [5], [6]. These articles brought more light than the standard itself.

Finally, just for the sake of completness, i pushed forward and my metadata.d file is now more complex that the C++ counterpart, because I implemented in it all ECMA 335 types and signatures, even the deprecated ones. I even found some bugs in the C++ implementation :)

Of course this module can be used now to read any metadata file and is cross-platform (tested it with mscorlib.dll and it works).

[1] https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/metadata/
[2] https://github.com/microsoft/cppwin32/tree/main/cppwin32/winmd
[3] https://www.ecma-international.org/publications-and-standards/standards/ecma-335/
[4] https://www.codeproject.com/Articles/12585/The-NET-File-Format
[5] https://www.codeproject.com/Articles/42649/NET-File-Format-Signatures-Under-the-Hood-Part-1-o
[6] https://www.codeproject.com/Articles/42655/NET-file-format-Signatures-under-the-hood-Part-2

>
> One thing you could do is define a common method that calls it:
>
> struct HDC
> {
>     auto free()
>     {
>         return CloseDC(this.value);
>     }
> }
>
> struct HANDLE
> {
>     auto free()
>     {
>         return CloseHandle(this.value);
>     }
> }

Excellent idea! I can take it further, why not a destructor?

struct HDC
{
  ~this()
  {
    CloseDC(this.value);
  }
}

>> 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.
>
> There is an interesting feature you might be able to implement something with. A while back I worked on my own set of Windows bindings and I developed a pattern where I categorized things by whether or not they required linking to a library.  Here's the modules for kernel32: https://github.com/dragon-lang/mar/tree/master/src/mar/windows/kernel32
>

This is also a good idea but I am thinking of dynamic bindings, meaning that you don't need the updated lib to be included in you project. Basically that means that you must declare function pointers instead of functions and load them from dll:

Example:
//original prototype: BOOL CloseHandle(HANDLE hObject)
alias CloseHandleFunc = BOOL function(HANDLE);
auto mod = LoadLibrary("kernel32.dll");
auto CloseHandle = cast(CloseHandleFunc)GetProcAddress(mod, "CloseHandle");

There is a nice implementation of this concept here:

https://github.com/JesseKPhillips/Juno-Windows-Class-Library/blob/bbbd9ced118365a9e05a4b73d8111ea775ff1ce9/source/juno/base/native.d#L2888


>
> IID_* and CLSID_* are actually supposed to be pointers to GUID's rather than GUIDS themselves.  So you might want to do something like:

I doubt it:

https://github.com/dlang/druntime/blob/master/src/core/sys/windows/uuid.d

Anyway, I am completely rethinking it. I am working on a GUIDOF template.

February 13
On Saturday, 13 February 2021 at 08:25:21 UTC, Jonathan Marler wrote:

>
> Not sure what to do yet about the weird enums whose values don't match the name of the enum, maybe they need to be addressed on a case by case basis?

You can simply identify and drop the common leading part of the names of the values. I had a script that did that when I was converting Android's headers. A couple of cases did require manual adjustment, though.
February 13
On 2/13/21 4:40 AM, Rumbu wrote:
> On Saturday, 13 February 2021 at 08:25:21 UTC, Jonathan Marler wrote:

>>
>> One thing you could do is define a common method that calls it:
>>
>> struct HDC
>> {
>>     auto free()
>>     {
>>         return CloseDC(this.value);
>>     }
>> }
>>
>> struct HANDLE
>> {
>>     auto free()
>>     {
>>         return CloseHandle(this.value);
>>     }
>> }
> 
> Excellent idea! I can take it further, why not a destructor?
> 
> struct HDC
> {
>    ~this()
>    {
>      CloseDC(this.value);
>    }
> }

Don't do this, because it will close on copying too.

In order to *properly* implement this, it would need reference counting.

A nice feature would be to implement a common method (like free), and then provide a reference-counting wrapper that would then call the appropriate thing when refcount is 0.

Another possibility is to define a freeFunction template that can be applied as an attribute:

template freeFunction(alias f)
{
    alias fn = f;
}

@freeFunction!CloseDC struct HDC ...

And then an appropriate free function can be called when the UDA is detected.

-Steve
« First   ‹ Prev
1 2