Windows bindings

Rumbu rumbu at rumbu.ro
Sat Feb 13 09:40:24 UTC 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.
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.



More information about the Digitalmars-d mailing list