www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Windows bindings

reply Rumbu <rumbu rumbu.ro> writes:
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
Feb 12
next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
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
Feb 12
prev sibling next sibling parent Imperatorn <johan_forsberg_86 hotmail.com> writes:
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! 🥇
Feb 12
prev sibling next sibling parent reply Vladimir Panteleev <thecybershadow.lists gmail.com> writes:
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) :)
Feb 12
parent Max Haughton <maxhaton gmail.com> writes:
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.
Feb 12
prev sibling next sibling parent kinke <noone nowhere.com> writes:
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& edirectedfrom=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.
Feb 12
prev sibling next sibling parent reply Jonathan Marler <johnnymarler gmail.com> writes:
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.
Feb 13
next sibling parent reply Rumbu <rumbu rumbu.ro> writes:
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.
 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.
Feb 13
parent Steven Schveighoffer <schveiguy gmail.com> writes:
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
Feb 13
prev sibling parent Max Samukha <maxsamukha gmail.com> writes:
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.
Feb 13
prev sibling next sibling parent Imperatorn <johan_forsberg_86 hotmail.com> writes:
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].

 [...]
everyone: "This is great to see and thanks for letting us know. I've added this project to the list of projections in our README to help with discovery and will do the same with other projects as they come online." https://github.com/microsoft/win32metadata
Feb 16
prev sibling parent reply Elronnd <elronnd elronnd.net> writes:
On Saturday, 13 February 2021 at 00:32:30 UTC, Rumbu wrote:
 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 ...
struct RaiiFreer(T, alias f) { T x; alias x this; ~this() { f(x); } } struct realHDC { original contents... } alias HDC = RaiiFreer!(realHDC, CloseDC); And make the win32 functions take a realHDC, but users declare HDC to get raii semantics.
Feb 16
parent reply Elronnd <elronnd elronnd.net> writes:
On Tuesday, 16 February 2021 at 21:35:32 UTC, Elronnd wrote:
 On Saturday, 13 February 2021 at 00:32:30 UTC, Rumbu wrote:
 4) On RAIFree attribute
struct RaiiFreer(T, alias f) ...
On Saturday, 13 February 2021 at 16:01:26 UTC, Steven Schveighoffer wrote:
 Don't do this, because it will close on copying too.

 In order to *properly* implement this, it would need reference 
 counting.
It's easy to get double frees with that solution--right. Can disable copy constructor, or make it a class. I would go for the former, as it's more flexible: if you need ref semantics, just make a pointer or use phobos refcount. I disagree that you _need_ refcounting to get it right, though. Providing a destructor makes it a more native interface. Manually destroying is something you would have had to do anyway, but now you can destroy the object the same as you would any other.
Feb 16
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/16/21 4:48 PM, Elronnd wrote:
 On Tuesday, 16 February 2021 at 21:35:32 UTC, Elronnd wrote:
 On Saturday, 13 February 2021 at 00:32:30 UTC, Rumbu wrote:
 4) On RAIFree attribute
struct RaiiFreer(T, alias f) ...
On Saturday, 13 February 2021 at 16:01:26 UTC, Steven Schveighoffer wrote:
 Don't do this, because it will close on copying too.

 In order to *properly* implement this, it would need reference counting.
It's easy to get double frees with that solution--right.  Can disable copy constructor, or make it a class.  I would go for the former, as it's more flexible: if you need ref semantics, just make a pointer or use phobos refcount.
Yes, this is a good solution -- it just alters the semantics of the function return.
 
 I disagree that you _need_ refcounting to get it right, though. 
 Providing a destructor makes it a more native interface. Manually 
 destroying is something you would have had to do anyway, but now you can 
 destroy the object the same as you would any other.
It's fine as long as it isn't the true return type. As long as you want the same *semantics* as the original type, it needs to be copyable. I'd say, leave the type itself as the return value, add an attribute that identifies how to free it, and then provide a wrapper that does what you want based on introspecting the attribute. e.g.: freeFunction!CloseDC struct HDC { ... normal contents } HDC someWindowsFunc(...); auto autoFree(T)(T val) if (hasUDA!(freeFunction, T)) { return RAIIWrapper!(extractFreeFunction!T)(val); } auto v = someWindowsFunc(...).autoFree; -Steve
Feb 17
parent reply Rumbu <rumbu rumbu.ro> writes:
On Wednesday, 17 February 2021 at 14:49:58 UTC, Steven 
Schveighoffer wrote:
 On 2/16/21 4:48 PM, Elronnd wrote:
What do you think? //Usage: RAIIFree!CloseHandle struct SomeHandle { ptrdiff_t value; } SomeHandle CreateSomeHandle(int x, int y); void DoSomethingWithSomeHandle(Handle h); void CloseSomeHandle(SomeHandle h); auto h = autoFree(CreateSomeHandle(42, 666)); DoSomethingWithSomeHandle(h); //Implementation struct RAIIFree(alias H) { private alias Handler = H; } auto autoFree(T)(T handle) { return RAIIWrapper!(T, FreeFunctionOf!T)(handle); } struct RAIIWrapper(Handle, alias CloseHandler) { Handle s; alias s this; disable this(); disable this(this); this(Handle value) { this.s = value; } ~this() { CloseHandler(s); } } private template FreeFunctionOf(T, A...) { static if (A.length == 0) { alias attrs = __traits(getAttributes, T); static assert(attrs.length > 0, T.stringof ~ " doesn't have any attribute attached to it"); alias FreeFunctionOf = FreeFunctionOf!(T, attrs); } else static if (A.length == 1) { static assert(isFreeHandler!(T, A[0]), T.stringof ~ " doesn't have a correct RAIIFree attribute attached to it"); alias FreeFunctionOf = A[0].Handler; } else static if (isFreeHandler!(T, A[0])) alias FreeFunctionOf = A[0].Handler; else alias FreeFunctionOf = FreeFunctionOf!(T, A[1 .. $]); } private template isFreeHandler(T, alias A) { enum isFreeHandler = is(typeof(A.Handler)) && is(typeof(A.Handler(T.init))); }
Feb 19
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 2/19/21 7:16 AM, Rumbu wrote:
 On Wednesday, 17 February 2021 at 14:49:58 UTC, Steven Schveighoffer wrote:
 On 2/16/21 4:48 PM, Elronnd wrote:
What do you think?
Looks pretty good :) -Steve
Feb 19