Delphi: Access violation after calling function from external DLL (C++) - c++

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 SCRAPER_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]

Related

Variadic Function Pointer definition is unclear for VxWorks spyLib

I'm using VxWorks 6.9 and am trying to use some spyLib.h functions but I'm having issues finding what signature to expect given that the type is variadic.
in vxTypesOld.h I find my type: typedef int (*FUNCPTR) (...);
and in spyLib.h i have my function call : extern void spyReportCommon (FUNCPTR printRtn);
But what function parameters are expected for printRtn ? I guess a c-style string is one but I don't know if each line of the table is a string or if its an array of strings, or even one large string.
I can't start writing the function to parse data from the outputted data until I know in what form that data is passed into the function.
All I know for certain is that it returns an int (e.g. int parsePrint( ???? );)
Here is my attempt at reporting:
#include <vxworks.h>
#include <spyLib.h>
#include <usrLib.h>
int ParseSpy(const char * spyOutput); // this is a guess
void Startup()
{
//startup logic
// the compiler said and int param is expected but the .h had void
spyLibInit(1);
spyCommon(1,50, (FUNCPTR) &ParseSpy);
}
int ParseSpy(const char * spyOutput){} // this is a guess
I'm getting an unexpected compiler error: 'spyCommon' was not declared in scope
but as you can see spyLib.h was included so I'm a bit confused by this.
That looks like a bad design. The print function cannot print if it does not know what the parameters are. At least one parameter is needed to specify what the rest of the parameters are.
Looking at the source and searching for "printRtn" I see that all calls to the print function are expecting a printf like function where the first parameter is a format string. Your function should better be written as
int ParseSpy(const char * spyOutput, ...);
Regarding the missing spyCommon you could try to let VxWorks write the preprocessor output to a file to check what the compiler sees. Maybe you are getting the wrong spylib.h file or something it that file is hidden by #if.

Loading and calling functions in C++ from pascal DLL/SO

I have a compiled a library and I am trying to access the functions from c++ code. Most functions work properly, however I have some trouble with passing parameters to a function that accepts an array as argument.
The pascal function header is defined as:
function MyFunc( const Name : PAnsichar;
const MyArr : array of single;
const ArrLength : Longint;
var output : single
): Longint;
I can compile this function and use is properly when using pascal to load the function and call the functions in the library. Note: the library is compiled using the CDecl calling convention.
However I have trouble with loading the functions in C++.
The function is defined as:
typedef long (*MyFunc)(char *, float, long, float *);
I am able to load the dll properly and acces all the function properly, all but the one above.
long ArrLeng = 300;
float out;
float Arr[ArrLeng];
\\ fill the array
result = MyFunc((char *) "default", Arr[0], ArrLeng, &out);
I can attach the debugger to the library and check the variables read by the library. The strange thing is that the Name and ArrLeng variables are passed on properly, but the array is not passed properly.
What am I doing wrong? How should I pass the array to the library?
Try passing a pointer to the first element. "Array of Single" is a so called open array which is a pascal construct that also passes array boundary information.
However when used in combination with cdecl afaik it reduces to a pointer to elementtype. (single *) At least Free Pascal does, I don't know what Delphi/Kylix does.
In doubt let pascal call it and check the resulting assembler.

FindFirstFile cout problems

I need to find files with certain extentions, for example *.doc, *.docx
First, i'm looking for all files
lstrcat(szPath, L"*");
hFind = FindFirstFile(szPath, &fdFindData);
Then, i compare founded file with extention i need
PCWSTR str1 = L".doc";
if(NULL != StrStr(fdFindData.cFileName,str1)) {
FoundFileFunction(fdFindData.cFileName);
}
And then I got problem with cout
VOID FoundFileFunction(HANDLE hFile)
{
std::cout<<hFile;
}
This is output:
009AE50000
009AEB0000
009AEBBB00
and so on. What's the problem?
WIN32_FIND_DATA::cFileName is a TCHAR[MAX_PATH], not a HANDLE.
I don't know why you wrote HANDLE, as that's not uttered on the documentation page even once.
Your function is trying to print out the C-string filename as if it were a HANDLE, which is a different kind of pointer to a TCHAR*. It doesn't know to take the pointer as a TCHAR* so it doesn't know you want it to format the output as a string. It can only know to print the address represented by the pointer.
Your function FoundFileFunction should take a TCHAR*.
The problem would have been automatically detected had you used STRICT mode. If (for example) NO_STRICT is defined then HANDLE is an alias for void* which, per the rules of the language, can be initialised implicitly from a TCHAR*. You should always compile with STRICT defined: that would have changed the types not to be implicitly convertible to one another, and you would have received a compilation error for your mistake.
Furthermore, if your program is using Unicode, then TCHAR is not char but wchar_t, so you need to use not std::cout but std::wcout.

How to invoke an exported function from DLL written in C/C++ which return type is char* or string?

We designed C/C++ DLL just like this:
WIN32_DLL_EXPORT int FnRetInt(int i)
{
....
return 32 ;
}
WIN32_DLL_EXPORT char* FnRetString()
{
return "THIS IS A TEST STRING" ;
}
when we invoke these two functions in Go by using syscall:
hd:=syscall.NewLazyDLL(dll_path)
proc:=hd.NewProc(dll_func_name)
ret:=proc.Call()
we found:
FnRetInt worked ok, but FnRetString didn't. proc.Call return type is uintptr, how can we change it to the type we wanted (for exsample: char* or string)?
A uintptr is a Go type that represents a pointer. You can use the unsafe package and convert it to unsafe.Pointer, and then you can convert an unsafe.Pointer into any Go pointer type. So you could do something like
str := (*uint8)(unsafe.Pointer(ret))
to get a *uint8 back.
Look at syscall.Getwd windows implementation http://code.google.com/p/go/source/browse/src/pkg/syscall/syscall_windows.go#323. It is different from your problem:
it passes buffer to the dll, instead of receiving it from dll;
the data is uint16s (Microsoft WCHARs), instead of uint8s;
GetCurrentDirectory tells us how long resulting string is going to be, while your example, probably, expects you to search for 0 at the end;
But should give you enough clues.
Alex

return string from c++ function to VB .Net

I am trying to call C++ function from VB.Net code which returns string using P/Invoke, but it is returning only single character.
C function Declaration
extern "C" __declspec(dllexport) LPSTR Get_GetDescription(HANDLE)
C function Definition
LPSTR Get_GetDescription(HANDLE resultBreakDown){
return LPSTR(((CalcBreakDown*)resultBreakDown)->GetDescription().c_str());
}
VB.Net Code
<DllImport("FeeEngineDll.dll", CallingConvention:=CallingConvention.Cdecl)> _
Public Shared Function Get_GetDescription(ByVal resultBreakDown As IntPtr, ByVal indexSubs As Integer, ByVal indexLine As Integer) As <MarshalAsAttribute(LPStr)> String
End Function
Is there any problem with return type or Marshalling?
extern "C" __declspec(dllexport) LPSTR Get_GetDescription(HANDLE)
Returning a pointer like this is rather dangerous, as it is not clear who owns the memory and thus who should take responsibility for freeing it.
It would be safer to create the buffer in your VB code and pass it into the DLL where the value can be memcpy'ed in. So we could rewrite the C++ side like:
extern "C" __declspec(dllexport) void Get_GetDescription(HANDLE, LPSTR)
void Get_GetDescription(HANDLE resultBreakDown, LPSTR buffer){
memcpy(buffer,
((CalcBreakDown*)resultBreakDown)->GetDescription().c_str(),
((CalcBreakDown*)resultBreakDown)->GetDescription().length()+1);
}
And then redo the VB code as follows:
<DllImport("FeeEngineDll.dll", CharSet:=CharSet.Ansi, CallingConvention:=CallingConvention.Cdecl)> _
Public Shared Sub Get_GetDescription(ByVal resultBreakDown As IntPtr, <MarshalAs(UnmanagedType.LPStr)> ByVal szFilename As StringBuilder)
End Sub
I have added CharSet:=CharSet.Ansi to the DllImport. Your C++ code is not using unicode characters, whereas VB probably will be, so best specify that, you probably don't need to put it in but I like to make these things explicit.
Note the use of StringBuilder instead of String as strings are immutable in VB. Finally, you will need to be careful to allocate enough space in your string builder for the description:
Dim buffer As StringBuilder = New StringBuilder(512)
You can either do this by using a large number in your VB code, as I have just done. This will however, cause problems if your C++ code ever copies more characters than you allocated.
Other better options would either be to pass in the buffer size to the C++ code so that it knows how much it is allowed to write, or to have a get size function in the C++ code that can be used to determine how much space should be allocated for the buffer.