5

I need to rewrite some Delphi code to C++ and we need to link to the dynamic library TMLComm2004.dll. It turns out that we don't have any .lib file so we decided to generate it, using the following command lines:

dumpbin /EXPORTS C:\Users\fayard\Desktop\TMLComm2004.dll > C:\Users\fayard\Desktop\TMLComm2004.txt

We obtain the following file

Microsoft (R) COFF/PE Dumper Version 14.00.24215.1
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file C:\Users\fayard\Desktop\TMLComm2004.dll

File Type: DLL

  Section contains the following exports for TMLcomm.dll

    00000000 characteristics
    401F6AD5 time date stamp Tue Feb  3 10:33:09 2004
        0.00 version
          1 ordinal base
          27 number of functions
          27 number of names

    ordinal hint RVA      name

          1    0 00001122 _MSK_COFFDownloadFlash@16
          2    1 0000114F _MSK_COFFDownloadRAM@20
          3    2 0000106E _MSK_CheckSum@12
          4    3 00001172 _MSK_CloseComm@0
          5    4 00001190 _MSK_GetActiveBoard@4
          6    5 0000109B _MSK_GetBoardInfo@8
          7    6 0000104B _MSK_GetBytesCountInQueue@0
          8    7 0000119A _MSK_GetChar@4
          9    8 000010E6 _MSK_GetCharNoWait@4
         10    9 000011A4 _MSK_GetCommBaudRate@4
         11    A 000010DC _MSK_GetHostBoard@4
         12    B 0000108C _MSK_OpenComm@16
         13    C 000011DB _MSK_ReceiveData@16
         14    D 000011CC _MSK_ReceiveMessage@4
         15    E 0000105A _MSK_RegisterReceiveUnsolicitedMsgHandler@4
         16    F 000011AE _MSK_ResetMSKBoard@0
         17   10 00001037 _MSK_SendChar@4
         18   11 000010EB _MSK_SendData@16
         19   12 0000126C _MSK_SendMessage@4
         20   13 0000128F _MSK_SetActiveBoard@4
         21   14 00001136 _MSK_SetB0BlockAsData@4
         22   15 0000100A _MSK_SetBoardBaudRate@4
         23   16 00001019 _MSK_SetCharMode@4
         24   17 00001168 _MSK_SetCommBaudRate@4
         25   18 00001050 _MSK_SetDebugWindow@8
         26   19 00001276 _MSK_SetHostBoard@4
         27   1A 00001046 _MSK_UpdateCommTimeouts@0

  Summary

        C000 .data
        3000 .idata
        6000 .rdata
        4000 .reloc
       21000 .rsrc
       3E000 .text
        2000 shdata

Then, we have edited the TMLComm2014.txt file and changed it to a .def file with the @xx removed. The file looks like:

EXPORTS
_MSK_COFFDownloadFlash
...

Then, we generate a .lib file with:

lib /def:C:\Users\fayard\Desktop\TMLComm2004.def /out:C:\Users\fayard\Desktop\TMLComm2004.lib

We compile the following program:

extern "C" {
  bool MSK_OpenComm(unsigned char a, unsigned short b, bool c, unsigned char d);
}

...

When we compile and link against the .lib, we get an error:

LNK2019 unresolved external symbol _MSK_OpenComm referenced in function...

What's wrong with what I do?

Update

I have managed to compile with:

extern "C" {
  bool __stdcall MSK_OpenComm(unsigned char a, unsigned short b, bool c, unsigned char d);
}

but now the problem is at link time. I am puzzled as from command line, I have:

==================================================
dumpbin /headers TMLComm.lib

  Version      : 0
  Machine      : 14C (x86)
  TimeDateStamp: 5964C365 Tue Jul 11 14:24:05 2017
  SizeOfData   : 00000022
  DLL name     : TMLComm2004.dll
  Symbol name  : __MSK_OpenComm@16
  Type         : code
  Name type    : no prefix
  Hint         : 11
  Name         : _MSK_OpenComm@16

==================================================
dumpbin /exports TMLComm.dll

         12    B 0000108C _MSK_OpenComm@16

==================================================

Compilation goes fine now, but the problem is at link time:

link tmlcomm.obj Motor.obj main.obj TMLComm.lib /out:main.exe


Microsoft (R) Incremental Linker Version 14.00.24215.1
Copyright (C) Microsoft Corporation.  All rights reserved.

tmlcomm.obj : error LNK2019: unresolved external symbol _MSK_OpenComm@16 referenced in function "bool __cdecl msk_opencomm(unsigned char,unsigned short,bool,unsigned char)" (?msk_opencomm@@YA_NEG_NE@Z)
Teclis.exe : fatal error LNK1120: 1 unresolved externals
InsideLoop
  • 6,063
  • 2
  • 28
  • 55
  • IMPLIB should import these functions from the DLL. But this looks like a VC++ (or compatible) DLL. That means you'll have to handle the decorations: [See here](http://bcbjournal.org/articles/vol4/0012/Using_Visual_C_DLLs_with_CBuilder.htm). – Rudy Velthuis Jul 11 '17 at 09:54
  • "Unresolved external symbol ..." You left out the interesting part: which symbols are reported as undefined? Could linking to MSVCRT.dll help? – Rudy Velthuis Jul 11 '17 at 09:57
  • @Rudy: _MSK_OpenComm is reported as undefined – InsideLoop Jul 11 '17 at 10:05
  • Probably because it is exported as `_MSK_OpenComm@16`, not as `_MSK_OpenComm`. `.def` files are only considered when the C++ code is compiled and linked, not when it is imported. – Rudy Velthuis Jul 11 '17 at 10:25
  • @Rudy: That might be the reason. But I have no idea how to call a function with an @ in its name. It is forbidden by C/C++. – InsideLoop Jul 11 '17 at 10:27
  • Take a look at the link I posted in my comment. – Rudy Velthuis Jul 11 '17 at 10:27
  • You could always dynamically import the functions, using `LoadLibrary`and `GetProcAddress`, i.e. without an import lib. – Rudy Velthuis Jul 11 '17 at 10:29
  • @Rudy. I have managed to generate a `.def` file that translates the mangled names to the C names. Now it compiles, but when I run the program I get: The procedure entry point MSK_SetBoardBaudRate could not be located in the dynamic link library C:\...\Main.exe . Why is he looking the .exe as a dynamic library? – InsideLoop Jul 11 '17 at 11:46
  • hard to tell from here, sorry. But it seems to be looking for `MSK_SetBoardBaudRate`, while the DLL exports `_MSK_SetBoardBaudRate@4`. That it reports the wrong executable is not so important, fact is that you still have the same problem (but now at runtime): it can't find the proper exported names. Try dynamic linking instead. There, you can give the proper name as a string and assign it to any suitable function pointer. No problems with decoration anymore. – Rudy Velthuis Jul 11 '17 at 12:25
  • FWIW, how did the Delphi code link to the DLL? – Rudy Velthuis Jul 11 '17 at 12:29
  • @Rudy: It was declared that way: `function MSK_OpenComm; external k_TMLCommName name '_MSK_OpenComm@16';` – InsideLoop Jul 11 '17 at 12:43
  • In Delphi, it is quite simple, indeed. In C++Builder, that is not so simple. Does your .def file have entries like `MSK_SetBoardBaudRate=_MSK_SetBoardBaudRate@4` ? That should actually work. – Rudy Velthuis Jul 11 '17 at 12:48
  • @Rudy: I have tried that but it did not work. But using `__stdcall` in the C files seems to look for the right function. Now it complains about not finding it (check the updated original question). – InsideLoop Jul 11 '17 at 12:59

1 Answers1

1

Have a look at Microsoft's documentation of decorated names. For C, a leading underscore and a trailing @<number> denotes the stdcall calling convention, which is different from the standard cdecl.

So, you should just modify your declarations for all functions where the symbol ends with @<number>:

extern "C" {
  __stdcall bool MSK_OpenComm(unsigned char a, unsigned short b, bool c, unsigned char d);
}

I'm not sure whether stripping the @<number> part from the .def file is the right thing to do, I doubt it, but just try it out.

From my experiment with MinGWs tool for creating a .def file (gendef), the name in the .def should not include the leading underscore, but it should include the @<number> part at the end. So for this example function, the .def should have the following line:

MSK_OpenComm@16

An alternative to manually creating these required files are the tools that come with MinGW. You could just do the following:

gendef foobar.dll                     # generates the .def
dlltool -d foobar.def -l foobar.lib   # generates the import library

Based on your comments, you might need to manually edit the .def file gendef is giving you to contain lines like this:

MSK_OpenComm@16=_MSK_OpenComm@16

It seems that gendef could be confused because the dll exports decorated names which isn't expected.


When using an import library, this will have "static stubs" named like the actual exported symbols of the DLL, but with __imp__ prepended. To account for this, you need yet another attribute in your declarations:

extern "C" {
  __declspec(dllimport) __stdcall bool MSK_OpenComm(unsigned char a, unsigned short b, bool c, unsigned char d);
}
  • That's cool if you are **writing** the DLL, but not if you are merely consuming an existing DLL written in VC++ from a different compiler. The DLL could have been written with a .def file giving nice and clean undecorated names, but it wasn't. This won't do much for the C++Builder code consuming the DLL. – Rudy Velthuis Jul 11 '17 at 12:31
  • Of course it will. The `__stdcall` in the **declaration** you use will tell the compiler for what kind of symbol to look and how to call it. -- but I overlooked something different I guess ... (and edited) –  Jul 11 '17 at 12:34
  • @RudyVelthuis if this is true, the compiler is unusable for the windows platform. I could imagine the actual attributes might look different from what I have in my answer (which works for MSVC as well as gcc and clang on windows) -- but there **has** to be a way to declare functions as `stdcall` and use them, even when the DLL was compiled with a different compiler. The win32 ABI mandates that. –  Jul 11 '17 at 12:57
  • @Felix: Thanks for your help. I have tried to follow your path but I am stuck at a later position. The description is in the updated original question. – InsideLoop Jul 11 '17 at 13:01
  • @Felix: no it isn't. The Win32 ABI has undecorated names, even when they are __stdcall. Many 3rd parties also generate undecorated names using a .def file (and that will work in VC++ as well as in C++Builder). Only those that generate names with underscore and at-num pose some problems (or at least more work) for C++Builder users. – Rudy Velthuis Jul 11 '17 at 13:01
  • @RudyVelthuis I don't see where the question relates to C++Builder, but anyways, although you can do any symbol naming with .def files and import libraries, it's just not useful if your tools don't support what's standard on the platform. –  Jul 11 '17 at 13:14
  • @Felix: I have removed the underscore. Now the link goes fine, but the program does not run. It says: "The procedure entry point MSK_OpenComm@16 could not be located in the dynamic link library C:\...\Main.exe" – InsideLoop Jul 11 '17 at 13:18
  • @Felix: I am confused too by this message – InsideLoop Jul 11 '17 at 13:22
  • @Felix: I have tried with __declspec too. Same problem. – InsideLoop Jul 11 '17 at 13:25
  • @Felix: This was a type. I was linking the `.lib`. The problem is now when I run the program. As said above, it seems to believe that `Main.exe` is a dynamic library which is strange. – InsideLoop Jul 11 '17 at 13:50
  • @Felix: It does not work. I have tried to generate the .def and .lib from Minw 32 bit (I am using a 32-bit library), and with __declspec(dllimport) it just says that it can't find the `__imp__...` symbol when linking. – InsideLoop Jul 11 '17 at 14:01
  • @InsideLoop check `nm TMLComm.lib`, it should show a symbol `00000000 I __imp__MSK_OpenComm@16`. Did you use the command lines for `gendef` and `dlltool` I showed in my answer? –  Jul 11 '17 at 14:05
  • @Felix: Yes, I have used the commands you have used. When running `nm TMLComm2004.lib`, I get `00000000 I __imp___MSK_OpenComm@16@16`, with 3 `_` after the imp and 2 `@16`. Just so you know, the program is then linked with Visual Studio 2015. – InsideLoop Jul 11 '17 at 14:10
  • @Felix: No, the `.def` contains `_MSK_OpenComm@16@16` – InsideLoop Jul 11 '17 at 14:14
  • @InsideLoop added another edit, my answer is now ultimatively patchwork -- you seem to have an unusual situation here ;) –  Jul 11 '17 at 14:20
  • @Felix: I have tried your suggestion, but no more luck – InsideLoop Jul 11 '17 at 14:38
  • @InsideLoop did you check the resulting .lib with `nm`? It **must** have a symbol `I __imp__MSK_OpenComm@16`. In the `.def`, the string after the equals sign is the symbol exported by the DLL. [See the documentation here](https://msdn.microsoft.com/en-us/library/hyx1zcd3.aspx), instead of the name, you could also use the "ordinal number". –  Jul 11 '17 at 14:41
  • @Felix: Yes, it does have this symbol. – InsideLoop Jul 11 '17 at 14:46
  • @Felix: Aren't you puzzled about the fact that it seems to take my `Main.exe` as a dynamic library? – InsideLoop Jul 11 '17 at 14:50
  • @InsideLoop not if you *don't* use `__declspec(dllimport)`. You *have* to use it, linking on windows works in two tiers... –  Jul 11 '17 at 15:01
  • @Felix: I use __declspec(dllimport) now. But it does not make a difference. – InsideLoop Jul 11 '17 at 15:13
  • It's **impossible** this doesn't make a difference. You can't link a DLL without an import library, and the `__declspec(dllimport)` on the declaration of a function tells the linker what kind of symbol to look for (the one prefixed `__imp__`). If you can link successfully but get a problem at runtime, and you actually used `__declspec(dllimport)`, something was still wrong in the .def file the import library was generated from. See my earlier comment with a link to the documentation. –  Jul 11 '17 at 15:28