Thread overview
access violation With dll?
Jul 16, 2015
Taylor Hillegeist
Jul 16, 2015
Adam D. Ruppe
Jul 16, 2015
jklp
Jul 16, 2015
jklp
Jul 16, 2015
Taylor Hillegeist
Jul 16, 2015
Adam D. Ruppe
Jul 16, 2015
Adam D. Ruppe
Jul 16, 2015
Adam D. Ruppe
July 16, 2015
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.

July 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

> 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(...);


July 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...
}
----

July 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:
>> [...]
>
> 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... ?)
July 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:
>> 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.
July 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.
July 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

July 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 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.