www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to call Windows function pointers?

reply Benji Smith <dlanguage benjismith.net> writes:
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
Aug 16 2008
next sibling parent reply downs <default_357-line yahoo.de> writes:
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); }
Aug 16 2008
parent reply downs <default_357-line yahoo.de> writes:
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.
Aug 16 2008
parent Benji Smith <dlanguage benjismith.net> writes:
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
Aug 16 2008
prev sibling parent torhu <no spam.invalid> writes:
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.
Aug 16 2008