Edit Delphi record from C++ DLL - c++

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.

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';

how to make a function return an array? for making DLL to use in VB.NET

i am trying to make a c++ DLL project in VS2017 to use in a VB.NET VS2017 project.
the c++ code;
MyCudaLib.h
#ifndef DLL3_H
#define DLL3_H
#ifdef DLL3_EXPORTS
#define DLL3_API __declspec(dllexport)
#else
#pragma message("automatic link to MyCudaLib.LIB")
//#pragma comment(lib, "MyCudaLib.lib")
#define DLL3_API __declspec(dllimport)
#endif
int* __stdcall test_array();
#endif //DLL3_H
MyCudaLib.cpp
#include <stdio.h>
#include "MyCudaLib.h"
#include <Windows.h>
#include <stdexcept>
#include <sstream>
using namespace std;
#define DLL3_EXPORTS
BOOL APIENTRY DllMain(HANDLE /*hModule*/,
DWORD ul_reason_for_call,
LPVOID /*lpReserved*/
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
int* __stdcall test_array()
{
int arr[]{ 30,50,60,70 };
return arr;
}
in VB part of the code
Private Declare Function test_array Lib "MyCudaLib.dll" () As Integer()
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Console.WriteLine(test_array(0))
End Sub
is my exporting part of code is wrong? c++ part compiled wtihout error but VB part of the code wont work gives error.
System.Runtime.InteropServices.MarshalDirectiveException: ''return value' cannot arranged.
Note: My point to make a proper working function in c++ is to write a CUDA code to program the GPU. I can write and compile code in CUDA in c++ without problem. But students in my class cannot program in c++ and i can't write more complex code with c++. So, i thought, if i can use the cuda function outside the c++ environment, we all can code it with vb or other languages.
Sure your function compiles fine - it is defined as a function returning a single int and does so as well (return *arr; is equivalent to return arr[0];).
From MarshalDirectiveException
The exception that is thrown by the marshaler when it encounters a MarshalAsAttribute it does not support.
As you did not provide an English error message, I can only guess what's happening now, but there are only few options available:
You have yet another error with your marshaling not visible here.
The function is not recognised due to C++ name mangling (see below).
Incompatibility between int returned in function and expected array is recognised.
The int is interpreted as pointer – but pointer size does not match and the type cannot be marshaled at all.
If you want to return an array, you need to define your function as such:
int* getArray()
// ^
{
static int a[] = {1, 2, 3};
// ^^^^^^ we cannot return an array with local storage duration, so make it
// static, global, or allocate one on heap (new int[]; but assure the
// array to be delete[]d appropriately again to avoid memory leak)
return a; // NOT dereferenced!
}
Be aware that you have C++ name mangling applied. You might possibly prefer a C-kompatible interface:
extern "C" int* getArray();
However, you cannot return C++ data types this way like std::vector – which in most cases even is preferrable, though, as those types usually are incompatible across different implementations (DLL compiled with MSVS, EXE with GCC/MinGW) anyway.
Finally be aware that via a pointer, you lose information about array size! You need to find other means to provide this information to the user of your library.

How to check if function is declared in global scope at compile time

Let I have a header, for example #include <GL/gl.h>. It contains subset of OpenGL API functions. I need something like this:
static_assert(has_glDrawArraysIndirect::value, "There is no glDrawArraysIndirect");
Or even better:
PFNGLDRAWARRAYSINSTANCEDPROC ptr_glDrawArraysIndirect = ptr_to_glDrawArraysIndirect::ptr;
Where ptr_to_glDrawArraysIndirect::ptr unrolls to pointer to glDrawArraysIndirect if it's defined or to a stub function stub_glDrawArraysIndirect otherwise.
My target operating system is very specific. Any linker based solution (like GetProcAddress or dlsym) doesn't work for me, since there is no dynamic linker. More than, my driver doesn't provide glXGetProcAdrress nor wglGetProcAddress, basically there there is no way to query pointer at run time by function name (Actually, I want to implement such a mechanism).
Any ideas?
Here is an answer that can detect it at compile time and produce a boolean value. It works by creating a template function of the same name in a namespace and then using that namespace inside of the is_defined() function. If the real glDrawArraysIndirect() exists it will take preference over the template version. If you comment out the first declaration of glDrawArraysIndirect() the static assert at the bottom will trigger.
Test on GodBolt
#include <type_traits>
enum GLenum {};
void glDrawArraysIndirect(GLenum, const void*);
namespace detail {
struct dummy;
template<typename T>
dummy& glDrawArraysIndirect(T, const void*);
}
constexpr bool is_defined()
{
using namespace detail;
using ftype = decltype(glDrawArraysIndirect(GLenum(), nullptr));
return std::is_same<ftype, void>();
}
static_assert(is_defined(), "not defined");
With a little tweak you can make your custom function the template and use a similar trick
ideone.com
#include <type_traits>
#include <iostream>
//#define USE_REAL
enum GLenum {TEST};
typedef void (*func_type)(GLenum, const void*);
#ifdef USE_REAL
void glDrawArraysIndirect(GLenum, const void*);
#endif
namespace detail {
struct dummy {};
template<typename T = dummy>
void glDrawArraysIndirect(GLenum, const void*, T = T())
{
std::cout << "In placeholder function" << std::endl;
}
}
void wrapDraw(GLenum x, const void* y)
{
using namespace detail;
glDrawArraysIndirect(x, y);
}
#ifdef USE_REAL
void glDrawArraysIndirect(GLenum, const void*)
{
std::cout << "In real function" << std::endl;
}
#endif
int main()
{
wrapDraw(TEST, nullptr);
}
Include the expression sizeof(::function) somewhere. (If the function exists then asking for the size of the pointer to the function is a perfectly valid thing to do).
It will be benign at runtime, and :: forces the use of the function declared at global scope.
Of course, if function does not exist at global scope, then compilation will fail.
Along with other errors, the compiler will issue a specific error if you were to write something on the lines of
static_assert(sizeof(::function), "There is no global function");
My target operating system is very specific. Any linker based solution (like GetProcAddress or dlsym) doesn't work for me, since there is no dynamic linker.
Is this an embedded system or just a weirdly stripped down OS running on standard PC hardware?
More than, my driver doesn't provide glXGetProcAdrress nor wglGetProcAddress, basically there there is no way to query pointer at run time by function name
The abiliy to query function pointers at runtime does not depend on the presence of a dynamic linker. Those two are completely orthogonal and even a purely statically linked embedded OpenGL implementation can offer a GetProcAddress interface just fine. Instead of trying to somehow solve the problem at compile or link time, I'd rather address the problem by implementing a GetProcAddress for your OpenGL driver; you can do that even if the driver is available as only a static library in binary form. Step one:
Create function pointer stubs for each and every OpenGL function, statically initialized to NULL and attributed weak linkage. Link this into a static library you may call gl_null_stubs or similar.
Create a GetProcAddress function that for every OpenGL function there is returns the pointer to the function symbol within the scope of the function's compilation unit.
Now link your weird OpenGL driver with the stubs library and the GetProcAddress implementation. For every function there is, the weak linkage of the stub will the static library symbol to take precedence. For all OpenGL symbols not in your driver the stubs will take over.
There: Now you have a OpenGL driver library that has a GetProcAddress implementation. That wasn't that hard, was it?
How to check if function is declared in global scope at compile time?
My target operating system is very specific...
A possible solution might be, if you are using a recent GCC -probably as a cross-compiler for your weird target OS and ABI- to customize the gcc (or g++ etc...) compiler with your own MELT extension.
MELT is a domain specific language, implemented as a free software GCC plugin (mostly on Linux), to customize the GCC compiler.

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

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);

How to manipulate/return data with dynamically loaded functions (dlopen)?

I've spent days reading and re-reading every tutorials I've found on the subject, and spent hours (and even days) browsing related questions here at SO, but I still can't get the following to work. Accept my apologies if this is a duplicate: chances are that I've seen and re-read many times the duplicate questions but couldn't understand the relevance of the answers to my problem. With that out of the way...
I'm trying to implement a plugin architecture for my Application. Plugins are compiled and installed as libraries. At run time, the Application then uses dlopen() / dlsym() to load and link to the plugin's functions.
The idea is that plugins (libraries) will implement a set of functions to return data to the main Application, or manipulate data passed from the Application.
In order to test this idea, I tried to implement a function (inside the plugin) that would return the (human readable) name of the plugin itself (as a std::string). I thought that would be something simple to start with.... :-/
Here is what I got so far:
// Plugin.cpp
extern "C" void plugin_name(std::string *name) {
name = new std::string("Example plugin name");
}
// Application.cpp
void* handle = dlopen("libplugin.so", RTLD_LAZY);
typedef void (*plugin_t)(std::string*);
dlerror(); // Reset errors.
plugin_t call_plugin_name = (plugin_t) dlsym(handle, "plugin_name");
// ... Some error handling code.
std::string my_plugin_name;
call_plugin_name(&my_plugin_name);
dlclose(handle);
// More code that displays my_plugin_name.
I've tried many different combinations, including one that seemed more straigtforward (but didn't work any better) where the plugin name is returned:
// Plugin.cpp
extern "C" std::string plugin_name(void) {
return std::string("Example plugin name");
}
I know I'm close: the code compiles and the Application stopped crashing ;)
However, I've got an empty space where I'd expect seeing the actual plugin name.
All the tutorials I've read so far go very quickly over the mechanism by which data is passed both ways: plugin <=> Application. What I'm trying to do with a "simple" std::string, I wish to do later with much more complex objects (i.e. a plugin function would take an object by reference and change some of its properties). The tutorials more or less all stop at the point of creating a pointer with dlsym() and do not give much examples on how to use this pointer.
So, how to do all that?
Another pertinent question: do I use a common header that I'd use both with the Application and with the plugin and where I'd define the function calls signature? How would I do this and how would that help?
The signature of a function is generated from its name and argument types (return value type doesn't matter). When you declare function with extern "C", C symbol naming scheme is used which apparently can't handle C++ types like std::string. That's why passing std::string as an arguments doesn't work.
I can't explain why returning std::string doesn't work. Maybe different calling conventions are used.
Anyway the correct way of importing C++ code from a shared library is to return pointers to C++ types from entry points. And this entry points have to have arguments with types available in C. (Entry point is a documented function exported from a shared library)
Here is a good article on basic aspects of loading C++ classes from shared libraries. This article will answer your question throughly.
Please note that there are pitfalls when using exceptions thrown from a shared library to the main applications. And with dynamic_cast of objects created inside a library. I've mentioned this topics so that you could be somewhat prepared when you face this problems.
[edit]
To make my answer more clear I'll add a couple of examples.
To get the plugin name you can use:
extern "C" const char * plugin_name() {
return "Example plugin name";
}
// main.cc:
void* handle = dlopen("libplugin.so", RTLD_LAZY);
// ...
typedef const char * (*plugin_t)();
plugin_t call_plugin_name = (plugin_t) dlsym(handle, "plugin_name");
// ...
std::string my_plugin_name(call_plugin_name());
// use it
To really use the plugin functionality you should declare a base class in a header:
// plugin.h
class Plugin {
public:
virtual void doStuff() = 0;
virtual ~Plugin() = 0;
};
// plugin.cc
Plugin::~Plugin() {
}
// myplugin.cc
class MyPlugin : public Plugin {
virtual void doStuff() {
std::cout << "Hello from plugin" << std::endl;
}
};
extern "C" Plugin *createMyPluginInstance() {
return new MyPlugin;
}
Try:
extern "C" void plugin_name(std::string **name) {
*name = new std::string("Example plugin name");
}
...
std::string *my_plugin_name;
call_plugin_name(&my_plugin_name);
As you are assigning a copy of the pointer you passed as the argument, not the one you intended to assign.
EDIT Here you go:
File main.cpp
#include <iostream>
#include <dlfcn.h>
#include <string>
// Application.cpp
int main() {
void* handle = dlopen("libplugin.so", RTLD_LAZY);
typedef void (*plugin_t)(std::string**);
dlerror(); // Reset errors.
plugin_t call_plugin_name = (plugin_t) dlsym(handle, "plugin_name");
// ... Some error handling code.
std::string *my_plugin_name;
call_plugin_name(&my_plugin_name);
dlclose(handle);
// More code that displays my_plugin_name.
std::cout << "Plugin name is " << *my_plugin_name << std::endl;
delete my_plugin_name;
return 0;
}
File plugin.cpp
#include <string>
extern "C" void plugin_name(std::string **name) {
*name = new std::string("example plugin name");
}
Just a word of warning. Although this compiles and runs, passing C++ types across the dll boundry is risky and the above code is just your code fixed enough to compile and run, it is not safe and has very explicit memory handling. You may want to attack the problem in a different way.
Please have a read of this question and its answers. There are many opportunities for incompatibilities across the shared lib boundaries in C++.