www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to call a function from a dll created with d ?

reply Vinod KC <kcvinu82 gmail.com> writes:
Hi all,
I have created a dll file with this code.
```d
module dimedll;
import core.sys.windows.windows;
import core.sys.windows.dll; // I don't what is this for.
import std.stdio;


mixin SimpleDllMain;

export void testFunc() {
     writeln("This is from dll");
}
```
So now I have a dll fie named `dimedll.dll` and a lib file named 
`dimedll.lib`.
Now, I have created a d source file called `dime.d` and wrote 
this code.
```d
import std.stdio;
import core.sys.windows.windows;
import std.stdio : log = writeln;

pragma(lib, "dimedll.lib");

extern void testFunc();

void main() { 	
	log("Lets build our own ime");
	testFunc();		
}
```
Everything seems to be okay. So I called dmd with this command.
`dmd -i -run dime.d`
But I got this error message.
```
dime.obj : error LNK2019: unresolved external symbol 
__D4dime8testFuncFZv referenced in function __Dmain
dime.exe : fatal error LNK1120: 1 unresolved externals
Error: linker exited with status 1120```
How to fix this ?
Jul 01 2022
next sibling parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
I think it is `extern(D) void testFunc();`?
Jul 01 2022
parent Vinod K Chandran <kcvinu82 gmail.com> writes:
On Friday, 1 July 2022 at 20:08:45 UTC, ryuukk_ wrote:
 I think it is `extern(D) void testFunc();`?
Thanks for the reply. But the result is same linker error.
Jul 01 2022
prev sibling next sibling parent reply mw <mingwu gmail.com> writes:
On Friday, 1 July 2022 at 19:11:16 UTC, Vinod KC wrote:
 Hi all,
 I have created a dll file with this code.
 ```d
 module dimedll;
 

 export void testFunc() {
     writeln("This is from dll");
 }
 ```
 

 void main() { 	
 	log("Lets build our own ime");
 	testFunc();		
 }
 ```
 
 ```
 dime.obj : error LNK2019: unresolved external symbol 
 __D4dime8testFuncFZv referenced in
I think the problem is the linker looking for dime.testFunc, while your lib function is dimedll.testFunc
Jul 01 2022
parent reply Vinod K Chandran <kcvinu82 gmail.com> writes:
On Friday, 1 July 2022 at 21:02:20 UTC, mw wrote:
 I think the problem is the linker looking for dime.testFunc, 
 while your lib function  is dimedll.testFunc
Thanks for the reply. What about this `mixin SimpleDllMain;` I suspect this.
Jul 01 2022
parent reply mw <mingwu gmail.com> writes:
On Friday, 1 July 2022 at 21:15:50 UTC, Vinod K Chandran wrote:
 On Friday, 1 July 2022 at 21:02:20 UTC, mw wrote:
 I think the problem is the linker looking for dime.testFunc, 
 while your lib function  is dimedll.testFunc
Thanks for the reply. What about this `mixin SimpleDllMain;` I suspect this.
Try follow instructions here: https://wiki.dlang.org/Win32_DLLs_in_D
Jul 01 2022
parent reply Vinod K Chandran <kcvinu82 gmail.com> writes:
On Friday, 1 July 2022 at 22:22:42 UTC, mw wrote:

 Try follow instructions here:

 https://wiki.dlang.org/Win32_DLLs_in_D
Thanks. So using a `def` file is a must I think. At first, I thought I can skip that.
Jul 01 2022
parent reply Adam D Ruppe <destructionator gmail.com> writes:
On Friday, 1 July 2022 at 22:32:24 UTC, Vinod K Chandran wrote:
 So using a `def` file is a must I think.
no it is not. you just need to mark things export and make sure names match (including module name)
Jul 01 2022
parent Vinod K Chandran <kcvinu82 gmail.com> writes:
On Friday, 1 July 2022 at 22:38:17 UTC, Adam D Ruppe wrote:
 On Friday, 1 July 2022 at 22:32:24 UTC, Vinod K Chandran wrote:
 So using a `def` file is a must I think.
no it is not. you just need to mark things export and make sure names match (including module name)
Thanks for the reply. These are my questions. 1. `mixin` statement in dll file - Do I need to export it ? 2. There is only one function and that is marked with `export`. 3. Name of the module which I wrote the dll code is `dimedll`. So my dll file's name is `dimedll.dll`. And my lib file's name is `dimedll.lib`. No change in names. 4. Name of my exported function is `testFunc`. And the same name is used in `extern` keyword and the calling site. So where do I check again ?
Jul 01 2022
prev sibling next sibling parent reply Ruby The Roobster <rubytheroobster yandex.com> writes:
The solution is to remove the extern declaration.  That does it 
for me, and it prints the expected output.  No need for a .def 
file, unless you are using optlink as your linker (which, as a 
matter of principle, you should use lld or ld instead.)
Jul 01 2022
parent Vinod KC <kcvinu82 gmail.com> writes:
On Saturday, 2 July 2022 at 00:23:20 UTC, Ruby The Roobster wrote:
 The solution is to remove the extern declaration.  That does it 
 for me, and it prints the expected output.  No need for a .def 
 file, unless you are using optlink as your linker (which, as a 
 matter of principle, you should use lld or ld instead.)
Thank you for the reply. Let me try that.
Jul 01 2022
prev sibling next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 7/1/22 12:11, Vinod KC wrote:

The following function is dimedll.testFunc:

 ```d
 module dimedll;
// ...
 export void testFunc() {
      writeln("This is from dll");
 }
 ```
We suspect the name of the file that defines main() is dime.d.
 extern void testFunc();
That symbol belongs to this module, which is implied to be 'module dime'.
      testFunc();
That's a call to dime.testFunc, which does not exist. With the provided information alone, the following is what I would do: 1) This dll must have a .di file, which should contain the following: // dimedll.di void testFunc(); (.di files can be generated by dmd with its -H command line switch.) 2) Provide dimedll.di as your library's interface file (a la "header file"). 3) The users of this dll should import that .di file (declaring the functions themselves won't work): import dimedll; void main() { // ... } Ali
Jul 01 2022
parent reply Vinod K Chandran <kcvinu82 gmail.com> writes:
On Saturday, 2 July 2022 at 01:05:25 UTC, Ali Çehreli wrote:
 3) The users of this dll should import that .di file (declaring 
 the functions themselves won't work):
 Ali
Hi, Thanks for the reply. I have tried your suggestion. First, I compiled my dll's source code with `-H` switch as you said. Then I got the header file with this content. ```d // D import file generated from dimedll.d module dimedll; import core.sys.windows.windows; import core.sys.windows.dll; import std.stdio; mixin SimpleDllMain!(); export void testFunc(); ``` So far so good. Then I change my `dime.d` source file like this. ```d import dimedll; void main() { log("Lets build our own ime"); testFunc(); } ``` Unfortunately, I got this error message. dime.obj : error LNK2019: unresolved external symbol __imp___D7dimedll8testFuncFZv referenced in function __Dmain dime.obj : error LNK2001: unresolved external symbol __D7dimedll12__ModuleInfoZ dime.exe : fatal error LNK1120: 2 unresolved externals Error: linker exited with status 1120 Then I have tested with `dimedll.testFunc()` instead of `testFunc()` at the calling site. Then also I got an error message like this. dime.obj : error LNK2019: unresolved external symbol __imp___D7dimedll8testFuncFZv referenced in function __Dmain dime.obj : error LNK2001: unresolved external symbol __D7dimedll12__ModuleInfoZ dime.exe : fatal error LNK1120: 2 unresolved externals Error: linker exited with status 1120 I want to test this with ddemangle.exe, but there is no proper documentation for that tool. So I don't know how to use that tool. So Actually I am stuck.
Jul 02 2022
parent reply apz28 <home home.com> writes:
Below is working on Windows

--file dimedll.d:

module dimedll;
import core.sys.windows.windows;
import core.sys.windows.dll;
import std.stdio;

mixin SimpleDllMain;

     export void testFunc()
     {
         writeln("This is from dll");
     }


--file dime.d:

import core.sys.windows.windows;
import std.stdio;
import dimedll;
pragma(lib, "dimedll.lib");

     void main() { 	
     	writeln("Lets call testFunc()");
     	testFunc();		
     }


--file dimedll.di:

module dimedll;
extern void testFunc();


--file dimedll.def

LIBRARY "dimedll.dll"
EXETYPE NT
SUBSYSTEM WINDOWS
CODE SHARED EXECUTE
DATA WRITE


-- command lines in sequence:
-- there should be files as first dmd: dimedll.exp, dimedll.lib, 
dimedll.obj

dmd -of=dimedll.dll dimedll.d dimedll.def
dmd dime.d dimedll.di
Jul 02 2022
parent reply Vinod KC <kcvinu82 gmail.com> writes:
On Saturday, 2 July 2022 at 14:32:11 UTC, apz28 wrote:
 dmd -of=dimedll.dll dimedll.d dimedll.def
 dmd dime.d dimedll.di
Thanks for the reply. Well, I am sorry to say that your suggestions resulted in failure. First of all, when I used this command -- ` dmd -of=dimedll.dll dimedll.d dimedll.def` I got this error message- `Error: unrecognized file extension dll`. So I avoided the `-of=dimedll.dll` part. Then I compiled it with this command - `dmd -H dimedll.d dimedll.def` And I got some warnings. Here are they. ```d dimedll.def(2) : warning LNK4017: EXETYPE statement not supported for the target platform; ignored dimedll.def(3) : warning LNK4017: SUBSYSTEM statement not supported for the target platform; ignored dimedll.def(4) : warning LNK4017: CODE statement not supported for the target platform; ignored dimedll.def(4) : warning LNK4017: DATA statement not supported for the target platform; ignored Creating library dimedll.lib and object dimedll.exp ``` I know all of them are from my `def` file. Anyways, I stepped forward and tried to run the main file with this dll & lib. So I ran this command. - `dmd dime.d dimedll.di`. But I got this error message. ```d dime.obj : error LNK2001: unresolved external symbol __D7dimedll12__ModuleInfoZ dime.exe : fatal error LNK1120: 1 unresolved externals Error: linker exited with status 1120 ```
Jul 02 2022
next sibling parent reply mw <mingwu gmail.com> writes:
On Saturday, 2 July 2022 at 20:43:41 UTC, Vinod KC wrote:
 On Saturday, 2 July 2022 at 14:32:11 UTC, apz28 wrote:
 dmd -of=dimedll.dll dimedll.d dimedll.def
 dmd dime.d dimedll.di
Thanks for the reply. Well, I am sorry to say that your suggestions resulted in failure. First of all, when I used this command -- ` dmd -of=dimedll.dll dimedll.d dimedll.def` I got this error message- `Error: unrecognized file extension dll`.
Actually, can you create a github repo, I'm sure people will send you a working PR.
Jul 02 2022
parent Vinod K Chandran <kcvinu82 gmail.com> writes:
On Saturday, 2 July 2022 at 21:36:50 UTC, mw wrote:
 Actually, can you create a github repo, I'm sure people will 
 send you a working PR.
Yes I can. I will inform here once I did it.
Jul 02 2022
prev sibling next sibling parent reply frame <frame86 live.com> writes:
On Saturday, 2 July 2022 at 20:43:41 UTC, Vinod KC wrote:

 But I got this error message.
 dime.obj : error LNK2001: unresolved external symbol 
 __D7dimedll12__ModuleInfoZ
 dime.exe : fatal error LNK1120: 1 unresolved externals
 Error: linker exited with status 1120
I tried the -H switch. You can't rely on that. I comment the lines that shouldn't be there - then it should work: dimedll.di ```d // D import file generated from 'dimedll.d' module dimedll; // import core.sys.windows.dll; // import std.stdio; // mixin SimpleDllMain!(); export void testFunc(); ``` dimedll.d: ```d module dimedll; import core.sys.windows.dll; import std.stdio; mixin SimpleDllMain; export void testFunc() { writeln("This is from dll"); } ``` app.d: ```d module app; import dimedll; import std.stdio; import std.stdio : log = writeln; pragma(lib, "dimedll.lib"); void main() { log("Lets build our own ime"); testFunc(); } ``` You should be able to change contents in the DLL and run the executable wihtout re-compiling (the library file should be round ~2kB). PS: ddemangle just waits for your input. You copy in the mangled symbol like `__D7dimedll12__ModuleInfoZ` and press enter ;-)
Jul 03 2022
parent reply cc <cc nevernet.com> writes:
On Sunday, 3 July 2022 at 09:43:20 UTC, frame wrote:
 app.d:
 ```d
 module app;
 import dimedll;

 import std.stdio;
 import std.stdio : log = writeln;

 pragma(lib, "dimedll.lib");

 void main() { 	
    log("Lets build our own ime");
    testFunc();		
 }
 ```

 You should be able to change contents in the DLL and run the 
 executable wihtout re-compiling (the library file should be 
 round ~2kB).

 PS: ddemangle just waits for your input. You copy in the 
 mangled symbol like `__D7dimedll12__ModuleInfoZ` and press 
 enter ;-)
Does importing dimedll into app.d properly NOT link in the functions that are exported to the DLL? When I tried something similar with dmd, I had to create a .di file containing just stubs, otherwise it looked like it was ignoring the DLL and compiling in an additional copy of each fuction.
Jul 07 2022
parent frame <frame86 live.com> writes:
On Thursday, 7 July 2022 at 17:29:42 UTC, cc wrote:

 Does importing dimedll into app.d properly NOT link in the 
 functions that are exported to the DLL?  When I tried something 
 similar with dmd, I had to create a .di file containing just 
 stubs, otherwise it looked like it was ignoring the DLL and 
 compiling in an additional copy of each fuction.
IMHO this is the expected behaviour since the compiler comes before the linker and `pragma(lib)` is a linker directive, so if dimedll.di is not present it looks for dimedll.d instead and just compiles the code. The link to DLL is only present in the actual lib-file and only used by the linker. The pitfall with DMD is that the automatic generated di-file is not usable as it contains a mixin that is meant for the DLL but not the application that links it, resulting in _ModuleInfoZ error while trying.
Jul 08 2022
prev sibling parent Ruby The Roobster <rubytheroobster yandex.com> writes:
On Saturday, 2 July 2022 at 20:43:41 UTC, Vinod KC wrote:
 On Saturday, 2 July 2022 at 14:32:11 UTC, apz28 wrote:
 dmd -of=dimedll.dll dimedll.d dimedll.def
 dmd dime.d dimedll.di
Thanks for the reply. Well, I am sorry to say that your suggestions resulted in failure. First of all, when I used this command -- ` dmd -of=dimedll.dll dimedll.d dimedll.def` I got this error message- `Error: unrecognized file extension dll`. So I avoided the `-of=dimedll.dll` part. Then I compiled it with this command - `dmd -H dimedll.d dimedll.def` And I got some warnings. Here are they. ```d dimedll.def(2) : warning LNK4017: EXETYPE statement not supported for the target platform; ignored dimedll.def(3) : warning LNK4017: SUBSYSTEM statement not supported for the target platform; ignored dimedll.def(4) : warning LNK4017: CODE statement not supported for the target platform; ignored dimedll.def(4) : warning LNK4017: DATA statement not supported for the target platform; ignored Creating library dimedll.lib and object dimedll.exp ``` I know all of them are from my `def` file. Anyways, I stepped forward and tried to run the main file with this dll & lib. So I ran this command. - `dmd dime.d dimedll.di`. But I got this error message. ```d dime.obj : error LNK2001: unresolved external symbol __D7dimedll12__ModuleInfoZ dime.exe : fatal error LNK1120: 1 unresolved externals Error: linker exited with status 1120 ```
First issue - you're using dmd on windows. Dmd gives me errors about the ModuleInfo, while LDC doesn't. [This is the LDC download link.](https://github.com/ldc-developers/ldc/releases/download/v1.29.0/ldc2-1.29.0-w ndows-multilib.exe) Next, change dimedll.d to the following: ```d module dimedll; export void testFunc() { import std.stdio; writeln("Lets build our own ime."); } ``` and dime.d to the following: ```d import dimedll; //In case you ever write more functions for dimedll.d; pragma(lib, "dimedll.lib"); void main() { testFunc(); } ``` Then run ```ldc2 -shared dimedll.d``` and right after that ```ldc2 dime.d```. If I didn't make a mistake in writing this (I tested it on my own system), it should output a working program that prints the expected output when ran.
Jul 07 2022
prev sibling parent reply kinke <noone nowhere.com> writes:
With LDC, this is sufficient for this trivial example:

```d
module dimedll;

export void testFunc() { // export only needed when compiling 
with `-fvisibility=hidden`
     import std.stdio;
     writeln("This is from dll");
}
```

`ldc2 -shared dimedll.d` generates import lib + DLL.

```d
import dimedll : testFunc;

pragma(lib, "dimedll");

void main() {
     import std.stdio;
     writeln("Lets build our own ime");
     testFunc();
}
```

`ldc2 -link-defaultlib-shared dime.d` generates the .exe and 
makes it share the druntime/Phobos DLLs with `dimedll.dll`. (More 
complex cases might need `-dllimport=all`).

```
C:\temp\dllTest>dime
Lets build our own ime
This is from dll

C:\temp\dllTest>dir
…
07/02/2022  03:54 PM               155 dime.d
07/02/2022  03:57 PM            18,432 dime.exe
07/02/2022  03:57 PM            19,679 dime.obj
07/02/2022  03:56 PM               162 dimedll.d
07/02/2022  03:57 PM            20,480 dimedll.dll
07/02/2022  03:57 PM             7,534 dimedll.exp
07/02/2022  03:56 PM            13,036 dimedll.lib
07/02/2022  03:57 PM            21,233 dimedll.obj
```

On Posix, the only difference is that one would have to link 
`libdimedll.{so,dylib}` explicitly via `-L-ldimedll` instead of 
the `pragma(lib)`.
Jul 02 2022
parent reply frame <frame86 live.com> writes:
On Saturday, 2 July 2022 at 14:06:03 UTC, kinke wrote:
 With LDC, this is sufficient for this trivial example:

 ```d
 module dimedll;

 export void testFunc() { // export only needed when compiling 
 with `-fvisibility=hidden`
     import std.stdio;
     writeln("This is from dll");
 }
 ```

 `ldc2 -shared dimedll.d` generates import lib + DLL.

 ```d
 import dimedll : testFunc;

 pragma(lib, "dimedll");

 void main() {
     import std.stdio;
     writeln("Lets build our own ime");
     testFunc();
 }
 ```

 `ldc2 -link-defaultlib-shared dime.d` generates the .exe and 
 makes it share the druntime/Phobos DLLs with `dimedll.dll`. 
 (More complex cases might need `-dllimport=all`).

 ```
 C:\temp\dllTest>dime
 Lets build our own ime
 This is from dll

 C:\temp\dllTest>dir
 …
 07/02/2022  03:54 PM               155 dime.d
 07/02/2022  03:57 PM            18,432 dime.exe
 07/02/2022  03:57 PM            19,679 dime.obj
 07/02/2022  03:56 PM               162 dimedll.d
 07/02/2022  03:57 PM            20,480 dimedll.dll
 07/02/2022  03:57 PM             7,534 dimedll.exp
 07/02/2022  03:56 PM            13,036 dimedll.lib
 07/02/2022  03:57 PM            21,233 dimedll.obj
 ```

 On Posix, the only difference is that one would have to link 
 `libdimedll.{so,dylib}` explicitly via `-L-ldimedll` instead of 
 the `pragma(lib)`.
 This is from dll
Are you sure? You import `testFunc` as normal import, the compiler ignores `pragma(lib)` - that's only for the linker which will ignore it too since the symbol is already in your executable. If you can run your exectuable without dimedll.dll present, then the function is **not** statically linked in. A static linked function should generate a very small lib-file and yours look too big to me. I don't know about LDC but with DMD I struggle with static linked DLLs because the library generated does not link to the DLL. To get right results, I need to pass the linker flag -`-L=/IMPLIB` (or `-L=/DLL` for 64bit) to generate a lib-file that is really linked to the DLL later.
Jul 03 2022
parent reply kinke <noone nowhere.com> writes:
On Sunday, 3 July 2022 at 08:15:38 UTC, frame wrote:
 Are you sure?
100%, just try yourself.
 You import `testFunc` as normal import, the compiler ignores 
 `pragma(lib)` - that's only for the linker which will ignore it 
 too since the symbol is already in your executable.
Why would the symbol be defined in the executable? `dimedll.d` isn't compiled into the executable.
 A static linked function should generate a very small lib-file 
 and yours look too big to me.
The code is using Phobos std.stdio.writeln templates, so the ~20 KB for both exe and DLL are to be expected and IMO absolutely acceptable.
 I don't know about LDC but with DMD I struggle with static 
 linked DLLs because the library generated does not link to the 
 DLL. To get right results, I need to pass the linker flag 
 -`-L=/IMPLIB` (or `-L=/DLL` for 64bit) to generate a lib-file 
 that is really linked to the DLL later.
DMD's DLL support is waaay behind LDC's, especially once stuff gets more interesting than trivial examples.
Jul 03 2022
parent reply frame <frame86 live.com> writes:
On Sunday, 3 July 2022 at 12:54:45 UTC, kinke wrote:
 On Sunday, 3 July 2022 at 08:15:38 UTC, frame wrote:
 Are you sure?
100%, just try yourself. Why would the symbol be defined in the executable? `dimedll.d` isn't compiled into the executable. The code is using Phobos std.stdio.writeln templates, so the ~20 KB for both exe and DLL are to be expected and IMO absolutely acceptable. DMD's DLL support is waaay behind LDC's, especially once stuff gets more interesting than trivial examples.
Yeah, I tried LDC and there are differences: The library file generated by LDC contains the link to the DLL. The library file generated by DMD is missing that link. So linking the DMD library would embed the symbol from the library - that was my confusion with your example. Only the -H switch or manual linker command generates a valid link to the DLL with DMD but then it's missing all the other library contents (also it needs `SimpleDllMain` or bails out linking errors to `_calloc` and Windows symbols) :\ Also the -H switch doesn't work correctly. Without supplying -L/DLL flag too, I get link error: ``` 1561: entry point must be defined ```
Jul 03 2022
parent frame <frame86 live.com> writes:
On Sunday, 3 July 2022 at 16:48:52 UTC, frame wrote:

 Only the -H switch or manual linker command generates a valid 
 link to the DLL with DMD but then it's missing all the other 
 library contents (also it needs `SimpleDllMain` or bails out 
 linking errors to `_calloc` and Windows symbols) :\
`dmd -shared` / `dmd -H -shared` seems to work too (library contains link to DLL). Don't know why I had in mind that it would fail - maybe it did in the past. However both DMD and LDC produces questionable header files for this task.
Jul 04 2022