www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - access violation With dll?

reply "Taylor Hillegeist" <taylorh140 gmail.com> writes:
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
next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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
 void __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
prev sibling next sibling parent reply "jklp" <jklp nowhere.fr> writes:
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
parent reply "jklp" <jklp nowhere.fr> writes:
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:
 [...]
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... } ----
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... ?)
Jul 16 2015
next sibling parent reply "Taylor Hillegeist" <taylorh140 gmail.com> writes:
On Thursday, 16 July 2015 at 17:28:52 UTC, jklp wrote:
 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:
 [...]
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... } ----
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... ?)
Hey that was very useful. Thank you both! Also if you have any good resources regarding this topic I would be interested.
Jul 16 2015
parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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
prev sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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
prev sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
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 WORLD
Just 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