My problem is with understanding the finer point of mixed langage programming
and accessing API's in external libraries. My skills at C++ are nonexistent
and at VB, mediocre.
I have a c++ dll compiled (portaudio library), and am trying to access it from VB
(Visual Studio 2005).
I am getting MarshallDirectiveException errors when calling a function, I believe because
I am interacting incorrectly with the dll.
the C++ function and structures are defined as follows:
header info:
typedef int PaHostApiIndex;
...
typedef double PaTime;
...
typedef struct PaDeviceInfo
{
int structVersion; /* this is struct version 2 */
const char *name;
PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/
int maxInputChannels;
int maxOutputChannels;
PaTime defaultLowInputLatency;
PaTime defaultLowOutputLatency;
PaTime defaultHighInputLatency;
PaTime defaultHighOutputLatency;
double defaultSampleRate;
} PaDeviceInfo;
...
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device );
program usage from docs:
const PaDeviceInfo* Pa_GetDeviceInfo ( PaDeviceIndex device )
Retrieve a pointer to a PaDeviceInfo structure containing information about the specified
device.
Returns:
A pointer to an immutable PaDeviceInfo structure. If the device parameter is out of range
the function returns NULL.
Parameters:
device A valid device index in the range 0 to (Pa_GetDeviceCount()-1)
In the VB program I have:
Private Declare Function Pa_GetDeviceInfo Lib "portaudio_x86.dll" (ByVal dindex As Integer) As PaDeviceInfo
...
Private Structure PaDeviceInfo
Dim structVersion As Integer
<MarshalAs(Runtime.InteropServices.UnmanagedType.LPStr)> Dim name As String
Dim hostapi As Integer
Dim maxInputChannels As Integer
Dim maxOutputChannels As Integer
Dim defaultLowInputLatency As Double
Dim defaultLowOutputLatency As Double
Dim defaultHighInputLatency As Double
Dim defaultHighOutputLatency As Double
Dim defaultSampleRate As Double
End Structure
...
Dim di As New PaDeviceInfo
di = Pa_GetDeviceInfo(outputParameters.device)
This feels wrong as docs state Pa_GetDeviceInfo returns a POINTER to a structure containing info
about the structure, implying function creates the structure initially.
I am completely new to mixed language programming, a c++ utter noob, and a poor VB programmer.
Can anyone guide me in the correct way to attack this problem ?
My feeling is that I need to understand how to get VB to reference a struct in memry created in
the dll, so I need to get vb to understand 'pointer to thing' as a function return.
Much appreciation for any assistance rendered. Please don't just say rtfm, I'm up to my eyes in FM's at
the minute, I just don't know where to look.
Many thanks,
David
Your API function declaration is wrong. The function returns a pointer which is not reflected in your code. The signature translates to VB as follows:
Private Declare Function Pa_GetDeviceInfo Lib "portaudio_x86.dll" ( _
ByVal dindex As Integer _
) As IntPtr
Of course, using an IntPtr directly is not easy. In fact, quite some marshalling is involved:
Dim obj As PaDeviceInfo = _
DirectCast(Marshal.PtrToStructure(ptr, GetType(PaDeviceInfo)), PaDeviceInfo)
(More or less important) side note: since your DLL apparently creates a new object in memory, it also needs to release/destroy it. Be sure to call the appropriate function after using the structure.
Yes, your structure is wrong. You indeed have to get a pointer and then read the memory to which it "points".
I've done external DLL calling in C++ before and it generally involves a huge wading-through of documentation. I don't think anyone here is going to do that for you but I'll try to point you in the proper direction.
First, a pointer is an address, a value which "points" to some location in memory. "dereferencing" a pointer is reading the memory that the pointers to (and if you read or write the wrong memory, the memory can get upset and kill your program).
Further, at a low level, calling a DLL involves copying bits of information to the stack and then having the function retrieve them. The "calling conventions" describe exactly how this is done - there are "c", pascal, and other such conventions.
You will want to call the DLL's function, get a pointer, copy the information pointed-to into your local structure and then continue. The hard thing will be figuring out exactly how to declare the DLL function. If your library documentation has some sample code, that might be where to start.
A short Google does not even show any consistent way to deal with pointers in VB at all. One idea would be to create a short C++ program which would call the DLL and return an object by value. I don't know if this would help but such kludges come up when dealing with external libraries.
Good Luck
Related
I have been able to achieve a program that imports a C++ DLL function and uses it correctly to get the required calculated values. I'm returning the char* values to VB.net using an intptr pointer.
It works fine, however, I cant seem to rest or clear the memory space where the result from the function is stored. When i call the function the first time it gives me the right answers, when it is called the second time, it gives me both the first and the second answers.
Here are relevant parts of my code:
CPM.cpp - The function that calculated the return variables in the cpp file
char* CPMfn(char* sdatabase, int project_num)
{
/* Retrieve data from database and calculate CPM for the selected project number*/
char* testvector = getCPM(sdatabase, project_num);
return testvector;
}
CPM.h - the header file for exporting the function
#pragma once
#ifdef CPM_EXPORTS
#define CPM_API __declspec(dllexport)
#else
#define CPM_API __declspec(dllimport)
#endif
extern "C" CPM_API char* CPMfn(char*, int);
VB.net code to import the DLL, declare the function and use it
'' Import C++ CPM Calculation function from CPM DLL
<DllImport("CPM.dll", CallingConvention:=CallingConvention.Cdecl)>
Private Shared Function CPMfn(ByVal dbstring As Char(), ByVal task As Int32) As System.IntPtr
End Function
'' Get CPM results from DLL function with database location string and selected project number
CPMresults = CPMfn(DBString, Val(Project_IDTextBox.Text))
CPMvalues = Marshal.PtrToStringAnsi(CPMresults)
If CPMvalues.Length() = 0 Then
MsgBox("No tasks for seleccted project")
Else
MsgBox(CPMvalues) ' Show CPM values
End If
As I run it consecutively the string just keeps getting longer i.e. 4th function call would return the values for project 1, 2, 3 and 4.
I've checked online for the past few hours trying to figure out how to return char* from C++ DLLs and then how to clear the intptr.
I just haven't had any luck with the solutions suggested. I would really really appreciate some help.
Thank you!
Per the following MSDN documentation:
Default Marshaling Behavior
The interop marshaler always attempts to free memory allocated by unmanaged code. This behavior complies with COM memory management rules, but differs from the rules that govern native C++.
Confusion can arise if you anticipate native C++ behavior (no memory freeing) when using platform invoke, which automatically frees memory for pointers. For example, calling the following unmanaged method from a C++ DLL does not automatically free any memory.
Unmanaged signature
BSTR MethodOne (BSTR b) {
return b;
}
However, if you define the method as a platform invoke prototype, replace each BSTR type with a String type, and call MethodOne, the common language runtime attempts to free b twice. You can change the marshaling behavior by using IntPtr types rather than String types.
The runtime always uses the CoTaskMemFree method to free memory. If the memory you are working with was not allocated with the CoTaskMemAlloc method, you must use an IntPtr and free the memory manually using the appropriate method. Similarly, you can avoid automatic memory freeing in situations where memory should never be freed, such as when using the GetCommandLine function from Kernel32.dll, which returns a pointer to kernel memory. For details on manually freeing memory, see the Buffers Sample.
So, the DLL needs to dynamically allocate a new char* string each time it returns, and the VB code needs to be told how to free that string properly. There are a few ways to handle this:
have the DLL return a char* (or wchar_t*) string that is allocated with CoTaskMemAlloc(), and then change the PInvoke to take the return value as a string marshaled as an UnmanagedType.LPStr (or UnmanagedType.LPWStr). The .NET runtime will then free the memory for you using CoTaskMemFree().
change the DLL to return a COM BSTR string that is allocated with SysAllocString(), and then change the PInvoke to take the return value as a string marshaled as an UnmanagedType.BStr. The .NET runtime will then free the memory for you using SysFreeString().
if you want to have the DLL return a raw char* (or wchar_t*) string and have PInvoke treat it as an IntPtr (because it is not allocated using CoTaskMemAlloc() to SysAllocString()), then the .NET runtime will have no way of knowing how the string was allocated and so cannot free the memory automatically. So either:
the IntPtr will have to be passed back to the DLL when done being used, since only the DLL will know how the memory was allocated, so only the DLL will be able to free it properly.
have the DLL allocate the char* (or wchar_t*) string using LocalAlloc(), and then the .NET code can use Marshal.PtrToStringAnsi() (or Marshal.PtrToStringUni()) to get a string from the IntPtr, and then pass the IntPtr to Marshal.FreeHGlobal() when done using it.
See the following article for more details (it is written for C#, but you can adapt it for VB.NET):
Returning Strings from a C++ API to C#
Thanks a lot #Remy Lebeau. I've tried implementing the CoTaskMemAlloc() method and it works!. My code is as follows:
In the cpp file i've edited the return value to be allocated using the CoTaskMemAlloc()
char* CPMfn(char* sdatabase, int project_num)
{
/* Retrieve data from database and calculate CPM for the selected project number*/
char* CPMvector = getCPM(sdatabase, project_num);
/* Store results in specially allocated memory space that can easily be deallocated when this DLL is called*/
ULONG ulSize = strlen(CPMvector) + sizeof(char);
char* ReturnValue = NULL;
ReturnValue = (char*)::CoTaskMemAlloc(ulSize);
// Copy the contents of CPMvector
// to the memory pointed to by ReturnValue.
int charlen = strlen(CPMvector);
strcpy_s(ReturnValue, charlen + 1, CPMvector);
// Return
return ReturnValue;
}
In the VB.net file I've written the Dllimport and Marshalling code as follows:
'' Import C++ CPM Calculation function from CPM DLL
<DllImport("CPM.dll", CallingConvention:=CallingConvention.Cdecl, CharSet:=CharSet.Ansi)>
Private Shared Function CPMfn(ByVal dbstring As String, ByVal task As Int32) As <MarshalAs(UnmanagedType.LPStr)> String
End Function
'' Get CPM results from DLL function
Dim teststring As String = CPMfn(cDBString, Val(Project_IDTextBox.Text))
Alternatively I've also tried freeing up the allocated memory manually using the GlobalAlloc() and Marshal.FreeHGlobal() functions but i'm getting the same results (i.e. first call = 1,2,3\n, second call = 1,2,3\n,4,5,6\n instead of just 4,5,6\n).
Here's my code with the GlobalAlloc() method:
In .cpp file
ReturnValue = (char*)::GlobalAlloc(GMEM_FIXED, ulSize);
strcpy_s(ReturnValue, strlen(CPMvector) + 1, CPMvector);
And in VB.net file
<DllImport("CPM.dll", CallingConvention:=CallingConvention.Cdecl)>
Private Shared Function CPMfn(ByVal dbstring As Char(), ByVal task As Int32) As System.IntPtr
End Function
Dim CPMresults As IntPtr = CPMfn(cDBString, Val(Project_IDTextBox.Text))
Dim CPMvalues As String = Marshal.PtrToStringAnsi(CPMresults)
Marshal.FreeHGlobal(CPMresults)
CPMresults = IntPtr.Zero
Thanks for all the help so far!
I am creating a DLL in C++, it would be used in a Delphi 7 project.
This question is related to this one, where I present two functions Validate and GetToken only that now they will be done in C++ and the array of strings GetToken produces would be sent back to Delphi.
The problems is I don't know how to create the function in the dll that will return the array of string in c++, and I don't know how it would be stored for further use in Delphi.
The declaration of the function is as follows:
function GetToken(Chain:string):Arrayofstring;
According to your code review, the Delphi code expects the function to have the following signature:
function GetToken(Chain: AnsiString): array of AnsiString;
You cannot write such a function in C++. C++ doesn't know what Delphi strings are, and it doesn't know what Delphi dynamic arrays are, either. Both types need to be allocated from Delphi's memory manager, which your C++ DLL won't have access to. Furthermore, C++ doesn't know how to use Delphi's register calling convention.
The DLL interface was designed poorly. DLLs should never use language-specific types unless it was the designer's intention to exclude all other languages. (And in this case, even later versions of the same language are excluded because the definition of AnsiString changed in Delphi 2009 to include more metadata that Delphi 7 won't handle properly.) The safest calling convention to choose is generally stdcall. It's what everything in the Windows API uses.
A better interface would use types that are common to all languages, and it would dictate the use of memory management that's accessible universally. There are several common ways to do that. For example:
The strings are returned as simple nul-terminated arrays of characters — PAnsiChar in Delphi; char* in C++. The DLL allocates buffers for the strings, and also allocates a buffer for the array of those strings. When the host application is finished using the array and the strings, it calls another function exported by the DLL wherein the DLL frees the memory it allocated. This is the model used by, for example, FormatMessage; when the host program is finished the with message string, it calls LocalFree.
type
PStringArray = ^TStringArray;
TStringArray = array[0..Pred(MaxInt) div SizeOf(PAnsiChar)] of PAnsiChar;
function GetToken(Char: PAnsiChar): PStringArray; stdcall;
procedure FreeStringArray(StringArray: PStringArray); stdcall;
char** __stdcall GetToken(char const* Chain);
void __stdcall FreeStringArray(char** StringArray);
Use COM to return a safearray of BStr objects. It's similar to the previous technique, but the memory management is defined by COM instead of by your DLL, so there's less stuff that needs to be defined by either party of the interface.
Pass a callback function to the DLL, so instead of returning an array of strings, the DLL just calls the function once for each string it identifies. Then you don't have to define what any array looks like, and the lifetime of each string can be just the lifetime of the callback call — if the host application wants a copy, it can do so. The new function signature would look something like this:
type
TTokenCallback = procedure(Token: PAnsiChar); stdcall;
procedure GetToken(Chain: PAnsiChar; ProcessToken: TTokenCallback); stdcall;
typedef void (__stdcall* TokenCallback)(char const* Token);
void __stdcall GetToken(char const* Chain, TokenCallback ProcessToken);
If you're not the one who designed the DLL interface, then you need to lean on the folks who did and get it changed to be more accessible to non-Delphi code. If you can't do that, then the final alternative is to write a DLL in Delphi that wraps your DLL to massage the parameters into something each side understands.
Dearest stack exchange,
I'm programming an MRI scanner. I won't go into too much background, but I'm fairly constrained in how much code I've got access to, and the way things have been set up is...suboptimal. I have a situation as follows:
There is a big library, written in C++. It ultimately does "transcoding" (in the worst possible way), writing out FPGA assembly that DoesThings. It provides a set of functions to "userland" that are translated into (through a mix of preprocessor macros and black magic) long strings of 16 bit and 32 bit words. The way this is done is prone to buffer overflows, and generally to falling over.*
The FPGA assembly is then strung out over a glorified serial link to the relevant electronics, which executes it (doing the scan), and returning the data back again for processing.
Programmers are expected to use the functions provided by the library to do their thing, in C (not C++) functions that are linked against the standard library. Unfortunately, in my case, I need to extend the library.
There's a fairly complicated chain of preprocessor substitution and tokenization, calling, and (in general) stuff happening between you writing doSomething() in your code, and the relevant library function actually executing it. I think I've got it figured out to some extent, but it basically means that I've got no real idea about the scope of anything...
In short, my problem is:
In the middle of a method, in a deep dark corner of many thousands of lines of code in a big blob I have little control over, with god-knows-what variable scoping going on, I need to:
Extend this method to take a function pointer (to a userland function) as an argument, but
Let this userland function, written after the library has been compiled, have access to variables that are local to both the scope of the method where it appears, as well as variables in the (C) function where it is called.
This seems like an absolute mire of memory management, and I thought I'd ask here for the "best practice" in these situations, as it's likely that there are lots of subtle issues I might run into -- and that others might have lots of relevant wisdom to impart. Debugging the system is a nightmare, and I've not really got any support from the scanner's manufacturer on this.
A brief sketch of how I plan to proceed is as follows:
In the .cpp library:
/* In something::something() /*
/* declare a pointer to a function */
void (*fp)(int*, int, int, ...);
/* by default, the pointer points to a placeholder at compile time*/
fp = &doNothing(...);
...
/* At the appropriate time, point the pointer to the userland function, whose address is supplied as an argument to something(): /*
fp= userFuncPtr;
/* Declare memory for the user function to plonk data into */
i_arr_coefficients = (int) malloc(SOMETHING_SENSIBLE);
/* Create a pointer to that array for the userland function */
i_ptr_array=&i_arr_coefficients[0];
/* define a struct of pointers to local variables for the userland function to use*/
ptrStrct=createPtrStruct();
/* Call the user's function: */
fp(i_ptr_array,ptrStrct, ...);
CarryOnWithSomethingElse();
The point of the placeholder function is to keep things ticking over if the user function isn't linked in. I get that this could be replaced with a #DEFINE, but the compiler's cleverness or stupidity might result in odd (to my ignorant mind, at least) behaviour.
In the userland function, we'd have something like:
void doUsefulThings(i_ptr_array, ptrStrct, localVariableAddresses, ...) {
double a=*ptrStrct.a;
double b=*ptrStrct.b;
double c=*localVariableAddresses.c;
double d=doMaths(a, b, c);
/* I.e. do maths using all of these numbers we've got from the different sources */
storeData(i_ptr_array, d);
/* And put the results of that maths where the C++ method can see it */
}
...
something(&doUsefulThings(i_ptr_array, ptrStrct, localVariableAddresses, ...), ...);
...
If this is as clear as mud please tell me! Thank you very much for your help. And, by the way, I sincerely wish someone would make an open hardware/source MRI system.
*As an aside, this is the primary justification the manufacturer uses to discourage us from modifying the big library in the first place!
You have full access to the C code. You have limited access to the C++ library code. The C code is defining the "doUsefullthings" function. From C code you are calling the "Something" function ( C++ class/function) with function pointer to "doUseFullThings" as the argument. Now the control goes to the C++ library. Here the various arguments are allocated memory and initialized. Then the the "doUseFullThings" is called with those arguments. Here the control transfers back to the C code. In short, the main program(C) calls the library(C++) and the library calls the C function.
One of the requirements is that the "userland function should have access to local variable from the C code where it is called". When you call "something" you are only giving the address of "doUseFullThings". There is no parameter/argument of "something" that captures the address of the local variables. So "doUseFullThings" does not have access to those variables.
malloc statement returns pointer. This has not been handled properly.( probably you were trying to give us overview ). You must be taking care to free this somewhere.
Since this is a mixture of C and C++ code, it is difficult to use RAII (taking care of allocated memory), Perfect forwarding ( avoid copying variables), Lambda functions ( to access local varibales) etc. Under the circumstances, your approach seems to be the way to go.
i am working on making my applications international. After two days digging on msdn i came up with a test, which loads language-specific library containing resources. This is also my first attempt at loading library as a resource, loading strings from it and so on.
Next, according to msdn example at http://msdn.microsoft.com/en-us/library/windows/desktop/dd319071%28v=VS.85%29.aspx, i'm trying the LoadString.
Since string loading for entire application equals a lot of text copying, i thought i would use the - what i think is - memory efficient feature of LoadString, which is setting nBufferMax parameter to zero. According LoadString documentation, it should return a pointer to string resource. I thought i'd make a struct or a class of string pointers and do something along these lines (i extracted only the important bits):
wchar_t textBuf[SOMEVALUE]; // <-- this is how id DOES work
wchar_t *myString; // <-- this is how i would like it
HMODULE resContainer=LoadLibraryEx(L"MUILibENU.dll",NULL, LOAD_LIBRARY_AS_DATAFILE);
if(0!=resContainer){
// this works OK
int copied=LoadStringW(resContainer,IDS_APP_TITLE,textBuf,SOMEVALUE);
// this fails, also gives a warning at compile time about uninitialized variable used.
int copied=LoadStringW(resContainer,IDS_APP_TITLE,myString,0);
}
As you can see i am trying to get myString to become a pointer to loaded resource library's string without actually copying anything.
My question is: am i misunderstanding msdn documentation? Can i or can i not get a pointer to the string directly within loaded library, and simply use it later, e.g. to show a messagebox, without actually copying anything? Until i unload said library?
MSDN says:
[...] If this parameter is 0, then lpBuffer receives a read-only pointer to the resource itself.
It means that a) the pointer must be of type const wchar_t*:
const wchar_t *myString;
and b) you must pass a pointer to the pointer and use an ugly cast:
int copied=LoadStringW(resContainer,IDS_APP_TITLE,(LPWSTR)&myString,0);
I would like to call my own C++ dll function from excel vba:
void my_cpp_fun ( int& n_size, double*& my_array);
The C++ function creates an array my_array of variable size n_size (this size is computed within my_cpp_fun).
Can I interface this function as is to VBA without using any Excel specific stuff in my C++ code?
So basically what I am looking for is a VBA Declare statement like
Declare Sub my_cpp_fun Lib "my_cpp.dll" (n_size As Long, Ref_to_Ptr_Qualifier my_array As Double)
An additional problem that just occured to me: If I allocate memory inside the c++ dll using new, will that memory be available once the dll function returns control to VB? If that is not the case, the above is pointless...
Short answer: yes, it is possible (and easier than the COM route, in my opinion) to call functions in a DLL from VBA. In my experience, the best way to go is to write wrapper functions with C linkage (to avoid running into various C++ name-mangling schemes) and exposing an interface of pointers rather than references (as the appropriate VBA type to declare a reference argument or result will be rather difficult to predict).
A great guide for how to write appropriate Declare statements (assuming 32-bit Windows) is Chapter 2 of the book "Hardcore Visual Basic", if you can find it.
Note also that any functions exposed to VBA via Declare statements will need to use stdcall (aka WINAPI) calling convention.
TLDR:
I'd do this:
extern 'C' {
void WINAPI my_cpp_fun_wrapper ( int *n_size, double **my_array )
{
my_cpp_fun(*n_size, *my_array);
}
}
and then
Declare Sub my_cpp_fun_wrapper Lib "my_cpp.dll" (ptr_n_size As Long, ptr_ptr_my_array As Long)
and use the various *Ptr functions of VB6/VBA to get the pointers to my data.
You'd need to create a COM object that exposes your function and load it in your VBA using CreateObject