There's a function, written in C++ and compiled as DLL, which I want to use in my Delphi application.
Scraper.cpp:
SCRAPER_API bool ScraperGetWinList(SWin winList[100])
{
iCurrWin=0;
memset(winList,0,100 * sizeof(SWin));
return EnumWindows(EnumProcTopLevelWindowList, (LPARAM) winList);
}
Scraper.h:
#ifdef SCRAPER_EXPORTS
#define SCRAPER_API __declspec(dllexport)
#else
#define SCRAPER_API __declspec(dllimport)
#endif
struct SWin
{
char title[512];
HWND hwnd;
};
extern "C" {
SCRAPER_API bool ScraperGetWinList(SWin winList[100]);
}
This is how I declare the function in the Delphi application:
type
tWin = record
Title: Array [0..511] of Char;
hWnd: HWND;
end;
tWinList = Array [0..99] of tWin;
function ScraperGetWinList(var WinList: tWinList): Boolean; stdcall; external 'Scraper.dll';
The function works, but when it's finished, I receive Debugger Fault Notification: Project ... faulted with message: ''access violation at 0x0012f773: write of address 0xffffffc0'. Process Stopped. Use Step or Run to continue.
If I add __stdcall
(after SCRA开发者_StackOverflowPER_API bool
) in Scraper.cpp and Scraper.h, then the Delphi application doesn't start at all: The procedure entry point ScraperGetWinList could not be located in the dynamic link library Scraper.dll.
You need to put __stdcall
after bool
. The complete declaration, after all macros expand, should look like this:
extern "C"
{
__declspec(dllexport)
bool __stdcall ScraperGetWinList(SWin winList[100]);
}
EDIT: Looks like you'll also need a .def file there. It's a file that lists every function exported in the DLL, and in this case it's needed only to force C++ compiler not mangle the exported names. Contents would be this:
EXPORTS
ScraperGetWinList
I'm not sure which C++ compiler you're using, but normally you'd just specify the .def file along with .cpp; for example, the following works for VC++:
cl.exe foo.cpp foo.def
Also, you will need to tell Delphi to use stdcall as well, by inserting stdcall
keyword right before external
in your Delphi function declaration.
If you use a packed array[1..512] of char you will not need the ConvertToString() function.
"packed array of char" is assignment compatible with Delphi string (this goes back to very early forms of Pascal - packed array of char WAS the string type). You nmay need to scab the result for a null ($0) char to find the end of the C-string
Also what Delphi version are you using? if Delphi 2009 + you will need to use packed array[1..512] of AnsiChar ;
It would be good to know where exactly your access violation occurs. What variable/memory location is your runtime trying to access?
Then find out if this location should actually be accessible, and if so, why it's not.
My suspicion: you access an array element that isn't initialized correctly.
Index := 0;
S := ConvertToString(myWinList[Index].Title);
while S <> '' do
begin
WinListMemo.Lines.Add(S);
Inc(Index);
//////// Is Index pointing to a valid entry here? No check!
S := ConvertToString(myWinList[Index].Title);
end;
Either
- the dll does not initialize it correctly,
- or there is another way to find out the last element.
- or you simply ran off the array altogether: the 101th element is also dereferenced. and the 102nd, if that memory location happens to contain a 0 character.
Check that the definition of your Delphi function matches what you are declaring the C++ function as too. In particular, make sure that you have stdcall at the end, and that your bool values are going to be consistent. C++ and Delphi use different values and sizes for bool, depending on the C++ compiler, so it may be better to use an appropriately sized Integer. As the size of the bool may not match the C++ size, this can affect the stack, and thus cause access violations.
[edited to remove mixed language duff response]
精彩评论