FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

DLL hell and D imports

 
Post new topic   Reply to topic     Forum Index -> General
View previous topic :: View next topic  
Author Message
Thrasymachus



Joined: 02 Feb 2009
Posts: 3
Location: London

PostPosted: Mon Feb 02, 2009 9:29 am    Post subject: DLL hell and D imports Reply with quote

Hi all, I was wondering if the folks might be able to help out with this. I have a DLL that I wrote in Delphi (I am not a language expert or bigot, but I enjoy good languages, hence my interest in D). The DLL is just a test library for experimenting with hitching up DLLs into D. I want to use D as a control layer in my app, and I want to call objects made from my existing Delphi codebase.

This seems like an easy enough task, and it should be. Still I get the infamous Error 42: Symbol Undefined.

Here is the Delphi code:
(Sorry to Pascal you, I realise most of you wouldn't probably be natives of Delphi, but it should be pretty easy to follow).

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
library mydll;

uses
SysUtils,
Classes,
MyDLLMU in 'C:\D\DMD\samples\d\aldll\MyDLLMU.pas';

exports

plusone name '_plusone@4',

ChangeString name '_ChangeString@4';

begin

end.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

And it is calling MyDLLMU.pas, which looks like this:
(note the stdcall declaration to force the compile to present the exported functions in C standardcall form)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
unit MYDLLMU;

interface

function plusone(val : Integer) : Integer; export; stdcall;
procedure ChangeString(AString : PChar); export; stdcall;

implementation

uses
Dialogs,
SysUtils;

function plusone(val : Integer) : Integer;
begin
Result := val + 1;
end;

procedure ChangeString(AString : PChar);
begin
if AString = 'Hello' then
StrPCopy(AString, 'World');
end;

end.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

And then I have made a .DEF that looks like this:
(I have included an = mapping to deal with name mangling)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
LIBRARY "mydll.dll"
EXETYPE NT
SUBSYSTEM WINDOWS
CODE SHARED EXECUTE
DATA WRITE
EXPORTS
_plusone@4 = plusone
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

And I have a mydll.d that looks like this:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

export int plusone(int);

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

So I do an IMPLIB /s mydll.lib mydll.def

and I get this:

C:\D\DMD\samples\d\aldll>implib /s mydll.lib mydll.def
Digital Mars Import Library Manager Version 7.6B1n
Copyright (C) Digital Mars 2000. All Rights Reserved.
Output is a Windows NT import library.
Digital Mars Import Library Creator complete.

All good there. If I do a lib on it, I get:

C:\D\DMD\samples\d\aldll>lib mydll.dll
Digital Mars Librarian Version 8.02n
Copyright (C) Digital Mars 2000-2007 All Rights Reserved
http://www.digitalmars.com/ctg/lib.html

Error: Corrupt file 'mydll.dll', Typ=x5e, Len=xc35b

Well, that's interesting... I got past the Corrupt file warning before, but I have been playing around with where I unmangle the name, putting _plusone@4 into the Delphi code so that it presents as a mangled name in the first instance rather than = mapping, but now I seem to get the Corrupt file no matter what I do. Hmmm...

Anyway, even when I am notting getting that error, on compile I get this:

C:\D\DMD\samples\d\aldll>dmd test.d mydll.lib
OPTLINK (R) for Win32 Release 8.00.1
Copyright (C) Digital Mars 1989-2004 All rights reserved.
c:\d\dmd\bin\..\lib\phobos.lib(dmain2)
Error 42: Symbol Undefined _D5mydll7plusoneFiZi
--- errorlevel 1

This is my test.d code:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
import std.stdio;
import mydll;

int main()
{
printf("%d\n", mydll.plusone(4));
return 0;
}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

So it would appear that I am having trouble with finding the symbols for some reason.

I am not very experienced with importing to D from foreign languages, but I have no trouble with pulling in DLLs when I use Delphi, C, or Python (although Python requires some strange shannanigans, based on making .pyd objects).

okay, so I think I am all confusticated now, with symbol mapping based deleria. Any kind assistance would be greatly appreciated Very Happy

NB: I based much of this on: -> http://www.esanu.name/delphi/DLL/Calling%20delphi%20DLL%20from%20MS%20Visual%20C.html
It gave me a bit of form to follow in my experiment.
_________________
* * * * * * * * * * * * * * *
A coward is much more exposed to quarrels than a man of spirit. - Thomas Jefferson

http://www.geekspace.org
Back to top
View user's profile Send private message Send e-mail MSN Messenger
csauls



Joined: 27 Mar 2004
Posts: 278

PostPosted: Mon Feb 02, 2009 2:01 pm    Post subject: Reply with quote

Hrm. I haven't actually seen/used any Pascal in... about thirteen years. ^^

One suggestion, is you will probably need to decorate the decleration(s) in the D module with an 'extern(Pascal)' directive. In fact, this may very well solve your missing symbol, as the mangling pattern it shows (_D5mydll7plusoneFiZi) is the D name mangling, which an extern(Pascal) symbol should not experience.

I say should... haven't tried it, mind you.
_________________
Chris Nicholson-Sauls
Back to top
View user's profile Send private message AIM Address Yahoo Messenger
Thrasymachus



Joined: 02 Feb 2009
Posts: 3
Location: London

PostPosted: Mon Feb 02, 2009 2:52 pm    Post subject: Reply with quote

csauls wrote:
Hrm. I haven't actually seen/used any Pascal in... about thirteen years. ^^


It's an elegant language with good programmer assistance (such as autocleanup). Likewise, D presents a great step forward in C-like languages IMHO in that it moves past the crudeness of C. I really am enjoying D, whereas I like to avoid C because I don't feel like I need the phantom errors it tends to suffer.

csauls wrote:
One suggestion, is you will probably need to decorate the decleration(s) in the D module with an 'extern(Pascal)' directive. In fact, this may very well solve your missing symbol, as the mangling pattern it shows (_D5mydll7plusoneFiZi) is the D name mangling, which an extern(Pascal) symbol should not experience.


Thanks for your help. That made a big difference, and stopped it expecting all sorts of weird and wonderful variations on the name.

It still left it looking for an undefined symbol though, although it was PLUSONE that it was now looking for.

All capital symbols such as it was now seeking rang a big bell for me. That's the Pascal far call format for symbols! (Of course, since we have now told the compiler extern(pascal)). So this made me feel much more like I was in home territory. I decided that case could be a problem, and a search on the subject turned up this thread:
http://www.digitalmars.com/d/archives/digitalmars/D/Linking_against_a_Win32-DLL_55423.html

The key suggestion from that thread was this:
Quote:

- pass /IGNORECASE to optlink to compensate for case sensitivity in
wintab32.dll
dmd wintab32.lib wintabtest.d -L/IGNORECASE


So I compiled it as it suggested with the -L/IGNORECASE switch.

And bingo... it links up nicely.

However, (there's always a 'but' Smile ) I am now getting an error:
Error: Access Violation

This might be a memory sharing error, and there are ways in Pascal of handling those inside the DLL. It is also talked about in that thread I mentioned. So I will go away and experiment more.

Thanks very muchly for your clues. I am very grateful.
_________________
* * * * * * * * * * * * * * *
A coward is much more exposed to quarrels than a man of spirit. - Thomas Jefferson

http://www.geekspace.org
Back to top
View user's profile Send private message Send e-mail MSN Messenger
Thrasymachus



Joined: 02 Feb 2009
Posts: 3
Location: London

PostPosted: Mon Feb 02, 2009 3:33 pm    Post subject: Reply with quote

Thrasymachus wrote:

The key suggestion from that thread was this:

Quote:

- pass /IGNORECASE to optlink to compensate for case sensitivity in
wintab32.dll
dmd wintab32.lib wintabtest.d -L/IGNORECASE


So I compiled it as it suggested with the -L/IGNORECASE switch.

And bingo... it links up nicely.

However, (there's always a 'but' Smile ) I am now getting an error:
Error: Access Violation

This might be a memory sharing error, and there are ways in Pascal of handling those inside the DLL. It is also talked about in that thread I mentioned. So I will go away and experiment more.



Okay. It's now working to a tee.

Here's the resolution for the sake of reference of those who come later and want to know how to fix this.

First the files:

The code for the DLL (mydll.dpr)
- - - - - - - - - - - - - - - - - - - - - - - - -
library mydll;

uses
SysUtils,
Classes,
MyDLLMU in 'C:\D\DMD\samples\d\aldll\MyDLLMU.pas';

exports
plusone name 'PLUSONE',
ChangeString name 'ChangeString';

begin
end.
- - - - - - - - - - - - - - - - - - - - - - - - -

The code for the unit it's calling, where the code for the actual plusone test function lives (MYDLLMU.pas):
- - - - - - - - - - - - - - - - - - - - - - - - -
unit MYDLLMU;
interface

function plusone(val : Integer) : Integer; export; stdcall;
procedure ChangeString(AString : PChar); export; stdcall;

implementation

uses
Dialogs,
SysUtils;

function plusone(val : Integer) : Integer;
begin
Result := val + 1;
end;

procedure ChangeString(AString : PChar);
begin
if AString = 'Hello' then
StrPCopy(AString, 'World');
end;

end.
- - - - - - - - - - - - - - - - - - - - - - - - -

The code for the DEF file (mydll.def):
- - - - - - - - - - - - - - - - - - - - - - - - -
LIBRARY "mydll.dll"
EXETYPE NT
SUBSYSTEM WINDOWS
CODE SHARED EXECUTE
DATA WRITE
EXPORTS
PLUSONE

- - - - - - - - - - - - - - - - - - - - - - - - -

The code for the unit file (mydll.d):
- - - - - - - - - - - - - - - - - - - - - - - - -
extern(Pascal)
{
int plusone(int);
}
- - - - - - - - - - - - - - - - - - - - - - - - -

The code for the test program in D (test.d):
- - - - - - - - - - - - - - - - - - - - - - - - -
import std.stdio;
import mydll;

int main()
{
printf("%d\n", mydll.plusone(4));
return 0;
}

- - - - - - - - - - - - - - - - - - - - - - - - -

The build and run process:

1) Compile the DLL in Delphi.
...then...
2) do the implib:
C:\D\DMD\samples\d\aldll>implib mydll.lib mydll.def
Digital Mars Import Library Manager Version 7.6B1n
Copyright (C) Digital Mars 2000. All Rights Reserved.
Output is a Windows NT import library.
Digital Mars Import Library Creator complete.
3) use lib the check the integrity of the D library file thus created:
C:\D\DMD\samples\d\aldll>lib mydll.lib
Digital Mars Librarian Version 8.02n
Copyright (C) Digital Mars 2000-2007 All Rights Reserved
http://www.digitalmars.com/ctg/lib.html
Digital Mars Librarian complete.
... all good! No errors. (btw: you definitely need the right level of implib, I had that problem earlier, make sure you are using the lastest from Digital Mars).
4) Compile the D test program, linking in the library file (mydll.lib):
C:\D\DMD\samples\d\aldll>dmd test.d mydll.lib
There is no output from this operation unless there is an error. It just returns glib to the command line on completion.
5) Run the test file (test.exe):
C:\D\DMD\samples\d\aldll>test
5
The output '5' is correct, as 4 was the input argument and the test function is int plusone(int).

What did I do? I had removed the stdcall delcaration in MYDLLMU.pas assuming that since I was doing an extern(Pascal) call now that it would no longer be appropriate to include stdcall (since it is no longer a C standard call). This is apparently not the case. So the line must read:
function plusone(val : Integer) : Integer; export; stdcall;

I think that this is because of the format in which arguments (tuples) are passed between the Delphi DLL and D. So although the symbol may be in Pascal format now, the arguments still need to be handled correctly.

Also, you will note that in mydll.dpr, the Delphi source file, I now name the symbol in capitals:
exports
plusone name 'PLUSONE',

This does away with the need to link with the -L/IGNORECASE switch because the Delphi DLL is delcaring the object in caps to begin with, which is what D is expecting of a Pascal symbol.

Thus:
dmd test.d mydll.lib -L/IGNORECASE
becomes
dmd test.d mydll.lib

Btw, if I have done anything incorrectly, especially but not only on the D side (as I am a D novice), I am happy to stand corrected, so please feel free to correct me.

Wunderbar. Done deal. Thanks for your help!
_________________
* * * * * * * * * * * * * * *
A coward is much more exposed to quarrels than a man of spirit. - Thomas Jefferson

http://www.geekspace.org
Back to top
View user's profile Send private message Send e-mail MSN Messenger
Display posts from previous:   
Post new topic   Reply to topic     Forum Index -> General All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group