Using a passed by reference string array to runtime loaded dll function - c++

I'm creating a DLL lib that should be used during runtime (i.e. loaded in Windows 7 with LoadLibrary, closed with FreeLibrary and function handles given by GetProcAddress). This is being done in C++ using Borland C++ Builder. All functions in the DLL should receive some value by reference as parameter (normally std::string).
By now the method I'm using to do this is the following (example) (summarized):
typedef void (*HIS_validity)(string &);
//LoadLibrary
HIS_validity fValidity = (HIS_validity) GetProcAddress(frMain->HIS_DLL.hisLibrary,"checkForValidity");
if (fValidity == NULL) return;
string testeValidade;
fValidity(testeValidade);
const AnsiString testeValidade2(testeValidade.c_str());
if (testeValidade2 != "...")
//etc...
In the DLL:
extern "C" void LIBRARY_API checkForValidity(string &str);
void checkForValidity(string &str)
{
str = "...";
}
Now this code is running fine. The problem is that in some functions I want to pass a whole array of strings by reference. Previously I discovered how to pass a string array by reference here and I though it would be just a matter of ajusting things accordingly:
typedef void (*HIS_patientData)(string (&)[32]);
HIS_patientData fPatientData = (HIS_patientData) GetProcAddress(frMain->HIS_DLL.hisLibrary,"patientDataFields");
string strDado2[32];
fPatientData(strDado2);
frMain->pluginData.patientData.numProntuario = AnsiString(strDado2[cont1++].c_str());
frMain->pluginData.patientData.pacNome = AnsiString(strDado2[cont1++].c_str());
In the DLL:
extern "C" void LIBRARY_API patientDataFields(string (&str)[32]);
void patientDataFields(string (&str)[32])
{
str[0] = "One";
str[1] = "Two";
str[2] = "Three";
//....
}
But here the problem appears. When I compile and run my application, the same problem always come up: if my function in the DLL has only two data attributed to 'str[]', the code goes one after 'fPatientData(strDado2);' but when I read the content of strDado2[0], it has the value of str[1] and strDado2[1] has NULL inside! By the other hand, if I add three or more attributions to 'str[]' in my DLL function, the software always crash when it comes to 'fPatientData(strDado2);' with a pop-up telling "access violation ... in module libstdc++-6.dll".
And I have no ideia what the problem is :T
Thanks for any help,
Momergil

Ok, it seems I found the answer to all such problems... Namely, I'm trying to return a C++ class (std::string) in a "extern "C"" function. It was just a matter of making it return a standart const char* that everything started to run just fine.
Thanks for the contributors,
Momergil

Related

Passing std::string with dllexport

I'm trying to make some sort of C++ "bridge" to connect an unmanaged C++ dll on one end (without modifying their code) to a C# Wrapper which uses DllImport for various imports.
I was able to pass a C# string to my bridge using char pointers, but the receiving Dll needs to receive std::string, so I tried with std::string(foo); with no luck, it always gets transformed into weird characters.
The structure is the following :
C# Wrapper
[DllImport(#"Bridge.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void initDetector(string foo, int something = 0);
C++ Bridge
extern "C" __declspec(dllexport) void initCppClass(char* foo, int something)
{
std::string bar = std::string(foo);
std::cout << bar << std::endl; //Returns "foo"
instance = new CppClass(bar, something);
}
C++ Imported DLL (not allowed to change code here)
CppClass::CppClass(std::string foo, int something)
{
std::cout << foo << std::endl; //Returns garbage
}
Note that this constructor is for demonstration purposes only, as I cannot disclose the original code.
I originally tried passing the char* directly to the constructor but that didn't work either. Is there something I'm missing here ?
I think the problem is different string encoding.
Try adding CharSet = CharSet.Ansi in C#, like this:
[DllImport(#"Bridge.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
However, please read the documentation of that C++ dll API. A lot of C++ code, especially if that’s cross-platform code, expect UTF8-encoded strings. If that’s your case, instead change the bridge to
extern "C" __declspec(dllexport) void initCppClass(const wchar_t* foo, int something)
And write code to convert the string from UTF16-encoded C pointer into UTF8-encoded std::string, see this answer for an example.
Update: another possible reason is different STL, or different CRT. When you pass std::string or any other STL objects objects across DLL boundaries, you have to use same compiler & same version of it, same build settings for (e.g. in VC++, std::strings memory layout differs between debug and release builds), and also both DLLs must link to CRT dynamically., e.g. Multi-threaded DLL (/MD)

How do I return a unicode string / wstring / CStringW from a C++ dll to InstallScript?

I am using InstallShield 2013 Premium. I created a C++ dll in Visual Studio 2010 to provide some functionality I could not achieve with InstallScript alone. My C++ function needs to return a small string (a username) to the InstallScript after doing considerable work to get this value.
Throughout the C++ am I using CStringW to represent my strings. Ideally, I would like to return it as Unicode, but I'm content with ANSI if that's my only option. I have tried numerous approaches with CStringW, std::wstring, std::string, LPCTSTR, LPSTR, char *... I tried direct returns, and attempts to return by reference. Nothing works!
Sometimes the dll function hangs, sometimes it throws an exception, at best it returns garbage values with non-printing characters. The official documentation on this does not seem accurate (it doesn't work for me!). Extensive Googling, and searching the Flexera boards produce "solutions" from others struggling with the same ridiculous problem, and yet non of those work for me either...
I didn't try this until the end, as I took for granted that you could pass strings between dlls and InstallScript easily enough. In retrospect, I should have started with the interface between the two and then developed the dll functionality after that.
Thanks for the help guys! I finally figured this out for myself though. There are multiple facets to the solution, however, which I have not found documented or suggested elsewhere.
Major points
1) return a WCHAR * from C++
2) use WSTRING as the corresponding return type in the InstallScript prototype
3) return it into a regular STRING variable in InstallScript, and treat it like any other
4) retain the value the WCHAR * points to in the C++ dll in a static variable, otherwise it apparently gets deleted and the pointer becomes invalid
If you've gotten far enough to find yourself in the same boat, I probably don't need to serve up every detail, but here's a chunk of example code to help you along:
Visual Studio Def File
LIBRARY MyIsDllHelper
EXPORTS
getSomeStringW #1
C++ Header
#ifdef MYISDLLHELPER_EXPORTS
#define MYISDLLHELPER_API __declspec(dllexport)
#else
#define MYISDLLHELPER_API __declspec(dllimport)
#endif
#include <stdexcept>
#include <atlstr.h>
namespace MyIsDllHelper
{
class MyIsDllHelper
{
public:
static MYISDLLHELPER_API WCHAR * getSomeStringW();
};
}
C++ Source
#include "stdafx.h"
#include "MyIsDllHelper.h"
static CStringW someStringRetained;
CStringW getTheString()
{
CStringW s;
// do whatever...
return s;
}
WCHAR * MyIsDllHelper::MyIsDllHelper::getSomeStringW()
{
someStringRetained = getTheString();
return someStringRetained.GetBuffer( someStringRetained.GetLength() ) + L'\0';
}
InstallScript
#define HELPER_DLL_FILE_NAME "MyIsDllHelper.dll"
prototype WSTRING MyIsDllHelper.getSomeStringW();
function DoSomething( hMSI )
STRING svSomeString;
STRING svDllPath;
begin
// Find the .dll file path. (A custom function)
GetSupportFilePath( HELPER_DLL_FILE_NAME, TRUE, svDllPath );
// Load the .dll file into memory.
if( UseDLL( svDllPath ) != 0 ) then
MessageBox ("Could not load dll: " + svDllPath, SEVERE );
abort;
endif;
// Get the string from the dll
try
svSomeString = MyIsDllHelper.getSomeStringW();
catch
MessageBox( "Could not execute dll function: MyIsDllHelper.getSomeStringW", SEVERE );
abort;
endcatch;
// Remove the .dll file from memory.
if( UnUseDLL( svDllPath ) < 0 ) then
MessageBox ("Could not unload dll: " + svDllPath, SEVERE );
abort;
endif;
// Use the string
MessageBox( "svSomeString: [" + svSomeString + "]", INFORMATION );
end;
You're best off when you can make your interface use C approaches rather than C++ ones. Match the interface of functions like GetEnvironmentVariable in which your function accepts a pointer to a buffer (and for correctness a size of that buffer), and then writes into that buffer. The majority of your implementation shouldn't have to change, as long as you can finish with something like a StringCchCopy from your CString into the buffer.
Since you specifically mention CStringW and other Unicode string types, I'd suggest choosing LPWSTR (rather than LPTSTR) for the interface type.
Then all that's left is declaring this for consumption by InstallScript. This means the prototype should use WSTRING and BYREF. If the function interface is the same as GetEnvironmentVariableW, the prototype should look something like this:
prototype MyFunc(WSTRING, BYREF WSTRING, NUMBER);
You can use strings, but I guess the problem is with the encoding.
Have a look here: https://adventuresinscm.wordpress.com/2014/01/12/unicode-files-and-installshield/

passing pointers to extern C function in a DLL from VB

I am a C++ (MSVC) writer, VB newbie trying to assist an expert VB.net writer who has just not done this task before.
We wish to develop both C/C++ and VB applications to use a DLL written in C++ with C extern-ed API functions. The C++ program is working just fine. It's VB where we are having difficulties.
The DLL provides an extern C function:
RegisterCallback( void* cbFuncPtr, void* dataPtr );
NOTE 1: See my note below for a design change and the reasons we made it.
NOTE 2: Additional update added as an answer below.
where the callback function havs this C typedef:
typedef (void)(* CALL_NACK)(void*);
The cbFuncPtr is expected to be a function pointer to some VB function that will get called as the CALL_BACK. The dataPtr is a pointer to a data structure that has this C definition:
typedef struct
{
int retCode;
void* a_C_ptr;
char message[500];
} cbResponse_t;
where a_C_ptr is an internal pointer in the DLL that the VB can cast tolong`. It uniquely identifies where in the DLL the callback was made and allows the VB function to recognize calls from same/different locations.
We are able to access and run the RegisterCallback() function from VB just fine. Logging shows we get there and that data is passed in. It is the actual data that seems to be the problem.
In reading about a million forum entries we have learned that VB doesn't know what pointers are and that a VB structure is more than just organized memory. We're pretty sure the "address" of a VB structure is not what C thinks an address is. We've seen repeated references to "marshaling" and "managed data", but lack enough understanding to know what that is telling us.
How should we code VB to give the DLL the execution address of its callback function and how do we code up a VB construct that the DLL can fill in just as it does for C++?
Might we need a DLL function where the calling app can say "C" or "VB" andhave the DLL handle the sturcture pointers differently? If so, how would one code up C to fill in the VB structure?
This is a bit too big and deep to just be an edit to the original posting...
From the link posted by #DaveNewman, I extracted this gem:
Here's a bit about the compact framework, but it's the same in the
grown-ups framework:
The .NET Compact Framework supports automatic marshaling of structures
and classes that contain simple types. All fields are laid out
sequentially in memory in the same order as they appear in the
structure or class definition. Both classes and structures appear in
native code as pointers to C/C++ structs.
Objects in the managed heap can be moved around in memory at any time
by the garbage collector, so their physical addresses may change
without notice. P/Invoke automatically pins down managed objects
passed by reference for the duration of each method call. This means
pointers passed to unmanaged code will be valid for that one call.
Bear in mind that there is no guarantee that the object will not be
moved to a different memory address on subsequent calls.
http://msdn.microsoft.com/en-us/library/aa446538.aspx#netcfmarshallingtypes_topic6
This is major hurdle for a RegisterCallback( fcnPtr, dataPtr) function. The pointer passed in at registration time could change at any time the RegisterCallback() is not the current statement. The posting author summed it up this way
You don't need to do anything as the structures are pinned automatically for duration of the call.
implying, of course, not pinned down outside the call.
For this reason we decided on a design change to have the response structure built in, so to speak, the C/C++ world of the DLL, not in VB's space. That way it'll stay put. The actual callback function's signature will remain unchanged so the VB program can know where the DLL put the response. This also allows the responders in the DLL to allocate separate response structures for separate needs.
Once again my update is too large for a mere comment!
Update 18 Apr 2013:
Well, the attempt to use the code from Calling Managed Code from Unmanaged Code cited above was a bust. We ended up having to add /clr to the DLL make turning the DLL into managed code, which made it unusable from a C application.
We are now testing the example at Callback Sample which I was able to show made a DLL that worked with both VB and C++. You'd need to have the PinvokeLib.dll Source to make this work.
Here is the code for the C++ (C really) tester. Compiled as a MSVC project.
NOTE: Notice the __cdecl in this line:
typedef bool (__cdecl *FPtr)(BOOL_FP_INT fp, int i );
It was the secret I had to find. The DLL and this app are compiled with __cdecl linkage, not __stdcall. They are the default in VC++ and I just used the defaults. I tried changing everything to __stdcall but that didn't work. Has to be __cdecl.
// PinvokeTester.cpp : Defines the entry point for the console application.
//
#include <stdio.h>
#include <cstdio>
#include <stdlib.h>
#include <cstdlib>
#include <string.h>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <Windows.h>
#define PINVOKELIB_API __declspec(dllimport)
HINSTANCE hLib; // Windows DLL handle
bool CALLBACK VBCallBack( int value );
bool AttachLibrary( void );
void * GetFuncAddress( HINSTANCE hLib, const char* procname );
int main(int argc, char* argv[])
{
if ( !AttachLibrary() )
{
printf( "Lib did not attach.\n" );
exit(1);
}
typedef bool (CALLBACK *BOOL_FP_INT)(int i );
typedef bool (__cdecl *FPtr)(BOOL_FP_INT fp, int i );
FPtr TestCallBack = (FPtr)GetFuncAddress( hLib, "TestCallBack" );
TestCallBack( (BOOL_FP_INT)VBCallBack, 255 );
return 0;
}
bool CALLBACK VBCallBack( int value )
{
printf( "\nCallback called with param: %d", value);
return true;
}
bool AttachLibrary( void )
{
// Get a var for the IPC-dll library.
std::string dllName;
/*--- First, link to the IPC-dll library or report failure to do so. ---*/
dllName = ".\\PinvokeLib";
if ( NULL == (hLib = LoadLibraryA( dllName.c_str() )) )
{
printf( "\nERROR: Library \"%s\" Not Found or Failed to Load. \n\n", dllName.c_str() );
printf( "\"%s\"\n", GetLastError() );
return false;
}
return true;
}
//=====================================================================
void * GetFuncAddress( HINSTANCE hLib, const char* procname )
{
void * procAddr = NULL;
procAddr = (void *)GetProcAddress( hLib, procname );
// If the symbol wasn't found, handle error ---------------------
if ( NULL == procAddr )
{
std::cout << "ERROR: Could not get an address for the \""
<< procname << "\" function. : "
<< GetLastError() << std::endl;
exit( 7 );
procAddr = (void*)NULL;
}
return procAddr;
}

C++ DLL returning pointer to std::list<std::wstring>

I got a dll with the following prototype:
DLL_EXPORT std::list<std::wstring>* c_ExplodeWStringToList(std::wstring in_delimiter, std::wstring in_string, int in_limit);
The application uses this like that:
std::list<std::wstring>* exploded = mydllclass->c_ExplodeWStringToList(L" ", in_command.c_str(), 0);
This works great under XP 32, but when I try this at home with my Vista 64 my program just closes itself. No error and no warning?
Some days ago the DLL was returning the list directly - no pointer. But I switched to VC++ 2010 Express, and I could not compile my DLL without this modification.
Anything I am not seeing here?
Thank you :)
Update:
DLL_EXPORT std::list<std::wstring>* c_ExplodeWStringToList(std::wstring in_delimiter, std::wstring in_string, int in_limit)
{
std::list<std::wstring>* returnlist = new std::list<std::wstring>();
std::list<std::wstring>* stringlist = new std::list<std::wstring>();
UINT pos = 0;
while(true)
{
pos = in_string.find(in_delimiter, 0);
if(pos == std::string::npos)
{
stringlist->push_back(in_string.substr(0, pos));
break;
}
else
{
stringlist->push_back(in_string.substr(0, pos));
in_string = in_string.substr(pos + in_delimiter.length());
}
}
// ****
// Here is missing some code I've commented out while searching for the error.
// ****
returnlist = stringlist;
return returnlist;
}
T
I didn't dig into the code, but a conclusion I came to regarding working with DLLs is to not return anything but primitive types from DLL functions. This is because due to different compilers or different switches or project settings structs and classes are not aligned the same not have the same size in the DLL and in the code calling the DLL.
So returning a list from a DLL might be considered malformed in the caller application.
Same thing regards throwing exceptions from a DLL - the class thrown might be misinterpreted by the catching code.
So, best is export only C functions that return primitive types (to denote error codes).

How to pass a char* from C to CPP?

A little background:
I've got a library of C code that is part of larger system (all C). For this particular part of the C library a GUI has to be created which allows users to play around with the options. For the GUI I've chosen QT, since cross-platform support is desired.
I'm using Eclipse and MinGW as IDE and compiler (but I think the question is more language than compiler specific?).
With QT I've created a widget that holds a pointer to a struct implemented in C that contains pointers to several functions that perform the logic of the C library.
//QTWidget.cpp
extern "C" {
#include "c-src/CLogic.h"
//extern char* textHelper;
}
QTWidget::QTWidget(QWidget *parent)
{
//rtw is a struct that contains a function pointer to a member of QTWidget
this->rtw.displayText = &QTWidget::displayText;
this->clogic = CLogic_getInstance(&rtw);
}
//Public SLOT, connected to a button's clicked SIGNAL
void QTWidget::buttonClicked()
{
this->clogic->buttonClicked();
}
void QTWidget::displayText(char *text, int position)
{
//I've tried creating a QString from the char*, but this does not work at all.
//ui.textItem->setText(textHelper);
ui.textItem->setText(text);
}
When the user presses a button in the GUI, the method QTWidget::buttonClicked() is called, which tells the C library to do something. Note the the CLogic struct has a reference to the the QTWidget in the form of a struct RefToWidget which holds a function pointer.
//CLogic.c
static CLogic instance;
void CLogic_buttonClicked()
{
//I've tried several variants here, such as making a global
//char* textHelper = "Hello World";
//that is referenced by using the "extern" keyword in the CPP file above.
instance.rtw->displayText("Hello World", 1);
}
CLogic* CLogic_getInstance(RefToWidget *rtw)
{
instance.rtw = rtw;
instance.buttonClicked = &CLogic_buttonClicked();
}
When debugging this program, I find that all the function calls are executed as intended (when I press a button, the QT slot buttonClicked() is called, the CLogic_buttonClicked() is called, which calls the QTWidget::displayText() as planned, but in this last call the parameters are invalid. The char* text points to 0x1 and claims to be pointing to memory out of bounds, while the int position looks like some random number (uninitialized).
How do I pass this data from C to CPP?
EDIT #Luccas Matteis:
#ifdef __cplusplus
#include "QTWidget.h"
extern "C" {
#endif
struct RefToWidget{
#ifdef __cplusplus
void (QTWidget::*displayLine)(char* text, int lineNumber);
#else
void (*displayLine)(char* text, int lineNumber);
#endif
};
typedef struct RefToWidget RefToWidget;
#ifdef __cplusplus
}
#endif
As said above the function calls behave as expected, but the data is not passed "correctly" (even though that when I look at it, the code seems a bit... weird... ;))
Your problem is not passing the char * from C to C++ but calling a C++ function from C. I presume that CLogic.c is compiled as a C library? If not, can you rename it to CLogic.cpp or use a compiler switch to force it to be compiled as C++ even though it has a C file extension?
If you want a C++ GUI on a C library/system, you need to use proper Model-View-Controller logic. Here the C++ code is the View and the Controller while the C code is the Model (as best as I can tell from your description). You need to make it so you set and get data from the model but the model never calls the View or Controller as you are trying to do.
Think about what you are REALLY trying to do. If you just want to display a static string when the button is pressed, why go to the bother of calling into CLogic.c? If you want to display a string that depends upon the state of the CLogic instance then instead do something like:
void QTWidget::buttonClicked()
{
char *display_text = this->clogic->get_button_click_text();
ui.textItem->setText(display_text);
}
I guess the problem is that the structure holds a pointer to a member function. The member function probably expects the first parameter to be 'this' - the object it refers to. So, in fact what you are seeing in debug as text is the second parameter.
A 'solution' would probably be to do something like "instance.rtw->displayText(instance.rtw, "Hello World", 1)", but have no idea if it is portable etc.
Edit: Saying it explicitly: the 'solution' stated above is just to try and check if this is the problem. As the comments are saying this is a horrible hack that might not work even on the same compiler.
I don't think that you should call a C++ method from C. Normally you need to go through a static method that dereferences a pointer argument