digitalmars.D.learn - How to call Windows function pointers?
- Benji Smith (68/68) Aug 16 2008 I'm using tango.sys.SharedLib to dynamically load a DLL and get a
- downs (4/94) Aug 16 2008 Try this (untested):
- downs (2/99) Aug 16 2008 If it doesn't work, try typedef instead.
- Benji Smith (4/11) Aug 16 2008 The template (with either the alias or the typedef) compiles just fine,
- torhu (17/23) Aug 16 2008 Do this instead:
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
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! --benjiTry 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
downs wrote:Benji Smith wrote:If it doesn't work, try typedef instead.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! --benjiTry 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
downs wrote:The template (with either the alias or the typedef) compiles just fine, but still results in a runtime Access Violation. --benjiTry 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
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