views:

690

answers:

4

I've compiled a DLL in Visual Studio (the source code is in C++, which I barely understand). Here's a piece of Scraper.h:

struct SWin
{
   char title[512];
   HWND hwnd;
};

SCRAPER_API bool ScraperGetWinList(SWin winList[100]);

Now I'm trying to use the above function in my Delphi application:

type
  tWin = record
    title: String;
    hwnd: HWND;
  end;

function ScraperGetWinList(var WinList: Array of tWin): Boolean; external 'Scraper.dll';

var
  myWinList: Array [1..100] of tWin;

procedure TMainForm.GetWinListButtonClick(Sender: TObject);
begin
  ScraperGetWinList(myWinList);
  ...

The project doesn't compile, and I get the following message: The procedure entry point ScraperGetWinList could not be located in the dynamic link library: Scraper.dll.

What am I doing wrong?

+4  A: 

From my Linux experience, I'd say that you've encountered so-called "name-mangling" issue. The entry point of your procedure is not called "ScraperGetWinList", but something like "_ZN18ScraperGetWinListEpN4SWin".

The thing is that, Unlike in C, in C++ language the name of entry point is not the same as the function name. No wonder: assume, you have a set of overloaded functions; they should have different entry points in your DLL. That's where name mangling comes into play.

The most common solution to this problem is to define interface of your library in such a way that it will use C calling convention. No name mangling will happen with the interface functions then.

Note that you don't have to write the whole library in C, you only should declare functions for them to emit C-like entry points.

Usually it's written like this:

extern "C" {

SCRAPER_API bool ScraperGetWinList(SWin winList[100]);
// More functions

}

Recompile your library and use it in Delphi without problems.

Note, that you should also adjust calling conventions (stdcall or cdecl) for them to match in your C++ header and Delphi code. However, that's best explained in another question.

Pavel Shved
+2  A: 

Also make sure that same calling convention is used in c++ and delphi. Take a look at jn answer here and here

beermann
Thanks for the tip. I changed the declaration to:function ScraperGetWinList(var WinList: tWinList): Boolean; stdcall; external 'Scraper.dll';When I run the application, after a moment or two I receive an access violation message. Any quick ideas why that might happen, or should I ask a separate question?
Mikhail
Mikhail, ask a separate question for that, where you re-state exactly what declarations you're using and can describe exactly what you do to reproduce the error. "Wait a few moments" isn't good enough.
Rob Kennedy
+3  A: 

Name mangling is most likely the problem. Name mangling is usually done is C++ code, and when writing a DLL in C++ that should be used by code in an other langauge, you should use the Extern "C" construction as Pavel Shved already suggested.

When using DLLs, especially when writtin in other languages, you should also keep an eye on calling conventions. I suggest that you specify in both delphi and c++ to use the stdcall calling convenstion. This is the calling convention also used by the windows api, so it guarantees the best interoperatability between different compilers.

This would mean

extern "C" {

SCRAPER_API __stdcall bool ScraperGetWinList(SWin winList[100]);

}

and

function ScraperGetWinList(var WinList: Array of tWin): Boolean; external 'Scraper.dll';

But that's not all, the stdcall calling convention has an impact on the name mangling, and it would turn out to be something like _ScraperGetWinList@4 (Where 4 is the size of the parameter, where an array would have a pointer to the first element, so 4 bytes)

To confirm the correct symbols to use, I suggest Dependency Walker ( http://www.dependencywalker.com/ ) this program shows that exactly the function names are exported by the dll. Having confirmed the name to be '_ScraperGetWinList@4' then you add this in delpgi like this:

function ScraperGetWinList(var WinList: Array of tWin): Boolean; external 'Scraper.dll' name '_ScraperGetWinList@4';

andremo
A: 

Have you actually exported the entry point function in the c++ code? This really stumped me the first time I compiled a C++ dll in Visual Studio for use in a dotnet app.

For example, I needed to expose a print driver in unmanaged code so some other developers could access it in VB.net. This is what I did.

In foo.cpp:

extern "c" {
  ___declspec(dllexport) bool FooBar()
  {
     // Call some code on my cpp objects to implement foobar
  }
}

Then in a file called foo.def:

LIBRARY   "mylib"

EXPORTS
        FooBar 
        AnyOtherFunctionsItExports

This is how I got it to work. I might not be doing things the best possiable way. I am a little light on C++ experience and also mainly don't work on windows.

Guy van den Berg