Hi I'm having quite some issues with integrating a DLL inside my Delphi 2007 application.
i suspect that I'm doing something wrong with the parameters of the calls.
At this moment i have 2 issues, but i think they are related to eachother.
1)
First call with the DLL:
from the .h file:
extern "C" {
__declspec(dllexport) HRESULT Startup(char* version);
}
This call should initialize the DLL and give me the version back of the DLL. HRESULT should be 0, and the version pointer should contain the version.
My Delphi code:
function Startup(var version: Pchar): HRESULT; cdecl; stdcall; external 'myDLL.dll';
And the actual call:
var
res : HRESULT;
Name1 : PChar;
test : AnsiString;
buf2: array [0..20] of AnsiChar;
begin
FillChar(buf2,20,0);
Name1:= #buf2[0];
res := RdmStartup(Name1);
//Here res = 0, but the Name1 stays empty, and the buf2 still contains 0.
end;
But as the result is 0 the call was a success.
Then my second issue: i need to call a function in the DLL that will open a COM port.
The .h:
extern "C" {
__declspec(dllexport) HRESULT Open(HWND hWnd, int Port, DWORD BaudRate, DWORD Interval);
}
And my Delphi declare:
function Open(hWnd: HWND;Port : integer;BaudRate:LongInt;Interval:LongInt): HRESULT; cdecl; stdcall; external 'myDLL.dll';
and i call this by:
res:= Open(self.Handle,5,115200,500);
And here i'm getting a failure back from the DLL in the res variable.
i also have the source of the DLL, and the failure that i'm getting is from the part where the DLL is checking if the parameters are valid, if they are valid it will continue, else return the error i'm currently getting.
The things it is checking:
if(hWnd == NULL)
{
return false;
}
if(BaudRate != 2400 && BaudRate != 9600 && BaudRate != 38400 && BaudRate != 115200)
{
return false;
}
if(IntervalTimer < 300)
{
return false;
}
std::string strPortName = lexical_cast<std::string>( format("COM%d") % Port);
std::string strPortName(lpPortName.c_str());
std::string::size_type loci = strPortName.find("COM");
if( loci == std::string::npos )
{
return false;
}
return true;
And one of these above is returning false on my call, because if the result of this function is false, the DLL gives the error i'm currently getting in the results.
Does anyone have an idea of what i am doing wrong?
i've tried numerous of combinations for the types in the end i sticked to the conversion i found at: http://www.drbob42.com/delphi/headconv.htm
i've also tried different ways of reading the char pointer, but all of them failed.....
So at this stage i know i am succesfully communicating with the DLL as i'm getting different HRESULTs back for the 2 calls, but i suspect my parameters are not working like the should.
I'm using Delphi 2007 and the C++ DLL was build with VS2010.
The declaration of Startup is pretty suspicious:
__declspec(dllexport) HRESULT Startup(char* version);
This translates into:
function Startup(version: PAnsiChar): HResult; stdcall; external 'myDLL.dll';
So there should be no var there.
I got from your comments that the cdecl calling convention works for some of your code. In that case remove stdcall, since it overrules the preceding cdecl.
The declaration of Open() seems to be pretty OK (I would use DWORD as type, not Longint, especially since DWORD is Longword these days -- but in Win32 they are the same size, so that won't make any big difference for you). And you seem to be passing the right parameters too.
You did not write what the HRESULT value is that you get back. But I assume that the port COM5 simply cannot be opened with these settings.
What can you do?
You should remove the var from Startup().
So you can try:
to use cdecl instead of stdcall (the stdcall in your declaration overrules the cdecl)
to open different COM ports with different parameters
to decode the HRESULT that is returned.
A better diagnosis is not possible, from a distance, without the same hardware and software, sorry.
You could read my article on conversion. This has also a few paragraphs that explain how to debug the code to find out the proper calling convention. It can probably help you with more of your problems converting headers, too.
Related
I have, on more than one occasion, advised people to use a return value of type WideString for interop purposes.
Accessing Delphi DLL throwing ocasional exception
ASP.NET web app calling Delphi DLL on IIS webserver, locks up when returning PChar string
Why can Delphi DLLs use WideString without using ShareMem?
The idea is that a WideString is the same as a BSTR. Because a BSTR is allocated on the shared COM heap then it is no problem to allocate in one module and deallocate in a different module. This is because all parties have agreed to use the same heap, the COM heap.
However, it seems that WideString cannot be used as a function return value for interop.
Consider the following Delphi DLL.
library WideStringTest;
uses
ActiveX;
function TestWideString: WideString; stdcall;
begin
Result := 'TestWideString';
end;
function TestBSTR: TBstr; stdcall;
begin
Result := SysAllocString('TestBSTR');
end;
procedure TestWideStringOutParam(out str: WideString); stdcall;
begin
str := 'TestWideStringOutParam';
end;
exports
TestWideString, TestBSTR, TestWideStringOutParam;
begin
end.
and the following C++ code:
typedef BSTR (__stdcall *Func)();
typedef void (__stdcall *OutParam)(BSTR &pstr);
HMODULE lib = LoadLibrary(DLLNAME);
Func TestWideString = (Func) GetProcAddress(lib, "TestWideString");
Func TestBSTR = (Func) GetProcAddress(lib, "TestBSTR");
OutParam TestWideStringOutParam = (OutParam) GetProcAddress(lib,
"TestWideStringOutParam");
BSTR str = TestBSTR();
wprintf(L"%s\n", str);
SysFreeString(str);
str = NULL;
TestWideStringOutParam(str);
wprintf(L"%s\n", str);
SysFreeString(str);
str = NULL;
str = TestWideString();//fails here
wprintf(L"%s\n", str);
SysFreeString(str);
The call to TestWideString fails with this error:
Unhandled exception at 0x772015de in BSTRtest.exe: 0xC0000005: Access violation reading location 0x00000000.
Similarly, if we try to call this from C# with p/invoke, we have a failure:
[DllImport(#"path\to\my\dll")]
[return: MarshalAs(UnmanagedType.BStr)]
static extern string TestWideString();
The error is:
An unhandled exception of type 'System.Runtime.InteropServices.SEHException' occurred in ConsoleApplication10.exe
Additional information: External component has thrown an exception.
Calling TestWideString via p/invoke works as expected.
So, use pass-by-reference with WideString parameters and mapping them onto BSTR appears to work perfectly well. But not for function return values. I have tested this on Delphi 5, 2010 and XE2 and observe the same behaviour on all versions.
Execution enters the Delphi and fails almost immediately. The assignment to Result turns into a call to System._WStrAsg, the first line of which reads:
CMP [EAX],EDX
Now, EAX is $00000000 and naturally there is an access violation.
Can anyone explain this? Am I doing something wrong? Am I unreasonable in expecting WideString function values to be viable BSTRs? Or is it just a Delphi defect?
In regular Delphi functions, the function return is actually a parameter passed by reference, even though syntactically it looks and feels like an 'out' parameter. You can test this out like so (this may be version dependent):
function DoNothing: IInterface;
begin
if Assigned(Result) then
ShowMessage('result assigned before invocation')
else
ShowMessage('result NOT assigned before invocation');
end;
procedure TestParameterPassingMechanismOfFunctions;
var
X: IInterface;
begin
X := TInterfaceObject.Create;
X := DoNothing;
end;
To demonstrate call TestParameterPassingMechanismOfFunctions()
Your code is failing because of a mismatch between Delphi and C++'s understanding of the calling convention in relation to the passing mechanism for function results. In C++ a function return acts like the syntax suggests: an out parameter. But for Delphi it is a var parameter.
To fix, try this:
function TestWideString: WideString; stdcall;
begin
Pointer(Result) := nil;
Result := 'TestWideString';
end;
In C#/C++ you will need to define the Result as out Parameter, in order to maintain binary code compatibility of stdcall calling conventions:
Returning Strings and Interface References From DLL Functions
In the stdcall calling convention, the function’s result is passed via the CPU’s EAX register. However, Visual C++ and Delphi generate different binary code for these routines.
Delphi code stays the same:
function TestWideString: WideString; stdcall;
begin
Result := 'TestWideString';
end;
C# code:
// declaration
[DllImport(#"Test.dll")]
static extern void TestWideString([MarshalAs(UnmanagedType.BStr)] out string Result);
...
string s;
TestWideString(out s);
MessageBox.Show(s);
i have a problem to use delphi class from C++ code. delphi dll demo that export a function that return an object.
my delphi Dll code is as follow:
library DelphiTest;
// uses part....
type
IMyObject = interface
procedure DoThis( n: Integer );
function DoThat : PWideChar;
end;
TMyObject = class(TInterfacedObject,IMyObject)
procedure DoThis( n: Integer );
function DoThat: PChar;
end;
// TMyObject implementation go here ...
procedure TMyObject.DoThis( n: Integer );
begin
showmessage('you are calling the DoThis methode with '+intToStr(n) +'parameter');
end;
function TMyObject.DoThat: PChar;
begin
showmessage('you are calling the DoThat function');
Result := Pchar('Hello im Dothat');
end;
// exporting DLL function :
function CreateMyObject : IMyObject; stdcall;export;
var
txt : TextFile;
begin
AssignFile(txt,'C:\log.log');
Reset(txt);
Writeln(txt,'hello');
Result := TMyObject.Create;
end;
exports CreateMyObject;
in my C++ project i declared the IMyObject interface as follow :
class IMyObject
{
public:
IMyObject();
virtual ~IMyObject();
virtual void DoThis(int n) = 0;
virtual char* DoThat() = 0;
};
and my main function as follow :
typedef IMyObject* (__stdcall *CreateFn)();
int main()
{
HMODULE hLib;
hLib = LoadLibrary(L"DelphiTest.dll");
assert(hLib != NULL); // pass !!
CreateFn pfnCreate;
pfnCreate = (CreateFn)GetProcAddress((HINSTANCE)hLib, "CreateMyObject");
if (pfnCreate == NULL)
{
DWORD errc = GetLastError();
printf("%u\n", errc); // it gets error 127
}
else{
printf("success load\n");
}
IMyObject* objptr = pfnCreate();
objptr->DoThis(5);
FreeLibrary(hLib);
int in;
scanf_s("%i", &in);
return 0;
}
with this example i got an error at run time when i try to access the exported function. the errors is at line :
IMyObject* objptr = pfnCreate();
can you tell me what is wrong about my example.
and if possible any working example to access Delphi class (in DLL) from C++ code.
The first problem is calling convention of the methods. The Delphi interface uses register which is a Delphi specific calling convention. Use stdcall, for example, for the methods of the interface.
The next problem is in the C++. Your C++ interface must derive from IUnknown. Further, it should not declare a constructor or destructor.
Beyond that your Delphi code exports PWideChar which does not map to char*. It maps to wchar_t*.
Looking further ahead, returning a PChar works fine here because your implementation returns a literal. But more serious code will want to use a dynamically allocated string presumably and at that point your design is flawed.
Do note that you need to be an elevate administrator to create a file at the root of the system drive. So that's yet another potential failure point.
I expect there are other mistakes, but that's all I've found so far.
I'm using GetProcAddress to gain access to a standard Isapi Filter DLL method - the GetFilterVersion method which takes a pointer to a HTTP_FILTER_VERSION structure.
https://msdn.microsoft.com/en-us/library/ms525822(v=vs.90).aspx
https://msdn.microsoft.com/en-us/library/ms525465(v=vs.90).aspx
I've tested the code against a working Isapi filter that I've written and it works fine. I debug the code against an Isapi filter from a vendor (I don't have access to the source code or anything beyond the dll itself) and I get the exception, "access violation writing location". What could be the issue? (Both Isapi filters work in IIS.)
//Attempted to define function ptr several ways
typedef BOOL(__cdecl * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
//typedef BOOL( * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
//typedef BOOL(WINAPI * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
void arbitraryMethod()
{
HINSTANCE hDLL; // Handle to DLL
TRIRIGAISAPIV lpfnDllFunc2; // Function pointer
DWORD lastError;
BOOL uReturnVal2;
hDLL = LoadLibrary(L"iisWASPlugin_http.dll"); //vendor's dll
//hDLL = LoadLibrary(L"KLTWebIsapi.dll //my dll
if (hDLL != NULL)
{
lpfnDllFunc2 = (TRIRIGAISAPIV)GetProcAddress(hDLL, "GetFilterVersion");
if (!lpfnDllFunc2)
{
lastError = GetLastError();
// handle the error
FreeLibrary(hDLL);
//return 1;
}
else
{
HTTP_FILTER_VERSION pVer = { 6 };
//Call the function via pointer; Works with my dll, fails with vendor's
uReturnVal2 = lpfnDllFunc2(&pVer);
//................ HELP!!!!!!!!!!!!!
}
}
}
One issue that I see is that your function pointer declaration is incorrect.
According to the Microsoft documentation, GetFilterVersion is prototyped as:
BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer);
The WINAPI is a Windows macro that is actually defined as __stdcall, thus you are declaring the function pointer incorrectly when you used __cdecl.
What does WINAPI mean?
Thus, your declaration should be:
typedef BOOL(__stdcall * TRIRIGAISAPIV)(PHTTP_FILTER_VERSION);
It could be that there are actually some additional structure fields filled by the custom filter.
You can try to increase the size of the structure to see if that will work, like for example:
struct HTTP_FILTER_VERSION_EXTRA {
HTTP_FILTER_VERSION v;
char[1024] extra;
};
HTTP_FILTER_VERSION_EXTRA ver;
ver.v.dwServerFilterVersion = 6;
uReturnVal2 = lpfnDllFunc2(&ver.v);
It is sometimes the case with the WinAPI structures that they allow versioning, so adding fields is possible. If the function doesn't then check (or doesn't know) the actual structure version, it might try to use an extended one which might be different than the one supplied - if the size of the supplied struct is then lesser than the structure version the func tries to use, bad things can happen.
Also check if the DLL is 64-bit or 32-bit. You cannot use 64-bit DLL by 32-bit app and vice versa (but I expect that would already fail during the LoadLibrary call).
I get this error when trying to use this function
void WSPAPI GetLspGuid( LPGUID lpGuid )
{
memcpy( lpGuid, &gProviderGuid, sizeof( GUID ) );
}
the error
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
the function is called by using
HMODULE hMod = NULL;
LPFN_GETLSPGUID fnGetLspGuid = NULL;
int retval = SOCKET_ERROR;
// Load teh library
hMod = LoadLibraryA( LspPath );
if ( NULL == hMod )
{
fprintf( stderr, "RetrieveLspGuid: LoadLibraryA failed: %d\n", GetLastError() );
goto cleanup;
}
// Get a pointer to the LSPs GetLspGuid function
fnGetLspGuid = (LPFN_GETLSPGUID) GetProcAddress( hMod, "GetLspGuid" );
if ( NULL == fnGetLspGuid )
{
fprintf( stderr, "RetrieveLspGuid: GetProcAddress failed: %d\n", GetLastError() );
goto cleanup;
}
// Retrieve the LSPs GUID
fnGetLspGuid( Guid );
This runtime check guards against a mismatch between the function declaration and the actual definition. Accidents that can happen when you compile code into a static library or a DLL. Common mismatches are the calling convention or the number or type of the arguments that are passed.
The shoe fits, you've got a macro named WSPAPI that declares the calling convention. It typically expands to either __cdecl or __stdcall, usually biased towards __stdcall. So very high odds that it this macro has the wrong value in your client code. Ask the library author for assistance if you can't figure out how to set this macro correctly.
After edit: with the additional failure mode that you are loading the wrong version of the DLL. And that your LPFN_GETLSPGUID function pointer declaration is wrong, missing the WSPAPI macro. I'll put my money on that one, especially since I can't see it.
After comment, the info is slowly trickling in:
it is defined as typedef void (*LPFN_GETLSPGUID) (GUID *lpGuid);
Which is wrong, it should be
typedef void (WSPAPI * LPFN_GETLSPGUID)(GUID *lpGuid);
If you don't have the macro available, unlikely, then substitute WSPAPI with __stdcall.
I'm not able to use the function of a dll developed in delphi. I'm having some difficulties with the conversions of types.
This is the function I want to call the DLL:
function rData(ID: Cardinal; queue: WideString): WideString; stdcall;
My code in C++ was so:
typedef string (*ReturnDataSPL)(DWORD, string);
string result;
HMODULE hLib;
hLib = LoadLibrary("delphi.dll");
pReturnDataSPL = (ReturnDataSPL)GetProcAddress(hLib,"rData");
if (NULL != pReturnDataSPL)
result = pReturnDataSPL(JobID,printerName);
The problem I'm not able to make it work. I do not know which type is compatible with Delphi WideString and Cardinal.
Someone help me
EDIT:
This is the function I want to call the DLL:
procedure rData(ID: Cardinal; queue: WideString; var Result: WideString); stdcall;
After changing the code looked like this:
typedef void (__stdcall *ReturnDataSPL)(DWORD, BSTR, BSTR&);
HMODULE hLib;
BSTR result = NULL;
hLib = LoadLibrary("delphi.dll");
pReturnDataSPL = (ReturnDataSPL)GetProcAddress(hLib,"rData");
if (NULL != pReturnDataSPL)
{
pReturnDataSPL(JobID,(BSTR)"Lexmark X656de (MS) (Copiar 2)",result);
}
You've got very little chance of calling that function.
For a start your current code can't hope to succeed since I presume string is std::string. That's a C++ data type which Delphi code cannot either provide or consume. To match up against Delphi's WideString you need to use the COM BSTR data type.
Another problem with your code as it stands is that it uses cdecl in the C++ side, and stdcall on the Delphi side. You'll need to align the calling conventions.
However, that will also fail because of a difference between Delphi's ABI for return values, and the platform standard. That topic was covered in detail here: Why can a WideString not be used as a function return value for interop?
Your best bet is to stop using WideString as a return value and convert it into a C++ reference parameter. You'll want to convert the Delphi to match.
You are looking at something like this:
Delphi
procedure rData(ID: Cardinal; queue: WideString; var Result: WideString); stdcall;
C++
typedef void (__stdcall *ReturnDataSPL)(DWORD, BSTR, BSTR&);