Calling a callback function in Delphi from a C++ DLL - c++

I have a C++ DLL that I wrote that has a single exposed function, that takes a function pointer (callback function) as a parameter.
#define DllExport extern "C" __declspec( dllexport )
DllExport bool RegisterCallbackGetProperty( bool (*GetProperty)( UINT object_type, UINT object_instnace, UINT property_identifer, UINT device_identifier, float * value ) ) {
// Do something.
}
I want to be able to call this exposed C++ DLL function from within a Delphi application and register the callback function to be used at a future date. But I am unsure of how to make a function pointer in Delphi that will work with the exposed C++ DLL function.
I have the Delphi application calling a simple exposed c++ DLL functions from the help I got in this question.
I am building the C++ DLL and I can change its parameters if needed.
My questions are:
How to I create a function pointer in Delphi
How to I correctly call the exposed C++ DLL function from within a Delphi application so that the C++ DLL function can use the function pointer.

Declare a function pointer in Delphi by declaring a function type. For example, the function type for your callback could be defined like this:
type
TGetProperty = function(object_type, object_instnace, property_identifier, device_identifier: UInt; value: PSingle): Boolean; cdecl;
Note the calling convention is cdecl because your C++ code specified no calling convention, and cdecl is the usual default calling convention for C++ compilers.
Then you can use that type to define the DLL function:
function RegisterCallbackGetProperty(GetProperty: TGetProperty): Boolean; cdecl; external 'dllname';
Replace 'dllname' with the name of your DLL.
To call the DLL function, you should first have a Delphi function with a signature that matches the callback type. For example:
function Callback(object_type, object_instnace, property_identifier, device_identifier: UInt; value: PSingle): Boolean cdecl;
begin
Result := False;
end;
Then you can call the DLL function and pass the callback just as you would any other variable:
RegisterCallbackGetProperty(Callback);

Related

DLL function that exports a pointer do Delphi program

I have a simple program that exports a DLL, this DLL exports functions from another DLL:
// SDK_DLL.cpp :
#include "functions.h"
#include "functions_advanced.h"
#include "stdafx.h"
#include <stdio.h>
using namespace std;
extern "C"
{
__declspec(dllexport) void DisplayHelloFromDLL()
{
printf("Hello from DLL...");
}
__declspec(dllexport) function_config FuncInit = appd_config_init();
__declspec(dllexport) function_config * FuncInit2()
{
function_config* cfg = function_config_init();
return cfg;
}
}
The function_config_init() returns a pointer, I cannot seem to find a way of making this proper export declaration.
I am loading a simple function to Delphi this way:
procedure DisplayHelloFromDLL; external 'C:\Users\Administrator\Documents\Visual Studio 2017\Projects\SDK_DLL\Debug\SDK_DLL.dll';
Will I need to change the way I am loading this pointer returning function?
Thanks a lot for your help.
FuncInit is an exported variable. Delphi does not support importing of variables via external, only of functions. If you need to import FuncInit, you will have to use GetProcAddress() directly to get a pointer to the variable at runtime:
type
// you did not show the C/C++ declaration
// of function_config, so I can't provide
// a translation here, but it is likely to
// be a struct, which is a record in Delphi ...
function_config = ...;
pfunction_config = ^function_config;
function GetFuncInit: pfunction_config;
begin
Result := pfunction_config(GetProcAddress(GetModuleHandle('SDK_DLL.dll'), 'FuncInit'));
end;
var
FuncInit: pfunction_config;
FuncInit := GetFuncInit;
For purposes of interop across languages/compilers, the only portable calling conventions are cdecl and stdcall. When no calling convention is specified in code, the default used by most C and C++ compilers is __cdecl (but can usually be specified in compiler settings), while the default used by Delphi is register instead (__fastcall in C++Builder).
When no parameters or return value are used, like in DisplayHelloFromDLL(), then declaring the wrong calling convention does not really matter. But when parameters and/or a return value are used, like in FuncInit2(), then declaring the correct calling convention matters. See Pitfalls of converting for more details.
So, the two DLL functions in question would likely need to be declared like the following in Delphi:
type
function_config = ...;
pfunction_config = ^function_config;
procedure DisplayHelloFromDLL; cdecl; external 'SDK_DLL.dll' name '_DisplayHelloFromDLL';
function FuncInit2: pfunction_config; cdecl; external 'SDK_DLL.dll' name '_FuncInit2';
If the DLL uses a .def file to remove name mangling from the exported names, you can omit the name attribute:
type
function_config = ...;
pfunction_config = ^function_config;
procedure DisplayHelloFromDLL; cdecl; external 'SDK_DLL.dll';
function FuncInit2: pfunction_config; cdecl; external 'SDK_DLL.dll';

Edit Delphi record from C++ DLL

I'm trying to create a DLL using Visual C++ that is called from a Delphi 5 program. The Delphi program passes in a record, which is then edited in the DLL, and the Delphi program uses the results.
For example, the Delphi code is similar to the following:
Type dll_btvar = record
age : smallint;
name : array[0..11] of char;
value : Double;
end;
// Import the function from the dll
function foo(CVars : dll_btvar):integer; external 'example.dll';
// Call the dll
function callFoo(var passedVar:dll_btvar):integer;
begin
result := foo(passedVar);
// Use passedVar.value
end;
A sample of the C++ code:
In example.h:
#pragma once
#include "dllVar.h"
extern "C" {
__declspec(dllexport) int foo(DLL_Var var);
}
In example.cpp:
#include "example.h"
int foo(DLL_Var var){
var.value = var.age + var.name[0];
return 0;
}
In dllVar.h:
#pragma once
#pragma pack(8)
extern "C" {
struct DLL_Var {
short age;
char name[12];
double value;
}
}
I use #pragma pack(8) as that value gave correct alignment so that the passed record is read correctly in the DLL.
In the sample code, when passing an age and name, I expect value to be set by the DLL, which can then be recovered in the Delphi program. Result would be some sort of error code.
Using identical code in C++ Builder 5 did work, however it is of course outdated and I haven't moved all the code in my DLL over (nor do I want to), only the minimum you see here.
I tested a couple of ways to have Delphi pass an address/pointer to the dll, however they didn't change anything.
Right now, the return value is sent correctly, but the fields of the record (i.e. value) remain unchanged.
What changes do I need to make to either the Delphi or C++ to capture changes in the passed record? I am happy to work extensively with the C++ but I'd prefer to keep the Delphi changes to a minimum since this is old software that I don't want to break.
function foo(CVars : dll_btvar):integer; external 'example.dll';
The problem starts here, in the Delphi code. The record is passed by value. That is, the caller's record variable is copied to a new variable which is then passed to the function. This means that modifications by the callee to this copy of the record are not seen by the caller. You must therefore pass the parameter as a var parameter:
function foo(var CVars : dll_btvar):integer; external 'example.dll';
The next problem is the calling convention. You must use the same calling convention for both sides. Your code uses the default register convention on the Delphi side, which is not supported by non-Borland/Embarcadero tools. Use stdcall or cdecl instead. Let's opt for cdecl, the default for most C++ tools:
function foo(var CVars : dll_btvar):integer; cdecl; external 'example.dll';
To make the C++ code match, pass the argument by reference:
__declspec(dllexport) int __cdecl foo(DLL_Var &var);
Or explicitly use a pointer:
__declspec(dllexport) int __cdecl foo(DLL_Var *var);
In the latter option, the implementation needs to be changed because of the use of a pointer:
int foo(DLL_Var *var){
var->value = var->age + var->name[0];
return 0;
}
Using identical code in C++ Builder 5 did work.
No it did not, because the Delphi code in your question cannot modify the caller's record.

Define optional char / wchar_t pointer parameter in MIDL

I have an existing Win32 C++ DLL, which needs to be accessed by a VB6 client. One of the exported functions is defined as follows:
__declspec(dllexport) long __stdcall Foo(long nId, LPCWSTR pszwPath = nullptr);
Unfortunately, VB6 always converts strings to ANSI when calling DLL functions via a Declare statement. In order to bypass this limitation, I have embedded a type library, which features the following signature for the function:
[uuid(...)]
library FooLib
{
[
helpstring("FooLib"),
dllname("Foo.dll")
]
module FooMdl
{
[entry("Foo")]
long __stdcall Foo([in] long nId, [in,unique,string,defaultvalue(0)] LPCWSTR pszwPath);
}
};
This won't compile however, as MIDL generates the following error:
error MIDL2020 : error generating type library : AddFuncDesc failed : Foo
My next attempt involved utilizing the optional attribute:
long __stdcall Foo([in] long nId, [in,unique,string,optional] LPCWSTR pszwPath);
While this type library can be compiled successfully, the VB6 client crashes with an access violation as soon as the optional string value is omitted.
I am aware that I could change the LPCWSTR argument to a BSTR type, thereby remedying the problem. However, this would also require me to change the signature and implementation of the existing DLL.
Is it therefore possible to define a char / wchar_t pointer argument as optional or with a NULL default value? Or am I simply out of luck here?

Why doesn't this code work (Please See Details)?

I have written the following code (x64 VS 2015):
typedef void(__stdcall *foo)(void* v);
HMODULE hmod = GetModuleHandle(NULL);
foo f = (foo) GetProcAddress(hmod, "_foo0");
f(0);
foo0 is defined as:
extern "C" void __stdcall foo0(void* v){int a = 0;}
I have disabled all optimizations and security checks.
What I want the code to do is to find the address of the foo0 and then call it.
For some weird reason, calling GetLastError() after GetModuleHandle() returns 0x00000032 which means ERROR_NOT_SUPPORTED, but it does return some nonzero value which I assume is the handle to the executable. GetProcAddress() returns 0x0000000000000000 and a GetLastError() call after it returns 0x0000007f which means ERROR_PROC_NOT_FOUND, but I defined the proc!
Why is this happening? Is GetProcAddress() not supposed to be used with GetModuleHandle()?
The code fails because GetProcAddress requires the supplied symbol to have been exported from the module in question. That is, the symbol must have been listed in the PE module's export table. You do not export the symbol, and so GetProcAddress cannot find it. Hence GetProcAddress returns NULL. If you wish to use GetProcAddress then you must export the symbol. Either by naming it in a .def file, or by using __declspec(dllexport).
Some other comments:
You appear to have a mismatch of calling conventions, stdcall and cdecl.
You don't perform any error checking. For these particular function you need to check the return value. If that indicates that the function has failed, then call GetLastError for extended error information.
It should be:
extern "C" __declspec(dllexport) void foo0(void* v) { int a = 0; }
and:
foo f = (foo)GetProcAddress(hmod, "foo0");
^^~~~ no need for underline
as to your GetLastError issue, I am not sure about it - I suppose it might be some random value.

Access C++ function declared without calling convention specified from Delphi

I try to use a DLL whose two functions are:
__declspec(dllexport) LPCWSTR* MW_ListReaders(_ULONG Context, int* NumberOfReaders);
__declspec(dllexport) _ULONG MW_Connect(_ULONG Context, LPCWSTR ReaderName);
For use with Delphi, and for the MW_ListReaders function, I made the following statement
function MW_ListReaders(Context : int64; var NumberOfReaders : integer) : PWideChar; stdcall;
Not knowing much in C ++ and in addition, pointers, I am lost.
How can I use these two functions in Delphi?
Your MW_ListReaders() function is declared incorrectly.
The default calling convention in C/C++ when no calling convention is specified is __cdecl, not __stdcall.
MW_ListReaders() is returning a pointer to a pointer to a wide char (which would be PPWideChar in Delphi), but you have declared it as returning a pointer to a wide char (PWideChar) instead.
ULONG is a 32bit unsigned integer, not a 64bit signed integer.
Try this instead:
function MW_ListReaders(Context: UInt32; var NumberOfReaders: Integer): PPWideChar; cdecl; external 'filename.dll';
function MW_Connect(Context: UInt32; const ReaderName: PWideChar): UInt32; cdecl; external 'filename.dll'
UInt32 was added in Delphi 2009. If you are using an older version, or just for good practice in general, you can (and should) use the ULONG or ULONG32 type (and other types) that is in the Windows unit instead to maintain compatibility with the original C/C++ declarations:
uses
..., Windows;
type
PLPCWSTR = ^LPCWSTR;
function MW_ListReaders(Context: ULONG; var NumberOfReaders: Integer): PLPCWSTR; cdecl; external 'filename.dll';
function MW_Connect(Context: ULONG; ReaderName: LPCWSTR): ULONG; cdecl; external 'filename.dll'
I think that _ULONG corresponds to 32-bit unsigned integer type (at least for MS compilers).
And note using PPWideChar as result type.
Edit: calling convention changed to cdecl, default one, as David Heffernan noticed.
function MW_ListReaders(Context: Cardinal; var NumberOfReaders : integer): PPWideChar; cdecl;
function MW_Connect(Context: Cardinal; ReaderName: PWideChar): Cardinal; cdecl;