Using a DLL

General FreeBASIC programming questions.
Post Reply
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Using a DLL

Post by jj2007 »

Looks simple and works like a charm:

Code: Select all

#include "crt.bi"
Dim s As zstring ptr
Dim as integer stack
Declare Function GetInput cdecl LIB "ConsoleInput" (ByRef prompt As zstring, ByRef prefill As zstring) As zString ptr
s=GetInput("What's your hobby? ", "programming")  ' the second string is editable; cursor left/right, backspace, Escape to clear etc
printf("My hobby is %s", s)
sleep
The DLL is here. Now to the problem:

Since the moment when I first compiled this snippet using another name, i.e. LIB "MbMicroDll", the executable built with ConsoleInput complains that it cannot find MbMicroDll, even though the source clearly tells it to use ConsoleInput.

This is weird. For compiling, ConsoleInput.dll must be present (and only that one), for running I need MbMicro.dll (and only that one). I have the impression that the compiler "remembers" the first name used, which makes no sense. I even searched the registry, no luck. I have been fighting with this the whole night...

I use DLLs all the time (on Windows 7 & Win 10), this is my first attempt at using a DLL in FreeBasic, and well, it works like a charm but getting it to work requires really weird acrobatics. Any idea why the compiler behaves like this? Please don't point me to static linking, def files or dylibload & friends - the code above works fine, I simply want to know why the FB compiler confuses the names of DLLs.
marcov
Posts: 3462
Joined: Jun 16, 2005 9:45
Location: Netherlands
Contact:

Re: Using a DLL

Post by marcov »

Probably the import library contains the old name?
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Using a DLL

Post by jj2007 »

I was about to write "there is no import library" but then decided to open the ConsoleInput.dll in a text editor. The string "MbMicro.dll" is there, so it seems that somehow it keeps a reference to the older name. Why that is, no idea. And in other languages it isn't a problem, LoadLibrary does its job. Hmmmm...
marcov
Posts: 3462
Joined: Jun 16, 2005 9:45
Location: Netherlands
Contact:

Re: Using a DLL

Post by marcov »

What I don't understand is why you are so sure it is FB. Search all FB generated .o's for the old name otherwise

It could also be the DLL itself, maybe the dll opens itself in a clumsy way for resources.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: Using a DLL

Post by dodicat »

Are you manually deleting any previously created .exe file before re compiling?
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Using a DLL

Post by jj2007 »

marcov wrote:What I don't understand is why you are so sure it is FB. Search all FB generated .o's for the old name otherwise
There are no object files around.
dodicat wrote:Are you manually deleting any previously created .exe file before re compiling?
Yes. I am testing the freshly built exe (I try changing the prompt to see if it's really new...), and the exe contains the old DLL name.

Only FreeBasic behaves like this. Here is the plain C equivalent, it works fine:

Code: Select all

#include <stdio.h>
#include <windows.h>
#include <conio.h>
#pragma comment(linker, "/subsystem:console" )
int main(void) {
  HINSTANCE pLib=LoadLibrary("ConsoleInput.dll");
  if (pLib) {
	char* s;
	typedef char* (__stdcall *MyInput)(char* prompt, char*prefill);
	MyInput pAlgo=GetProcAddress(pLib, "GETINPUT");
	s=pAlgo("What's your hobby? ", "programming in Basic");
	printf("My hobby is %s", s);
	FreeLibrary(pLib);
  } else {
	printf("lib not found");
  }
  _getch();	// wait for Return
}
Same in assembler:

Code: Select all

include \masm32\MasmBasic\MasmBasic.inc
  Init
  Dll "ConsoleInput.dll"
  Declare input, C:2 Alias "GETINPUT"
  MsgBox 0, input("What's your hobby? ", "programming in Basic"), "That's my hobby:", MB_OK
EndOfCode
St_W
Posts: 1626
Joined: Feb 11, 2009 14:24
Location: Austria
Contact:

Re: Using a DLL

Post by St_W »

You are not providing an import library for your dll file, thus FreeBasic (or the GNU Linker "ld", to be more precise) creates a default one for you. And this obviously respects the library name as defined in the DLLs export directory (using dumpbin ConsoleInput.dll /exports):

Code: Select all

Dump of file ConsoleInput.dll

File Type: DLL

  Section contains the following exports for MbMicroDll.dll

    00000000 characteristics
    5BF8B70B time date stamp Sat Nov 24 03:27:23 2018
        0.00 version
           1 ordinal base
           8 number of functions
           8 number of names

    ordinal hint RVA      name

          4    0 0000100A GETINPUT
          5    1 0000103B InstrDLL
          1    2 00008000 MyDwords
          6    3 0000800C MyQwords
          7    4 00008034 MyReal10
          8    5 0000801C MyReal8
          2    6 00001040 PrintR8
          3    7 0000803E _DwDeco@0
To fix the faulty DLL you need to create an import library yourself, containing the correct library name. To do so create a DEF file with the required imports (and make sure to use the correct library name), like this:

Code: Select all

LIBRARY ConsoleInput.dll
EXPORTS
  GETINPUT @4
  InstrDLL @5
  MyDwords @1
  MyQwords @6
  MyReal10 @7
  MyReal8 @8
  PrintR8 @2
  _DwDeco@0 @3
To create an import library from the DEF file run
if using MSVC toolchain:
lib /def:ConsoleInput.def /out:ConsoleInput.lib /machine:x86
if using MinGW toolchain:
dlltool -d ConsoleInput.def -l libConsoleInput.dll.a

You'll get a ConsoleInput.lib or libConsoleInput.dll.a file, which is the import library and will be used during linking. When you now compile your application FreeBasic uses the import library you provided (including the library name stored there). The EXE will use the correct library name (as specified in the import library) in its import directory now. Verify using dumpbin dllname.exe /imports:

Code: Select all

Dump of file dllname.exe

File Type: EXECUTABLE IMAGE

  Section contains the following imports:

    ConsoleInput.dll
                410188 Import Address Table
                410064 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                    4 GETINPUT
btw, LoadLibrary() is a different method of loading DLLs. In this example the imported method is directly added to the executable's import table and thus loaded by the operating system's loader during application initialization phase. When using LoadLibrary() the DLL is loaded when the application is already running. You won't need an import library or the DLL itself at compile time of the application in that case. Of course you can use LoadLibrary in FreeBasic too (or better: FB's built-in platform independent DyLibLoad).
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: Using a DLL

Post by jj2007 »

St_W wrote:You are not providing an import library for your dll file, thus FreeBasic (or the GNU Linker "ld", to be more precise) creates a default one for you. And this obviously respects the library name as defined in the DLLs export directory (using dumpbin ConsoleInput.dll /exports)
Thanks a lot for explaining this behaviour. I would drop the "obviously", though - the linker might as well use the file name of the DLL, or at least check if there is a conflict between file and library name, and issue a warning.
St_W
Posts: 1626
Joined: Feb 11, 2009 14:24
Location: Austria
Contact:

Re: Using a DLL

Post by St_W »

jj2007 wrote:Thanks a lot for explaining this behaviour. I would drop the "obviously", though - the linker might as well use the file name of the DLL, or at least check if there is a conflict between file and library name, and issue a warning.
Sorry, used the wrong word there, I meant "apparently".
Post Reply