Thread overview
How to call Windows function pointers?
Aug 16, 2008
Benji Smith
Aug 16, 2008
downs
Aug 16, 2008
downs
Aug 16, 2008
Benji Smith
Aug 17, 2008
torhu
August 16, 2008
I'm using tango.sys.SharedLib to dynamically load a DLL and get a function pointer to a win32 API function. And that's working nicely. But the function pointer is returned as a void*, and to call it, I think I need to cast it to a function pointer. But the function needs to be called with extern(Windows) calling conventions. At first, I tried this:


  alias uint BOOL;
  alias uint DWORD;
  alias ulong DWORDLONG;

  struct MEMORYSTATUSEX {
    DWORD dwLength;
    DWORD dwMemoryLoad;
    DWORDLONG ullTotalPhys;
    DWORDLONG ullAvailPhys;
    DWORDLONG ullTotalPageFile;
    DWORDLONG ullAvailPageFile;
    DWORDLONG ullTotalVirtual;
    DWORDLONG ullAvailVirtual;
    DWORDLONG ullAvailExtendedVirtual;
  }

  public void callWin32Function() {
    auto lib = SharedLib.load(`c:\windows\system32\kernel32.dll`);
    void* sym = lib.getSymbol("GlobalMemoryStatusEx");
    auto fn = cast(extern(Windows) BOOL function(MEMORYSTATUSEX)) sym;

    MEMORYSTATUSEX memStatus;
    fn(memStatus);
  }


But the compiler rejects that cast because of the "extern(Windows)" bit.

Without the cast, it compiles, but throws an Access Violation when I call the function pointer, obviously because it's using the wrong calling convention.

I hate to suggest that the calling convention should be part of the type system (because the type system is already burdened with too much, especially in D2, with const semantics and now thread-sharing semantics). But clearly two function pointers with different calling conventions are different types of functions, and it seems like the type system should take responsibility for ensuring the correct calling convention.

I've also tried this, thinking that declaring "fn" as a member with the "extern(Windows)" calling convention would solve the problem:


  alias uint BOOL;
  alias uint DWORD;
  alias ulong DWORDLONG;

  struct MEMORYSTATUSEX {
    DWORD dwLength;
    DWORD dwMemoryLoad;
    DWORDLONG ullTotalPhys;
    DWORDLONG ullAvailPhys;
    DWORDLONG ullTotalPageFile;
    DWORDLONG ullAvailPageFile;
    DWORDLONG ullTotalVirtual;
    DWORDLONG ullAvailVirtual;
    DWORDLONG ullAvailExtendedVirtual;
  }

  extern(Windows) BOOL function(MEMORYSTATUSEX) fn;

  public void callWin32Function() {
    auto lib = SharedLib.load(`c:\windows\system32\kernel32.dll`);
    void* sym = lib.getSymbol("GlobalMemoryStatusEx");
    fn = cast(BOOL function(MEMORYSTATUSEX)) sym;

    MEMORYSTATUSEX memStatus;
    fn(memStatus);
  }


But the the compiler still doesn't like that cast. It gives this error:

cannot implicitly convert expression (cast(uint function(MEMORYSTATUSEX))exFunctionPtr) of type uint function(MEMORYSTATUSEX) to uintWindows  function(MEMORYSTATUSEX)

Anyone have any suggestions?

Thanks!

--benji
August 16, 2008
Benji Smith wrote:
> I'm using tango.sys.SharedLib to dynamically load a DLL and get a function pointer to a win32 API function. And that's working nicely. But the function pointer is returned as a void*, and to call it, I think I need to cast it to a function pointer. But the function needs to be called with extern(Windows) calling conventions. At first, I tried this:
> 
> 
>   alias uint BOOL;
>   alias uint DWORD;
>   alias ulong DWORDLONG;
> 
>   struct MEMORYSTATUSEX {
>     DWORD dwLength;
>     DWORD dwMemoryLoad;
>     DWORDLONG ullTotalPhys;
>     DWORDLONG ullAvailPhys;
>     DWORDLONG ullTotalPageFile;
>     DWORDLONG ullAvailPageFile;
>     DWORDLONG ullTotalVirtual;
>     DWORDLONG ullAvailVirtual;
>     DWORDLONG ullAvailExtendedVirtual;
>   }
> 
>   public void callWin32Function() {
>     auto lib = SharedLib.load(`c:\windows\system32\kernel32.dll`);
>     void* sym = lib.getSymbol("GlobalMemoryStatusEx");
>     auto fn = cast(extern(Windows) BOOL function(MEMORYSTATUSEX)) sym;
> 
>     MEMORYSTATUSEX memStatus;
>     fn(memStatus);
>   }
> 
> 
> But the compiler rejects that cast because of the "extern(Windows)" bit.
> 
> Without the cast, it compiles, but throws an Access Violation when I call the function pointer, obviously because it's using the wrong calling convention.
> 
> I hate to suggest that the calling convention should be part of the type system (because the type system is already burdened with too much, especially in D2, with const semantics and now thread-sharing semantics). But clearly two function pointers with different calling conventions are different types of functions, and it seems like the type system should take responsibility for ensuring the correct calling convention.
> 
> I've also tried this, thinking that declaring "fn" as a member with the "extern(Windows)" calling convention would solve the problem:
> 
> 
>   alias uint BOOL;
>   alias uint DWORD;
>   alias ulong DWORDLONG;
> 
>   struct MEMORYSTATUSEX {
>     DWORD dwLength;
>     DWORD dwMemoryLoad;
>     DWORDLONG ullTotalPhys;
>     DWORDLONG ullAvailPhys;
>     DWORDLONG ullTotalPageFile;
>     DWORDLONG ullAvailPageFile;
>     DWORDLONG ullTotalVirtual;
>     DWORDLONG ullAvailVirtual;
>     DWORDLONG ullAvailExtendedVirtual;
>   }
> 
>   extern(Windows) BOOL function(MEMORYSTATUSEX) fn;
> 
>   public void callWin32Function() {
>     auto lib = SharedLib.load(`c:\windows\system32\kernel32.dll`);
>     void* sym = lib.getSymbol("GlobalMemoryStatusEx");
>     fn = cast(BOOL function(MEMORYSTATUSEX)) sym;
> 
>     MEMORYSTATUSEX memStatus;
>     fn(memStatus);
>   }
> 
> 
> But the the compiler still doesn't like that cast. It gives this error:
> 
> cannot implicitly convert expression (cast(uint
> function(MEMORYSTATUSEX))exFunctionPtr) of type uint
> function(MEMORYSTATUSEX) to uintWindows  function(MEMORYSTATUSEX)
> 
> Anyone have any suggestions?
> 
> Thanks!
> 
> --benji

Try this (untested):

template ExtWinType(T) { extern(Windows) alias T ExtWinType; }

void callWin32Fn() { auto lib ...; auto fn = cast(ExtWinType!(BOOL function(MEMORYSTATUSEX))) lib.getSymbol("..."); fn(foo); }
August 16, 2008
downs wrote:
> Benji Smith wrote:
>> I'm using tango.sys.SharedLib to dynamically load a DLL and get a function pointer to a win32 API function. And that's working nicely. But the function pointer is returned as a void*, and to call it, I think I need to cast it to a function pointer. But the function needs to be called with extern(Windows) calling conventions. At first, I tried this:
>>
>>
>>   alias uint BOOL;
>>   alias uint DWORD;
>>   alias ulong DWORDLONG;
>>
>>   struct MEMORYSTATUSEX {
>>     DWORD dwLength;
>>     DWORD dwMemoryLoad;
>>     DWORDLONG ullTotalPhys;
>>     DWORDLONG ullAvailPhys;
>>     DWORDLONG ullTotalPageFile;
>>     DWORDLONG ullAvailPageFile;
>>     DWORDLONG ullTotalVirtual;
>>     DWORDLONG ullAvailVirtual;
>>     DWORDLONG ullAvailExtendedVirtual;
>>   }
>>
>>   public void callWin32Function() {
>>     auto lib = SharedLib.load(`c:\windows\system32\kernel32.dll`);
>>     void* sym = lib.getSymbol("GlobalMemoryStatusEx");
>>     auto fn = cast(extern(Windows) BOOL function(MEMORYSTATUSEX)) sym;
>>
>>     MEMORYSTATUSEX memStatus;
>>     fn(memStatus);
>>   }
>>
>>
>> But the compiler rejects that cast because of the "extern(Windows)" bit.
>>
>> Without the cast, it compiles, but throws an Access Violation when I call the function pointer, obviously because it's using the wrong calling convention.
>>
>> I hate to suggest that the calling convention should be part of the type system (because the type system is already burdened with too much, especially in D2, with const semantics and now thread-sharing semantics). But clearly two function pointers with different calling conventions are different types of functions, and it seems like the type system should take responsibility for ensuring the correct calling convention.
>>
>> I've also tried this, thinking that declaring "fn" as a member with the "extern(Windows)" calling convention would solve the problem:
>>
>>
>>   alias uint BOOL;
>>   alias uint DWORD;
>>   alias ulong DWORDLONG;
>>
>>   struct MEMORYSTATUSEX {
>>     DWORD dwLength;
>>     DWORD dwMemoryLoad;
>>     DWORDLONG ullTotalPhys;
>>     DWORDLONG ullAvailPhys;
>>     DWORDLONG ullTotalPageFile;
>>     DWORDLONG ullAvailPageFile;
>>     DWORDLONG ullTotalVirtual;
>>     DWORDLONG ullAvailVirtual;
>>     DWORDLONG ullAvailExtendedVirtual;
>>   }
>>
>>   extern(Windows) BOOL function(MEMORYSTATUSEX) fn;
>>
>>   public void callWin32Function() {
>>     auto lib = SharedLib.load(`c:\windows\system32\kernel32.dll`);
>>     void* sym = lib.getSymbol("GlobalMemoryStatusEx");
>>     fn = cast(BOOL function(MEMORYSTATUSEX)) sym;
>>
>>     MEMORYSTATUSEX memStatus;
>>     fn(memStatus);
>>   }
>>
>>
>> But the the compiler still doesn't like that cast. It gives this error:
>>
>> cannot implicitly convert expression (cast(uint
>> function(MEMORYSTATUSEX))exFunctionPtr) of type uint
>> function(MEMORYSTATUSEX) to uintWindows  function(MEMORYSTATUSEX)
>>
>> Anyone have any suggestions?
>>
>> Thanks!
>>
>> --benji
> 
> Try this (untested):
> 
> template ExtWinType(T) { extern(Windows) alias T ExtWinType; }
> 
> void callWin32Fn() { auto lib ...; auto fn = cast(ExtWinType!(BOOL function(MEMORYSTATUSEX))) lib.getSymbol("..."); fn(foo); }

If it doesn't work, try typedef instead.
August 16, 2008
downs wrote:
>> Try this (untested):
>>
>> template ExtWinType(T) { extern(Windows) alias T ExtWinType; }
>>
>> void callWin32Fn() { auto lib ...; auto fn = cast(ExtWinType!(BOOL function(MEMORYSTATUSEX))) lib.getSymbol("..."); fn(foo); }
> 
> If it doesn't work, try typedef instead.

The template (with either the alias or the typedef) compiles just fine, but still results in a runtime Access Violation.

--benji
August 17, 2008
Benji Smith wrote:
[...]
>    extern(Windows) BOOL function(MEMORYSTATUSEX) fn;
> 
>    public void callWin32Function() {
>      auto lib = SharedLib.load(`c:\windows\system32\kernel32.dll`);
>      void* sym = lib.getSymbol("GlobalMemoryStatusEx");
>      fn = cast(BOOL function(MEMORYSTATUSEX)) sym;

Do this instead:
fn = cast(typeof(fn)) sym;


Below is a useful template if you have many functions.  It could be changed into a struct that saves the SharedLib object first, so you don't need to give it that for each function.  I'm not sure if the stringof trick will always work, though...

void bindFunc(alias funcPtr)(SharedLib lib)
{
	funcPtr = cast(typeof(funcPtr))lib.getSymbol(funcPtr.stringof.ptr);
}


Example usage:

extern(Windows) BOOL function(MEMORYSTATUSEX) GlobalMemoryStatusEx;

auto lib = SharedLib.load(`c:\windows\system32\kernel32.dll`);
bindFunc!(GlobalMemoryStatusEx)(lib);


Now you can call GlobalMemoryStatusEx.