www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Linking against a Win32-DLL

reply =?ISO-8859-15?Q?Marc_M=FCller?= <dont spam.me> writes:
Hello,

as my first Project in D I am going to write a Application for my Wacom 
graphic tablet.

So i have to use a windows dll(wintab32.dll) - but i don't have a 
corresponding lib-file.

I have converted the C-Header I found with the dev-kit of my graphic 
tablet and converted it to D.

I tried to use the "export(C)" or the "export(Windows)" settings in the 
converted header file.

Example from the converted header -> wintab.d:
extern(Windows)
{
	UINT  WTInfoA(UINT , UINT , LPVOID );
}



demo.d:
import std.stdio;
import wintab;

void main(char[][] args)
{
	writefln("Hello World\n");
	WTInfo( 0, 0, null);
}



When i compile my Hello-World Application which is calling a function 
from the DLL i get the following errormessages from the compiler (using 
"extern (C)"):


dmd.exe demo.d -LC:/WINDOWS/system32/wintab32.dll
d:\Projects\wacom\dmd\bin\..\..\dm\bin\link.exe 
demo,,,user32+kernel32/noiC:/WINDOWS/system32/wintab32.dll;
OPTLINK (R) for Win32  Release 7.50B1
Copyright (C) Digital Mars 1989 - 2001  All Rights Reserved

OPTLINK : Warning 9: Unknown Option : NOIC
OPTLINK : Warning 9: Unknown Option : WINDOWS
OPTLINK : Warning 9: Unknown Option : SYSTEM32
OPTLINK : Warning 9: Unknown Option : WINTAB32.DLL
demo.obj(demo)
  Error 42: Symbol Undefined _WTInfoA 12



With "extern(Windows)" the last line reads:
  Error 42: Symbol Undefined _WTInfoA


I don't know what to do - and I can not find usefull information for 
using Win32-DLLs without a lib-file.
Are my compile settings correct?
Is the converted header correct?
Or is it just not possible using a DLL without a library?

Please give me a hint

Thanks
	-Marc-
Jul 08 2007
next sibling parent reply torhu <fake address.dude> writes:
Marc Müller wrote:
 Hello,
 
 as my first Project in D I am going to write a Application for my Wacom 
 graphic tablet.
 
 So i have to use a windows dll(wintab32.dll) - but i don't have a 
 corresponding lib-file.

You can use the implib tool to create an import library. implib /s wintab32.lib wintab32.lib Download and docs: http://ftp.digitalmars.com/bup.zip http://www.digitalmars.com/ctg/implib.html
Jul 08 2007
parent reply BLS <nanali nospam-wanadoo.fr> writes:
torhu schrieb:
 Marc Müller wrote:
 Hello,

 as my first Project in D I am going to write a Application for my 
 Wacom graphic tablet.

 So i have to use a windows dll(wintab32.dll) - but i don't have a 
 corresponding lib-file.

You can use the implib tool to create an import library. implib /s wintab32.lib wintab32.lib Download and docs: http://ftp.digitalmars.com/bup.zip http://www.digitalmars.com/ctg/implib.html

Guess you mean : implib /s wintab32.lib wintab32.DLL Hi Marc , what tool do you use to convert the c header ? Walter s htod, or Gregor s BCDGen Bjoern
Jul 08 2007
next sibling parent reply torhu <fake address.dude> writes:
BLS wrote:
  > Guess you mean :
 implib /s wintab32.lib wintab32.DLL

Yup, that's what I meant. The output goes first, the input last.
Jul 08 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
torhu wrote:
 BLS wrote:
  > Guess you mean :
 implib /s wintab32.lib wintab32.DLL

Yup, that's what I meant. The output goes first, the input last.

You said .lib for both input and output. I got the order of lib and dll backwards. ;-) --bb
Jul 08 2007
parent BLS <nanali nospam-wanadoo.fr> writes:
Bill Baxter schrieb:
 torhu wrote:
 BLS wrote:
  > Guess you mean :
 implib /s wintab32.lib wintab32.DLL

Yup, that's what I meant. The output goes first, the input last.

You said .lib for both input and output. I got the order of lib and dll backwards. ;-) --bb

Bjoern
Jul 09 2007
prev sibling parent reply =?ISO-8859-15?Q?Marc_M=FCller?= <dont spam.me> writes:
 Guess you mean :
 implib /s wintab32.lib wintab32.DLL
 
 Hi Marc , what tool do you use to convert the c header ? Walter s htod, 
 or Gregor s BCDGen
 Bjoern

Wow - I did not even tried to hope that I get help that fast... Thank you a lot - the library generated by the implib tool did it! I converted the header with htod (and quite a lot of manual work...). I did not know BCDGen, but I'm going to check it out next time. Nevertheless I still have a problem with the code: Sometimes I get an "Access Violation" error out of nowhere... For example the following Code works, the DLL-function is called correctly and returns with the expected Value: void main(char[][] args) { writefln("Getting WTInfo..."); int value = WTInfo( 0, 0, null); writefln("Received WTInfo"); } Output: Getting WTInfo... Received WTInfo Everything is fine - but now the same code (I'm just ignoring the return value of the function): void main(char[][] args) { writefln("Getting WTInfo..."); WTInfo( 0, 0, null); writefln("Received WTInfo"); } This time the program fails with the following output: Getting WTInfo... Received WTInfo Error: Access Violation When I surround the 3 lines with a try-catch block it runs without even throwing an exception. Very strange... I don't know how reliable the order of the program output is, but since the error occurs after the function call - I guess somehow I have a memory - or stack problem... Perhaps the calling convention? Hmm... I just can't find out what's wrong :-( Do you have a idea? Hopefully, -Marc-
Jul 08 2007
parent reply BLS <nanali nospam-wanadoo.fr> writes:
Marc Müller schrieb:

 
 void main(char[][] args)
 {
     writefln("Getting WTInfo...");
     int value = WTInfo( 0, 0, null);
     writefln("Received WTInfo");
 }
 Output:
 Getting WTInfo...
 Received WTInfo
 
 
 Everything is fine - but now the same code (I'm just ignoring the return 
 value of the function):
 
 
 void main(char[][] args)
 {
     writefln("Getting WTInfo...");
     WTInfo( 0, 0, null);
     writefln("Received WTInfo");
 }
 
 This time the program fails with the following output:
 Getting WTInfo...
 Received WTInfo
 Error: Access Violation

I suggest to publish the WTInfo() C Header and your D translation, as well as your try block. Otherwise it is a bit difficult to help. ...probabely you should start a new thread in D learn. I often got better results using this forum. I suppose/guess a WTInfo() return value != 0 says either, feature not available, or something else went wrong. So I think you need the return value anyway ??? Bjoern
Jul 09 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
BLS wrote:
 Marc Müller schrieb:
 
 void main(char[][] args)
 {
     writefln("Getting WTInfo...");
     int value = WTInfo( 0, 0, null);
     writefln("Received WTInfo");
 }
 Output:
 Getting WTInfo...
 Received WTInfo


 Everything is fine - but now the same code (I'm just ignoring the 
 return value of the function):


 void main(char[][] args)
 {
     writefln("Getting WTInfo...");
     WTInfo( 0, 0, null);
     writefln("Received WTInfo");
 }

 This time the progrbam fails with the following output:
 Getting WTInfo...
 Received WTInfo
 Error: Access Violation

I suggest to publish the WTInfo() C Header and your D translation, as well as your try block. Otherwise it is a bit difficult to help. ...probabely you should start a new thread in D learn. I often got better results using this forum. I suppose/guess a WTInfo() return value != 0 says either, feature not available, or something else went wrong. So I think you need the return value anyway ??? Bjoern

You're saying if you stash the return value it runs fine, otherwise it crashes? That sounds like some kind of messing up of the stack. Is it possible you're doing extern(C) when you need to be doing extern(Window), or vice versa? I don't have much experience with that so I'm just throwing it out. Maybe it just won't link if you get that wrong. --bb
Jul 09 2007
parent reply =?ISO-8859-15?Q?Marc_M=FCller?= <dont spam.me> writes:
 Is it possible you're doing extern(C) when you need to be doing 
 extern(Window), or vice versa?
 
 I don't have much experience with that so I'm just throwing it out. 
 Maybe it just won't link if you get that wrong.

With extern(Windows) the program does not link :-( I simplified the program and wrote it to digitalmars.D.learn ("Problems when using DLL-Functions") as Bjoern suggested. Again - thanks a lot for your help! -Marc-
Jul 09 2007
parent reply Jascha Wetzel <firstname mainia.de> writes:
the problem is indeed the calling convention.

the version of wintab32.dll i found uses FAR PASCAL. the callee's clean 
up the stack. if you use extern(C), the caller cleans up the stack. 
therefore the return address at the end of the main function is 
incorrect and it crashes.
this is only the case, though, as long as you don't use any local 
variables. in this case, D uses the ENTER and LEAVE x86 instructions to 
create and destroy stack frames. the correct stack pointers (the pointer 
to the former stack frame) are saved (in EBP) and therefore the 
incorrect value in ESP (that results from the obsolete cleanup) gets 
overwritten by the saved value, the correct return address is in place 
-> no crash.

after looking at the C headers for wintab32.dll, you see that they use 
the WINAPI = __stdcall = extern(Windows) calling convention only if the 
win32 headers have been included (WINAPI is defined). else they use FAR 
PASCAL. since the function names in wintab32.dll do not adhere to the 
__stdcall convention (leading _ and trailing  X with X being the number 
of parameter bytes), they must be using FAR PASCAL.

DMD's extern(Pascal) calling convention expects all uppercase function 
names. therefore you'll have to specify the /IGNORECASE option to 
optlink. not that FAR PASCAL has to leading underscore in the function 
names (as opposed to __cdecl or extern(C)).

putting it all together:
- use implib without the /s switch (no leading underscores)
implib wintab32.lib \WINDOWS\system32\Wintab32.dll

- declare all imports as extern(Pascal)
extern(Pascal) UINT WTInfoA(UINT, UINT, LPVOID);

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


i used Ddbg to hunt this down in a very short amount of time, btw ;)

Marc Müller wrote:
 Is it possible you're doing extern(C) when you need to be doing 
 extern(Window), or vice versa?

 I don't have much experience with that so I'm just throwing it out. 
 Maybe it just won't link if you get that wrong.

With extern(Windows) the program does not link :-( I simplified the program and wrote it to digitalmars.D.learn ("Problems when using DLL-Functions") as Bjoern suggested. Again - thanks a lot for your help! -Marc-

Jul 09 2007
next sibling parent Jascha Wetzel <firstname mainia.de> writes:
Jascha Wetzel wrote:
 not that FAR PASCAL has to leading underscore ...

should read: note that FAR PASCAL has no leading underscore
Jul 09 2007
prev sibling next sibling parent reply BLS <nanali nospam-wanadoo.fr> writes:
Jascha Wetzel schrieb:
  > putting it all together:
 - use implib without the /s switch (no leading underscores)
 implib wintab32.lib \WINDOWS\system32\Wintab32.dll
 
 - declare all imports as extern(Pascal)
 extern(Pascal) UINT WTInfoA(UINT, UINT, LPVOID);
 
 - pass /IGNORECASE to optlink to compensate for case sensitivity in 
 wintab32.dll
 dmd wintab32.lib wintabtest.d -L/IGNORECASE
 
 
 i used Ddbg to hunt this down in a very short amount of time, btw ;)

Hallo Jascha, I wonder how to setup Ddbg to figure out this information. Not nessesary to say that a step by step (4 dummies) guideline is more than welcome ! MANY Thanks in advance. Bjoern
Jul 09 2007
parent reply Jascha Wetzel <firstname mainia.de> writes:
BLS wrote:
 Hallo Jascha,
 I wonder how to setup Ddbg to figure out this information. Not nessesary 
 to say that a step by step (4 dummies) guideline is more than welcome !
 MANY Thanks in advance.
 Bjoern

here's log from the ddbg session. i've added comments starting with a # C:\>ddbg wintabtest.exe Ddbg 0.10 beta - D Debugger Copyright (c) 2007 Jascha Wetzel see http://ddbg.mainia.de/doc.html for documentation Loading symbols from wintabtest.exe # use line 1 as a shortcut to "the first source line in the file" ->bp win:1 Breakpoint set: wintabtest.d:8 0x402010 all threads ->r ntdll.dll loaded at 0x7c900000 KERNEL32.dll loaded at 0x7c800000 Wintab32.dll loaded at 0x10000000 USER32.dll loaded at 0x7e410000 GDI32.dll loaded at 0x77f10000 ADVAPI32.dll loaded at 0x77dd0000 RPCRT4.dll loaded at 0x77e70000 Breakpoint 0 hit at wintabtest.d:8 0x402010 thread(5112) void main(char[][] args) ->da wintabtest.d:8 void main(char[][] args) 00402010: 55 push ebp 00402011: 8bec mov ebp, esp wintabtest.d:12 writefln("Getting WTInfo..."); 00402013: ff359c504100 push dword [0x41509c] 00402019: ff3598504100 push dword [0x415098] 0040201f: b800514100 mov eax, 0x415100 00402024: 50 push eax 00402025: e8fe030000 call 0x402428 std.stdio.writefln wintabtest.d:13 WTInfoA(0, 0, null); 0040202a: 6a00 push 0x0 0040202c: 6a00 push 0x0 0040202e: 6a00 push 0x0 00402030: e8d3250100 call 0x414608 _WTInfoA wintabtest.d:14 writefln("Received WTInfo"); 00402035: ff35b4504100 push dword [0x4150b4] 0040203b: ff35b0504100 push dword [0x4150b0] 00402041: b900514100 mov ecx, 0x415100 00402046: 51 push ecx 00402047: e8dc030000 call 0x402428 std.stdio.writefln 0040204c: 31c0 xor eax, eax 0040204e: 83c424 add esp, 0x24 # here, DMD cleans up the stack for all 3 calls at once # note that 0x24 = 9*4 accounts for all the 9 PUSH instructions above # this means also cleaning up for the WTInfoA call wintabtest.obj 00402051: 5d pop ebp 00402052: c3 ret ->ov wintabtest.d:12 0x402013 thread(5112) writefln("Getting WTInfo..."); -> Getting WTInfo... wintabtest.d:13 0x40202a thread(5112) WTInfoA(0, 0, null); # now we're just before the 3 PUSHes for the WTInfo parameters # let's check out the ESP value ->dr EAX = 00000000 EBX = 008903c4 ECX = 0012ff74 EDX = 0000000b EDI = 00000001 ESI = 00000001 EBP = 0012ff30 ESP = 0012ff24 EIP = 0040202a EFL = 00000202 CS = 0000001b DS = 00000023 ES = 00000023 FS = 0000003b GS = 00000000 SS = 00000023 # and step over the PUSHes and the call ->ov wintabtest.d:14 0x402035 thread(5112) writefln("Received WTInfo"); # check ESP again ->dr EAX = 00001157 EBX = 008903c4 ECX = 7ec7fb9c EDX = 10019e38 EDI = 00000001 ESI = 00000001 EBP = 0012ff30 ESP = 0012ff24 EIP = 00402035 EFL = 00000296 CS = 0000001b DS = 00000023 ES = 00000023 FS = 0000003b GS = 00000000 SS = 00000023 # ESP is still 0x12ff24, therefore WTInfo cleaned up the stack by itself We have two calls to variadic D functions (writefln), which don't clean up the stack (see D's ABI docs). Each call has 3 dword PUSHes, which gives us 24 = 0x18 bytes. So the "add esp, 0x24" moves the stack pointer 12 bytes to much. Now consider the other version of Marc's snippet, that saves the return value in a local variable: wintabtest.d:8 void main(char[][] args) 00402010: c8040000 enter 0x4, 0x0 wintabtest.d:20 writefln("Getting WTInfo..."); 00402014: ff359c504100 push dword [0x41509c] 0040201a: ff3598504100 push dword [0x415098] 00402020: b800514100 mov eax, 0x415100 00402025: 50 push eax 00402026: e801040000 call 0x40242c std.stdio.writefln wintabtest.d:21 int value = WTInfoA(0, 0, null); 0040202b: 6a00 push 0x0 0040202d: 6a00 push 0x0 0040202f: 6a00 push 0x0 00402031: e8d2250100 call 0x414608 _WTInfoA 00402036: 8945fc mov [ebp-0x4], eax wintabtest.d:22 writefln("Received WTInfo"); 00402039: ff35b4504100 push dword [0x4150b4] 0040203f: ff35b0504100 push dword [0x4150b0] 00402045: b900514100 mov ecx, 0x415100 0040204a: 51 push ecx 0040204b: e8dc030000 call 0x40242c std.stdio.writefln 00402050: 31c0 xor eax, eax 00402052: 83c424 add esp, 0x24 wintabtest.obj 00402055: c9 leave 00402056: c3 ret The ENTER at the beginning of the function saves the current ESP to EBP (among other things). The "add esp, 0x24" still corrupts the stack pointer, but the LEAVE instruction restores the saved value from EBP, so it doesn't hurt. Now that we know that WTInfo cleans up the stack, we can narrow down the possible calling conventions to __stdcall, __fastcall and PASCAL. Since we were able to link against the library using C convention (after adding leading underscores to the function names using the /s switch on implib), that eliminates __stdcall and __fastcall, because they require more decoration than the leading underscore. So we change the declaration to extern(Pascal) and observe OPTLINK complaining about a missing function "WTINFOA". Apparently DMD's idea of function names in Pascal convention is all uppercase, while wintab32.dll has C-style function names (which you can also see by looking at the DLL file in a text editor). All this is definitely under-the-hood-type debugging. Just because it came up in other threads a couple of times: this is not the only kind of stuff you can do with a debugger :) You don't need to know assembly language to make use of a debugger. You can simply run it to have line numbers for access violations, extensive stack traces or variable contents without having to add printf's and recompile. Debuggers are friendly little creatures, they don't bite :)
Jul 09 2007
parent BLS <nanali nospam-wanadoo.fr> writes:
Thank you Jascha,
allready made a print copy
Since I also have some problems with (better : against)  DLLs your 
Tool/Information is worth a lot.
Bjoern
Jul 09 2007
prev sibling next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Jascha Wetzel wrote:
 the problem is indeed the calling convention.
 
 the version of wintab32.dll i found uses FAR PASCAL. the callee's clean 
 up the stack. if you use extern(C), the caller cleans up the stack. 
 therefore the return address at the end of the main function is 
 incorrect and it crashes.
 this is only the case, though, as long as you don't use any local 
 variables. in this case, D uses the ENTER and LEAVE x86 instructions to 
 create and destroy stack frames. the correct stack pointers (the pointer 
 to the former stack frame) are saved (in EBP) and therefore the 
 incorrect value in ESP (that results from the obsolete cleanup) gets 
 overwritten by the saved value, the correct return address is in place 
 -> no crash.
 
 after looking at the C headers for wintab32.dll, you see that they use 
 the WINAPI = __stdcall = extern(Windows) calling convention only if the 
 win32 headers have been included (WINAPI is defined). else they use FAR 
 PASCAL. since the function names in wintab32.dll do not adhere to the 
 __stdcall convention (leading _ and trailing  X with X being the number 
 of parameter bytes), they must be using FAR PASCAL.
 
 DMD's extern(Pascal) calling convention expects all uppercase function 
 names. therefore you'll have to specify the /IGNORECASE option to 
 optlink. not that FAR PASCAL has to leading underscore in the function 
 names (as opposed to __cdecl or extern(C)).
 
 putting it all together:
 - use implib without the /s switch (no leading underscores)
 implib wintab32.lib \WINDOWS\system32\Wintab32.dll
 
 - declare all imports as extern(Pascal)
 extern(Pascal) UINT WTInfoA(UINT, UINT, LPVOID);
 
 - pass /IGNORECASE to optlink to compensate for case sensitivity in 
 wintab32.dll
 dmd wintab32.lib wintabtest.d -L/IGNORECASE

But using /IGNORECASE means you can no longer have any functions in your app anywhere that are different only in case? I guess it's not that huge a problem with name mangling. If you have CoolThing and coolthing they're not likely to both be functions... but still. Expecting extern(Pascal) to be all uppercase seems like a bug if that's not really how extern(Pascal) libraries do things in practice. --bb
Jul 09 2007
parent Jascha Wetzel <firstname mainia.de> writes:
Bill Baxter wrote:
 But using /IGNORECASE means you can no longer have any functions in your 
 app anywhere that are different only in case?
 
 I guess it's not that huge a problem with name mangling.  If you have 
 CoolThing and coolthing they're not likely to both be functions... but 
 still.
 
 Expecting extern(Pascal) to be all uppercase seems like a bug if that's 
 not really how extern(Pascal) libraries do things in practice.
 
 --bb

DMD does it correctly. Given Pascal's case insensitivity in source it makes sense. "Function name decoration consists of conversion to uppercase" http://blogs.msdn.com/oldnewthing/archive/2004/01/02/47184.aspx
Jul 09 2007
prev sibling next sibling parent =?ISO-8859-15?Q?Marc_M=FCller?= <dont spam.me> writes:
Yeah you are awesome! You made my day!

Not only that you solved the problem (which I probably never had solved 
on my own) - now I also have a useful debugger on hand :-)
And of course - a working one... I was a bit scared, that (unpatched) 
gdb did not work with dmd-generated code... And windbg... Well - it's 
Microsoft, so I was not scared at all, but they approved again my 
prejudices...


Thank you also for your detailed informations about the calling 
conventions and the asm-explanations (in the later posting). Since my 
last asm-programming experience was before the protected-mode era it was 
very useful and interesting for me.


Tomorrow I will try to get Code::Blocks work with Ddbg.
Again, thank you a lot!

	-Marc-
Jul 09 2007
prev sibling parent reply =?ISO-8859-15?Q?Marc_M=FCller?= <dont spam.me> writes:
Sorry - there is still a problem...

At first:
I downloaded Code::Blocks and installed the D-Compiler and the 
Ddbg-Debugger.
After downloading - it took me less than 5 minutes to get everything 
working. That's the way it should be ;-)

Now back to the problem:
When using extern(Pascal) the stack seems to be clean.
And the function I used in former news works flawless (because I passed 
three times a zero-value)

Now I tried to call the external functions with real parameters.
Then again some very strange bugs occurred, but thanks to your debugger 
I found the reason quickly:

The program pushes the parameters in reversed order on the stack.
I wrote some lines of inline-assembler to reverse the order manually:


=============================================================
int result = WTInfoA(WTI_DEVICES, i, buffer);
asm
{
     nop;

     push dword ptr buffer;
     push i;
     push WTI_DEVICES;

     call WTInfoA;
     mov result, EAX;
}
-------------------------------------------------------------
Compiles to:
-------------------------------------------------------------
push 0x64
push dword [ebp-0xc]
push dword [ebp-0x10]
call 0x414748	WTINFOA
mov [ebp-0x8], eax

nop
push dword [ebp-0x10]
push dword [ebp-0xc]
push 0x64
call 0x414748	WTINFOA
mov [ebp-0x8], eax
=============================================================

When I use the asm-code the function is called correctly.

Do you know a calling convention or compiler/linker switch to tell D to 
let the callee clean up the stack like in extern(Pascal), but push the 
parameters in reverse order on the stack?

Kind regards,
	-Marc-



Jascha Wetzel schrieb:
 the problem is indeed the calling convention.
 
 the version of wintab32.dll i found uses FAR PASCAL. the callee's clean 
 up the stack. if you use extern(C), the caller cleans up the stack. 
 therefore the return address at the end of the main function is 
 incorrect and it crashes.
 this is only the case, though, as long as you don't use any local 
 variables. in this case, D uses the ENTER and LEAVE x86 instructions to 
 create and destroy stack frames. the correct stack pointers (the pointer 
 to the former stack frame) are saved (in EBP) and therefore the 
 incorrect value in ESP (that results from the obsolete cleanup) gets 
 overwritten by the saved value, the correct return address is in place 
 -> no crash.
 
 after looking at the C headers for wintab32.dll, you see that they use 
 the WINAPI = __stdcall = extern(Windows) calling convention only if the 
 win32 headers have been included (WINAPI is defined). else they use FAR 
 PASCAL. since the function names in wintab32.dll do not adhere to the 
 __stdcall convention (leading _ and trailing  X with X being the number 
 of parameter bytes), they must be using FAR PASCAL.
 
 DMD's extern(Pascal) calling convention expects all uppercase function 
 names. therefore you'll have to specify the /IGNORECASE option to 
 optlink. not that FAR PASCAL has to leading underscore in the function 
 names (as opposed to __cdecl or extern(C)).
 
 putting it all together:
 - use implib without the /s switch (no leading underscores)
 implib wintab32.lib \WINDOWS\system32\Wintab32.dll
 
 - declare all imports as extern(Pascal)
 extern(Pascal) UINT WTInfoA(UINT, UINT, LPVOID);
 
 - pass /IGNORECASE to optlink to compensate for case sensitivity in 
 wintab32.dll
 dmd wintab32.lib wintabtest.d -L/IGNORECASE
 
 
 i used Ddbg to hunt this down in a very short amount of time, btw ;)
 
 Marc Müller wrote:
 Is it possible you're doing extern(C) when you need to be doing 
 extern(Window), or vice versa?

 I don't have much experience with that so I'm just throwing it out. 
 Maybe it just won't link if you get that wrong.

With extern(Windows) the program does not link :-( I simplified the program and wrote it to digitalmars.D.learn ("Problems when using DLL-Functions") as Bjoern suggested. Again - thanks a lot for your help! -Marc-


Jul 10 2007
parent reply torhu <fake address.dude> writes:
Marc Müller wrote:
 Do you know a calling convention or compiler/linker switch to tell D to 
 let the callee clean up the stack like in extern(Pascal), but push the 
 parameters in reverse order on the stack?
 


Sounds like you're describing stdcall. You could use extern (Pascal) only to be able to link with the symbol, then assign the address of the function to an extern (Windows) pointer, and call it through that. This is all in theory, of course. --- typedef extern (Windows) UINT function(UINT , UINT , LPVOID) WTInfoA_type; extern (Pascal) UINT WTInfoA(UINT , UINT , LPVOID); WTInfoA_type WTInfoA_ptr; void main() { WTInfoA_ptr = cast(WTInfoA_type)&WTInfoA; WTInfoA_ptr(/* args here*/); } --- Untested code, but it looks plausible.
Jul 10 2007
parent Jascha Wetzel <firstname mainia.de> writes:
yep, sounds like __stdcall without decoration.

alternatively, you can write a .def file (if you don't have it already), 
use aliases with decorated names and create the .lib file from that.
there should be plenty of resources on the net on how to do this.

torhu wrote:
 Sounds like you're describing stdcall.
 
 You could use extern (Pascal) only to be able to link with the symbol,
 then assign the address of the function to an extern (Windows) pointer,
 and call it through that.  This is all in theory, of course.
 
 ---
 typedef extern (Windows) UINT function(UINT , UINT , LPVOID) WTInfoA_type;
 
 extern (Pascal) UINT WTInfoA(UINT , UINT , LPVOID);
 
 WTInfoA_type WTInfoA_ptr;
 
 void main()
 {
    WTInfoA_ptr = cast(WTInfoA_type)&WTInfoA;
    WTInfoA_ptr(/* args here*/);
 }
 ---
 
 Untested code, but it looks plausible.

Jul 10 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Marc Müller wrote:
 Hello,
 
 as my first Project in D I am going to write a Application for my Wacom 
 graphic tablet.
 
 So i have to use a windows dll(wintab32.dll) - but i don't have a 
 corresponding lib-file.
 
 I have converted the C-Header I found with the dev-kit of my graphic 
 tablet and converted it to D.
 
 I tried to use the "export(C)" or the "export(Windows)" settings in the 
 converted header file.
 
 Example from the converted header -> wintab.d:
 extern(Windows)
 {
     UINT  WTInfoA(UINT , UINT , LPVOID );
 }
 
 
 
 demo.d:
 import std.stdio;
 import wintab;
 
 void main(char[][] args)
 {
     writefln("Hello World\n");
     WTInfo( 0, 0, null);
 }
 
 
 
 When i compile my Hello-World Application which is calling a function 
 from the DLL i get the following errormessages from the compiler (using 
 "extern (C)"):
 
 
 dmd.exe demo.d -LC:/WINDOWS/system32/wintab32.dll
 d:\Projects\wacom\dmd\bin\..\..\dm\bin\link.exe 
 demo,,,user32+kernel32/noiC:/WINDOWS/system32/wintab32.dll;
 OPTLINK (R) for Win32  Release 7.50B1
 Copyright (C) Digital Mars 1989 - 2001  All Rights Reserved
 
 OPTLINK : Warning 9: Unknown Option : NOIC
 OPTLINK : Warning 9: Unknown Option : WINDOWS
 OPTLINK : Warning 9: Unknown Option : SYSTEM32
 OPTLINK : Warning 9: Unknown Option : WINTAB32.DLL
 demo.obj(demo)
  Error 42: Symbol Undefined _WTInfoA 12
 
 
 
 With "extern(Windows)" the last line reads:
  Error 42: Symbol Undefined _WTInfoA
 
 
 I don't know what to do - and I can not find usefull information for 
 using Win32-DLLs without a lib-file.
 Are my compile settings correct?
 Is the converted header correct?
 Or is it just not possible using a DLL without a library?
 
 Please give me a hint
 
 Thanks
     -Marc-

That's great. I hope you'll be releasing this when you get it working. I wrote a wintab wrapper for C++ that I'd like to port to D some day. http://www.billbaxter.com/projects/bbtablet As torhu said, you can create a D-compatible import lib from the DLL with implib from the "BUP" ... although the command should be: implib /s wintab32.dll wintab32.lib And I like to name the dmd-specific import libs something like wintab32_dmd.lib just so I know it's not a normal Microsoft import lib. --bb
Jul 08 2007
parent =?ISO-8859-15?Q?Marc_M=FCller?= <dont spam.me> writes:
 That's great.  I hope you'll be releasing this when you get it working. 
  I wrote a wintab wrapper for C++ that I'd like to port to D some day.
   http://www.billbaxter.com/projects/bbtablet

*g* that's funny... I digged through your bbtablet-code just a few hours before I wrote the former article, just to learn a bit more about the wintab-api. Since it's just a hobby for me I will also release the tool when it's done. But at the moment i'm more twiddling with D than really working on it... By the way - you have quite impressive and interesting graphic-projects and papers on you homepage. Good work! -Marc-
Jul 08 2007