C++ XE 5 Application and C++ Builder 5 dll compatibility - c++

I have BCB5 dll with method:
extern "C" void __declspec(dllexport) __stdcall SM(TDataSource *DS) {
TForm *form = new TForm(Application);
form->Width = 300;
form->Height = 300;
form->Name = "frm";
TDBGrid *grd = new TDBGrid(form);
grd->Parent = form;
grd->Name = "grd";
grd->Align = alClient;
grd->DataSource = DS;
form->ShowModal();
}
When I call this method from C++ builder 5 application, it's working fine.
try {
typedef void __declspec(dllexport) __stdcall SM(TDataSource *DS);
SM *Upload;
HINSTANCE hDll = LoadLibrary("main.dll");
Upload = (SM*) GetProcAddress(hDll,"SM");
Upload(DataSource1);
FreeLibrary(hDll);
}
catch (Exception *ex) {
ShowMessage(ex->Message);
}
But, if I'm trying to call this method from C++ XE 5 application, I get Access Violation.
Is there a way to solve the problem of data transmission from XE 5 application to BCB 5 dll without recompile dll in XE5?

It is not safe to pass/use RTL/VCL objects over the DLL boundary like you are doing, unless both EXE and DLL are compiled with Runtime Packages enabled so that they share a common instance of the same RTL and VCL frameworks (but then you have to deploy the RTL/VCL BPL binaries with your app).
Your DLL doesn't work in XE5 because the DLL is expecting the BCB5 version of the TDataSource component, not the XE5 version. No, they are not compatible, as they have different memory layouts and dependancies.
So your choices are to either:
recompile the DLL in XE5, and live with the risk that passing TDataSource over the DLL boundary is not safe in general, unless you enable Runtime Packages.
re-write the DLL to be a Runtime Package (BPL) instead. Then passing TDataSource between EXE and DLL is safe. However, Runtime Packages are version-specific, so you will need to compile separate BPLs if you need to continue using the code in both BCB5 and XE5.
re-write the DLL to not pass a TDataSource over the DLL boundary to begin with. Figure out another interop-safe way to exchange data between EXE and DLL.

Related

Calling C# programs from D

How can I call C# functions (a DLL) from D?
I have tried or am looking at the following:
Using the Derelict Mono package
(https://github.com/kubasz/derelict-mono)
Using Unmanaged Exports (See
Calling C# from C), D
to C to C#, eventually maybe eliminating the C.
The Derelict Mono approach works well for Hello World programs, however a larger DLL (with references to lots of other assemblies, each of which may or may not use genuine Windows API calls) fails as the DLL is not properly loaded.
Initial experiments with Unmanaged Exports result in errors with MSBUILD.
Try the following. First the D code:
module main;
import std.stdio;
import std.conv;
extern (C++) ulong receiveMe(ulong i);
extern (C++) ulong freeMe(ulong i);
void main() {
ulong l = receiveMe(0);
char* p = cast(char*)l;
char[] s = to!(char[])(p);
byte[] b = cast(byte[])(s.dup);
writeln("The answer is " ~ to!string(s));
ulong m = freeMe(0);
}
Then a C++/CLI shim:
#include "stdafx.h"
#using "...\CS-Interop\bin\x64\Debug\netstandard2.0\CS-Interop.dll"
using namespace System;
UInt64 sendMe(UInt64 arg) {
return CS_Interop::Test::receiveMe(42);
}
UInt64 freeMe(UInt64 arg) {
return CS_Interop::Test::freeMe(42);
}
Lastly the C#:
using System.Runtime.InteropServices;
using System.Text;
namespace CS_Interop {
public class Test {
public static byte[] buffer;
public static GCHandle gcbuf;
public static ulong receiveMe(ulong arg) {
string s = "I was a string " + arg;
s = (s.Length + 2) + s;
buffer = Encoding.ASCII.GetBytes(s);
gcbuf = GCHandle.Alloc(buffer, GCHandleType.Pinned);
ulong l = (ulong)gcbuf.AddrOfPinnedObject();
return l;
}
public static ulong freeMe(ulong arg) {
gcbuf.Free();
return 42;
}
}
}
I'm still looking at ways to get rid of that C++/CLI shim.
This code is written in such a way that you can poke around with the VS debugger.
This is very simple to set up and test in Visual Studio. With Visual D installed, first set up a C++/CLI project (NOT a Visual D project) and park the D and C++ code there. Then setup a C# DLL project under the D project.
It is one thing to call C# code from D, but another thing to get data back unless you are using only simple scalar types like int. The key lines of C# are
gcbuf = GCHandle.Alloc(buffer, GCHandleType.Pinned);
ulong l = (ulong)gcbuf.AddrOfPinnedObject();
where you first need to pin the thing you are sending back then send the address back to D. There is no tedious mucking about with marshaling in the C++ part, your D code just needs to be able to deal with whatever sits behind the pointer.
Be sure also to free the pinned pointer once you're done with it. Comment out the freeMe line in the D code and watch the memory usage grow (and grow) in VS.
Personally, I find the pin process a bit fickle as GCHandle.Alloc will only work when its first argument, be it a byte array or structure, contains blittable items.
See also https://learn.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types
I have a preliminary string passing solution from D to (a small amount of) C++ to C# based on the following articles: (I gave up on unmanaged exports from Robert Giesecke)
C# "Unmanaged Exports" (tutorial from Hans Passant)
Calling C# function from C++/CLI - Convert Return C# string to C String
The D to C++ integration with Visual D just works.
https://rainers.github.io/visuald/visuald/vcxproject.html (See Visual C/C++ Project Integration)
https://dlang.org/spec/cpp_interface.html
You can use Unmanaged Exports to call C# from D. I've done it without problems.
See https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports
However, when I tried Unmanaged Exports with Visual Studio 2017, I also could not get it to work. Unmanaged Exports worked well with VS2015. Considering the link is from July 2009, other aspects could have become stale.
Be sure to read the instructions carefully, and most importantly make sure you are building for x86 or x64, and not "any CPU". Marshalling the data will be another challenge.

ShowMessage not showing in Delphi DLL called from C++

I want to call this delphi code via a DLL from C++
procedure MyMessage; stdcall;
begin
ShowMessage(DLLName + ' more text');
end;
Using some delphi test code I see no problem , but from C++ no message box is shown.
i did the following C++ coding
// function prototype
typedef void(__stdcall*VoidCall)();
// prototype for info function inside DLL
extern "C" __declspec(dllimport) void __stdcall MyMessage();
MyMessage = (VoidCall)::GetProcAddress(load, "MyMessage");
MyMessage;
As I want to use as a next steps existing delphi forms with a wrapper DLL from C++ , I guess the solution of this problem will also enable me for the next task ....
The presented C++ code does not compile. It mixes binding of a dll via an import library with dynamic loading of a dll via LoadLibrary / GetProcAddress.
To load a DLL created with Delphi, it is easiest to use dynamic loading of the dll. Do this as follows:
// function prototype
typedef void(__stdcall*VoidCall)();
[...]
// Load the library
HMODULE lib = LoadLibrary("Project1.dll");
if (lib != 0)
{
__try
{
// Get the address to the exported function in the DLL
// and store it in the variable myMessageFunction
VoidCall myMessageFunction = (VoidCall) GetProcAddress(lib, "MyMessage");
// Call the function. Note you need the parenthesis even
// when there are no parameters to pass
myMessageFunction();
}
__finally
{
// Unload the library again. Note that you cannot use
// functions from the library after that. So only unload
// the dll once you don't need it anymore.
FreeLibrary(lib);
}
}
else // TODO: Error handling, dll cannot be loaded
If you want to use load time linking, you can create a *.lib file to be used with C++ for a DLL created with Delphi. Use the solution from this question.

C++ Builder XE2: Initializing a Data Module in a dll

I'm trying to create a dll that contains a VCL data module - the idea being that various applications can all load the same dll and use the same database code.
The data module itself is tested ok as part of an application - I've copied the form over to my dll project.
So in the dll entry point method, I need to initialize the data module:
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
//if I don't call this, I get an exception on initializing the data module
CoInitialize(NULL);
//initialize a standard VCL form; seems to works fine
//I'm not using Application->CreateForm as I don't want the form to appear straight away
if(!MyForm) MyForm = new TMyForm(Application);
//this doesn't work - the thread seems to hang in the TDataModule base constructor?
//I've also tried Application->CreateForm; same result
if(!MyDataModule) MyDataModule = new TMyDataModule(Application);
}
I've also seen something about how I need to call Application->Initialize before creating the form but this doesn't seem to make any difference.
Any ideas?
Thanks
You really should not be doing very much work in your DllEntryPoint() at all. Certainly not calling CoInitialize(), anyway. It is not the DLL's responsibility to call that when loaded. It is the calling app's responsibility before loading the DLL.
You should either:
export an additional function to initialize your DLL and then have the app it after loading the DLL (same for uninitialing the DLL before unloading it)
don't create your TForm/TDataModule until the first time the DLL actually needs them.
move your TForm/TDataModule into their own worker thread inside the DLL. In this case, you would then call CoIniitalize().
And in all cases, don't relay on the DLL's Application object to manage the lifetime of your TForm/TDataModule. Free them yourself instead before the DLL is unloaded.

Tcl Extension Calling a VB.NET DLL

I have a need to create a Tcl extension that calls a managed .NET DLL/Class Library. Currently, the structure of my application is Tcl > DLL Wrapper (C++ CLR) > .NET Class Library (VB.NET), where ">" represents a function call.
My VB.NET DLL just takes a value and returns it back, keeping it simple for now. In the end, this will do some more advanced stuff that makes use of some .NET functionality.
Public Class TestClass
Public Function TestFunction(ByVal param As Integer) As Integer
Return param
End Function
End Class
My Tcl Extension (C++ CLR) creates an object of the type above
int TestCmd(ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
// Check the number of arguments
if (objc != 2) {
Tcl_WrongNumArgs(interp, 0, objv, "arg");
return TCL_ERROR;
}
int param, result;
if (Tcl_GetIntFromObj(interp, objv[1], &param) != TCL_OK)
return TCL_ERROR;
SimpleLibrary::TestClass^ myclass = gcnew SimpleLibrary::TestClass(); //System.IO.FileNotFoundException
result = myclass->TestFunction(param);
Tcl_SetObjResult(interp, Tcl_NewIntObj(result));
return TCL_OK;
}
And finally, my Tcl script loads the extension and calls the function.
load SimpleTclExtension.dll
TestCmd 2
If my VB.NET DLL is in the same directory as my extension DLL, the extension crashes when it instantiates a TestClass object. I've noticed if the VB.NET DLL is relocated to C:\Tcl\bin, the extension will find it, and TestCmd can be called just fine. The problem is that this will eventually need to be deployed across a number of PCs, and it's preferred not to mingle my application's files with another application's.
It seems like there should be some configuration settings that will fix this problem, but I'm not sure where. Any help is greatly appreciated.
Firstly, depending on just what kind of Tcl application you are using you may want to look at Eagle which is a implementation of Tcl in CLR.
I think you are bumping into .Net's desire to only load assemblies from your application's directory or its immediate subdirectories. The application here is the tclsh/wish executable which is why moving the .Net assembly makes it load. This is something you can fix with suitable manifests or calls to the API to permit assembly loading from alternate locations. In this case I think you will need to run some initialization code in your Tcl extension when it gets loaded into the Tcl interpreter to init the CLR and add the extensions location as a suitable place to load assemblies from. It has been a while since I was looking at this so I forgot the details but I think you want to look at the AppDomain object and check the assembly loading path properties associated with that or its child objects. Try AppDomain.RelativeSearchPath
To be more specific, Eagle includes Garuda which is a Tcl extension built specifically to allow calling .Net from Tcl

How to load a VC++ CLR library in MFC application?

HI I have a application developed in VC++6.0 ,now I want use some new features from .NET and developed a library, to develop this library I have to use the CLR-VC++ now I packaged this in a DLL.Now I need to call the routine of this DLL in my MFC application.
I tried to write a small MFC application to load this DLL, All the time the LoadLibrary() call is failing #err =126, module not found.I check the the dll with dependency walker everthig is fine there. Please Help me in this regard.
If possible provide me a sample code or link.
Thanks in advance
-Sachin
Use ClrCreateManagedInstance to create a COM-Callable-Wrapper for the object you want to call. Then use it like any other COM type.
you have to go to property page -> Common properties ->Add New reference and include you
CLR Address there .
I have a native C++ application which uses a managed C++ assembly and loads it with LoadLibrary() without problems. I had to do two things, however, before LoadLibrary() worked:
Make sure that the current directory is the one where the managed assembly resides (use chdir() to change directory)
In the managed assembly, the first function invoked by native code only defines the handler for AppDomain::CurrentDomain->AssemblyResolve event which explicitly loads assemblies from the folder of the managed application. It then invokes another managed function to do the rest of the initialization.
The reason for the last point is that CLR attempts to load an assembly dependency only if a function uses it. So I had to ensure that types in non-system assemblies are not referenced before the AssemblyResolve handler has been defined.
ref class AssemblyResolver
{
public:
/// The path where the assemblies are searched
property String^ Path
{
String^ get()
{ return path_; }
}
explicit AssemblyResolver(String^ path)
: path_(path)
{ /* Void */ }
Assembly^ ResolveHandler(Object^ sender, ResolveEventArgs^ args)
{
// The name passed here contains other information as well
String^ dll_name = args->Name->Substring(0, args->Name->IndexOf(','));
String^ path = System::IO::Path::Combine(path_, dll_name+".dll");
if ( File::Exists(path) )
return Assembly::LoadFile(path);
return nullptr;
}
private:
String^ path_;
};
extern "C" __declspec(dllexport) void Initialize()
{
String^ path = "The path where the managed code resides";
AssemblyResolver^ resolver = gcnew AssemblyResolver(path);
AppDomain::CurrentDomain->AssemblyResolve += gcnew ResolveEventHandler(
resolver,
&AssemblyResolver::ResolveHandler
);
FunctionWhichUsesOtherManagedTypes();
}