digitalmars.D.learn - access violation With dll?
- Taylor Hillegeist (42/42) Jul 16 2015 Beleive it or not the code below does work. However I get an
- Adam D. Ruppe (16/20) Jul 16 2015 Those signatures don't actually match, your call is overwriting
- jklp (17/60) Jul 16 2015 Your proto is wrong. Your forgot the FFI convention (__cdecl =
- jklp (10/28) Jul 16 2015 Also i guess that the dll returns a null terminated string so the
- Taylor Hillegeist (3/35) Jul 16 2015 Hey that was very useful. Thank you both! Also if you have any
- Adam D. Ruppe (6/8) Jul 16 2015 my book goes into it briefly *shameless plug*
- Adam D. Ruppe (6/15) Jul 16 2015 That's not needed with printf, since it works with null
- Adam D. Ruppe (33/40) Jul 16 2015 Just to go a little deeper into what is happening here, we'll
Beleive it or not the code below does work. However I get an access violation after every run? any Ideas why? +++++++++++++++++++++++++Code to Run DLL function+++++++++++++++++++++++++++ import core.runtime; import std.stdio; import core.memory; import std.c.windows.windows; int main() { HMODULE h; FARPROC fp; printf("Start Dynamic Link...\n"); h = cast(HMODULE) Runtime.loadLibrary("SharedLib.dll"); void function(ref char[], int) Testf = cast(void function(ref char[], int)) GetProcAddress(h, "Test"); //Function Says HELLO WORLD char[] STUFF; STUFF.length = 5000; Testf( STUFF , STUFF.length); printf("%s\n", (&STUFF)); //PRINTS HELLO WORLD Runtime.unloadLibrary(h); printf("End...\n"); return 0; } ++++++++++++++++++++++++++++++END CODE+++++++++++++++++++++++++++ the function header has this line: void __cdecl Test(char MyOutput[], int32_t len); and i get this Error: object.Error (0): Access Violation ---------------- 0x77206568 0x646C726F 0x00405A2C in int object.ModuleInfo.opApply(scope int delegate(object.ModuleInfo*)) 0x0040222C in main 0x00414949 in mainCRTStartup 0x7678337A in BaseThreadInitThunk 0x770592E2 in RtlInitializeExceptionChain 0x770592B5 in RtlInitializeExceptionChain To be honest I was surprised this worked, since i am fairly unfamiliar to linking to dlls.
Jul 16 2015
On Thursday, 16 July 2015 at 17:04:09 UTC, Taylor Hillegeist wrote:void function(ref char[], int) Testf = cast(void function(ref char[], int)) GetProcAddress(h, "Test"); //Function Says HELLO WORLDvoid __cdecl Test(char MyOutput[], int32_t len);Those signatures don't actually match, your call is overwriting the stack with the string data, which means main cannot return properly; all that stuff has been smashed to bits. The proper D signature of that C function is: extern(C) void function(char*, int); You really shouldn't try to use D arrays or `ref` when interacting with C functions, C doesn't understand those D features. (You can make it work but it needs cooperation in both functions.) Instead, stick to the basic types C supports like ints and pointers. If you fix that, your code should work. I'd say go ahead and alias the type for easier use extern(C) alias dll_func_type = void function(char*, int); auto Testf = cast(dll_func_type) GetProcAddress(...);
Jul 16 2015
On Thursday, 16 July 2015 at 17:04:09 UTC, Taylor Hillegeist wrote:Beleive it or not the code below does work. However I get an access violation after every run? any Ideas why? +++++++++++++++++++++++++Code to Run DLL function+++++++++++++++++++++++++++ import core.runtime; import std.stdio; import core.memory; import std.c.windows.windows; int main() { HMODULE h; FARPROC fp; printf("Start Dynamic Link...\n"); h = cast(HMODULE) Runtime.loadLibrary("SharedLib.dll"); void function(ref char[], int) Testf = cast(void function(ref char[], int)) GetProcAddress(h, "Test"); //Function Says HELLO WORLD char[] STUFF; STUFF.length = 5000; Testf( STUFF , STUFF.length); printf("%s\n", (&STUFF)); //PRINTS HELLO WORLD Runtime.unloadLibrary(h); printf("End...\n"); return 0; } ++++++++++++++++++++++++++++++END CODE+++++++++++++++++++++++++++ the function header has this line: void __cdecl Test(char MyOutput[], int32_t len); and i get this Error: object.Error (0): Access Violation ---------------- 0x77206568 0x646C726F 0x00405A2C in int object.ModuleInfo.opApply(scope int delegate(object.ModuleInfo*)) 0x0040222C in main 0x00414949 in mainCRTStartup 0x7678337A in BaseThreadInitThunk 0x770592E2 in RtlInitializeExceptionChain 0x770592B5 in RtlInitializeExceptionChain To be honest I was surprised this worked, since i am fairly unfamiliar to linking to dlls.Your proto is wrong. Your forgot the FFI convention (__cdecl = extern(C)). Try this instead: --- extern(C) alias Proto = void function(char*, int); Proto func = cast(Proto) GetProcAddress(h, "Test"); if (func) { char[] STUFF; STUFF.length = 5000; func (STUFF.ptr , STUFF.length); // prints etc... } ----
Jul 16 2015
On Thursday, 16 July 2015 at 17:22:50 UTC, jklp wrote:On Thursday, 16 July 2015 at 17:04:09 UTC, Taylor Hillegeist wrote:Also i guess that the dll returns a null terminated string so the result has to be read like this: --- import std.string; printf("%s\n", fromStringz(STUFF.ptr)); --- Otherwise you'll get a random output after the 'HELLO WORLD'. (wait maybe the console will automatically cut... ?)[...]Your proto is wrong. Your forgot the FFI convention (__cdecl = extern(C)). Try this instead: --- extern(C) alias Proto = void function(char*, int); Proto func = cast(Proto) GetProcAddress(h, "Test"); if (func) { char[] STUFF; STUFF.length = 5000; func (STUFF.ptr , STUFF.length); // prints etc... } ----
Jul 16 2015
On Thursday, 16 July 2015 at 17:28:52 UTC, jklp wrote:On Thursday, 16 July 2015 at 17:22:50 UTC, jklp wrote:Hey that was very useful. Thank you both! Also if you have any good resources regarding this topic I would be interested.On Thursday, 16 July 2015 at 17:04:09 UTC, Taylor Hillegeist wrote:Also i guess that the dll returns a null terminated string so the result has to be read like this: --- import std.string; printf("%s\n", fromStringz(STUFF.ptr)); --- Otherwise you'll get a random output after the 'HELLO WORLD'. (wait maybe the console will automatically cut... ?)[...]Your proto is wrong. Your forgot the FFI convention (__cdecl = extern(C)). Try this instead: --- extern(C) alias Proto = void function(char*, int); Proto func = cast(Proto) GetProcAddress(h, "Test"); if (func) { char[] STUFF; STUFF.length = 5000; func (STUFF.ptr , STUFF.length); // prints etc... } ----
Jul 16 2015
On Thursday, 16 July 2015 at 19:25:42 UTC, Taylor Hillegeist wrote:Also if you have any good resources regarding this topic I would be interested.my book goes into it briefly *shameless plug* https://www.packtpub.com/application-development/d-cookbook The interfacing with C page on the dlang.org helps too: http://dlang.org/interfaceToC
Jul 16 2015
On Thursday, 16 July 2015 at 17:28:52 UTC, jklp wrote:Also i guess that the dll returns a null terminated string so the result has to be read like this: --- import std.string; printf("%s\n", fromStringz(STUFF.ptr)); ---That's not needed with printf, since it works with null terminated strings natively (it is itself a C function). Good advice for using writef though, or you can also just `to!string(cstr)` to get one.Otherwise you'll get a random output after the 'HELLO WORLD'. (wait maybe the console will automatically cut... ?)the console won't, it'll generally spew trash.
Jul 16 2015
On Thursday, 16 July 2015 at 17:04:09 UTC, Taylor Hillegeist wrote:void function(ref char[], int) Testf = cast(void function(ref char[], int)) GetProcAddress(h, "Test"); //Function Says HELLO WORLD char[] STUFF; STUFF.length = 5000; Testf( STUFF , STUFF.length); printf("%s\n", (&STUFF)); //PRINTS HELLO WORLDJust to go a little deeper into what is happening here, we'll convert the D features into C features and understand what it sees. A D array, `char[]`, is seen in C as: `struct char_array { size_t length; char* ptr; }`. A D `ref` is seen in C as a pointer. So, your function there, in C, would look like: void function(struct char_array *arr, int); Which isn't what you were hoping for... so what happened when you called it was the C side got: Testf((char*) &STUFF, 5000); Which runs... but what is &STUFF? It is one of those char_array structs sitting on the stack! Which is only 8 bytes long. So when the C function wrote out HELLO WORLD\0, your D array got smashed with: length = (size_t) 'LLEH'; ptr = (char*) 'ROW '; And the 'LD\0' went right out of bounds and wrote over whatever else was on the stack next (which is probably the HMODULE so when you called FreeLibrary, it tried to free junk and caused the access violation, but I'm not sure, you could load up a debugger and try to find out, but the main point is that it overwrote memory that it wasn't supposed to!). The printf, however, ran because you made the same mistake twice - passing &STUFF to it, while wrong, just passed the data your last call clobbered.... coincidentally being valid enough to print out. Then the extern(C) vs extern(D) thing can come into play, but here you just lucked out. So yeah to avoid this in the future, be sure all those things match the way C sees them - ref and arrays are generally wrong there, use plain pointers instead.
Jul 16 2015