digitalmars.D - Windows bindings
- Rumbu (120/120) Feb 12 2021 I recently started a new project for Windows bindings [1] based
- Steven Schveighoffer (9/15) Feb 12 2021 This is amazing! I hope it works out well, I don't usually program with
- Imperatorn (2/5) Feb 12 2021 Thanks for doing this! 🥇
- Vladimir Panteleev (8/10) Feb 12 2021 Very cool!
- Max Haughton (4/15) Feb 12 2021 I vote for yes for both. If we make them D-ish then we could hit
- kinke (27/122) Feb 12 2021 I don't think you can translate it to `const` because that can
- Jonathan Marler (117/233) Feb 13 2021 Very cool I've been working with this project as well. I decided
- Rumbu (61/108) Feb 13 2021 Very nice, but I set myself as a challenge to use D only and make
- Steven Schveighoffer (16/45) Feb 13 2021 Don't do this, because it will close on copying too.
- Max Samukha (6/9) Feb 13 2021 You can simply identify and drop the common leading part of the
- Imperatorn (7/10) Feb 16 2021 @everyone:
- Elronnd (12/17) Feb 16 2021 struct RaiiFreer(T, alias f) {
- Elronnd (12/18) Feb 16 2021 On Saturday, 13 February 2021 at 16:01:26 UTC, Steven
- Steven Schveighoffer (16/36) Feb 17 2021 Yes, this is a good solution -- it just alters the semantics of the
- Rumbu (63/64) Feb 19 2021 What do you think?
- Steven Schveighoffer (3/8) Feb 19 2021 Looks pretty good :)
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 2021
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 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]. [...]Thanks for doing this! 🥇
Feb 12 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].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 2021
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 vote for yes for both. If we make them D-ish then we could hit subtle ABI problems in the future.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 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& 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 stringAnd 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 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].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/212For 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 2021
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-2One 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); } }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#L28885) 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/kernel32IID_* 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 2021
On 2/13/21 4:40 AM, Rumbu wrote:On Saturday, 13 February 2021 at 08:25:21 UTC, Jonathan Marler wrote: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. -SteveOne 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); Â } }
Feb 13 2021
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 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]. [...]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 2021
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 2021
On Tuesday, 16 February 2021 at 21:35:32 UTC, Elronnd wrote:On Saturday, 13 February 2021 at 00:32:30 UTC, Rumbu wrote:On Saturday, 13 February 2021 at 16:01:26 UTC, Steven Schveighoffer wrote:4) On RAIFree attributestruct RaiiFreer(T, alias f) ...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 2021
On 2/16/21 4:48 PM, Elronnd wrote:On Tuesday, 16 February 2021 at 21:35:32 UTC, Elronnd wrote:Yes, this is a good solution -- it just alters the semantics of the function return.On Saturday, 13 February 2021 at 00:32:30 UTC, Rumbu wrote:On Saturday, 13 February 2021 at 16:01:26 UTC, Steven Schveighoffer wrote:4) On RAIFree attributestruct RaiiFreer(T, alias f) ...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.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 2021
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 2021
On 2/19/21 7:16 AM, Rumbu wrote:On Wednesday, 17 February 2021 at 14:49:58 UTC, Steven Schveighoffer wrote:Looks pretty good :) -SteveOn 2/16/21 4:48 PM, Elronnd wrote:What do you think?
Feb 19 2021