I have a function writen in C++ with the next header:
void EncodeFromBufferIN(void* bufferIN,int bufferINSize, unsigned char* &bufferOUT, int &bufferOUTSize);
I've edited the .h and .cpp files like this, to be able calling the function by importing the DLL in C#:
**EncodeFromBufferIN.h**
extern "C" {
__declspec(dllexport) void EncodeFromBufferIN(void* bufferIN, int bufferINSize, unsigned char* &bufferOUT, int &bufferOUTSize);
}
**EncodeFromBufferIN.cpp**
extern void EncodeFromBufferIN(void* bufferIN, int bufferINSize, unsigned char* &bufferOUT, int &bufferOUTSize){
// stuff to be done
}
But now my problem is that I don't know how to call the function in C#. I've added the next code in C# but not sure how to pass the parameters to the function.
[DllImport("QASEncoder.dll")]
unsafe public static extern void EncodeFromBufferIN(void* bufferIN, int bufferINSize, out char[] bufferOUT, out int bufferOUTSize);
The bufferIN and bufferOUT should be strings but if I'm calling the function like this:
public string prepareJointsForQAS()
{
string bufferIN = "0 0 0 0 0";
char[] bufferOUT;
int bufferOUTSize;
EncodeFromBufferIN(bufferIN, bufferIN.Length, bufferOUT, bufferOUTSize);
}
I get this error: "The best overloaded method matrch for ... has some invalid arguments". So how should the parameters be passed?
Marshalling works best with C Style calls. So it is best to use
pure C on your public interface.
If it is at all feasible to change the native code to
void EncodeFromBufferIN(
unsigned char* bufferIN,
int bufferINSize,
unsigned char* bufferOUT,
int* bufferOUTSize);
Then the call in C# can be thus
[DllImport("QASEncoder.dll")]
public static extern void EncodeFromBufferIN(
String bufferIN,
int bufferINSize,
StringBuilder bufferOUT,
ref int bufferOUTSize);
String inStr = new String(255);
int inSize = 255;
// make an educated estimate for the output size
// and preallocate in C# (I am guessing 255)
StringBuilder outStr = new StringBuilder(255);
int outSize = 255;
EncodeFromBufferIN(inStr, inSize, outStr, outSize);
This way you can avoid memory allocations in unmanaged code
which (although feasible) can get messy.
Hope this gets you going.
A few corrections that worked for me:
string inStr = "Value to pass";
int inSize = inStr.Length;
StringBuilder outStr = new StringBuilder(255);
int outSize = 255;
EncodeFromBufferIN(inStr, inSize, outStr, ref outSize);
regards.
Related
I am trying to create a simple dll in C++ and call it from C# using PInvoke. I want to pass in a byte array, do some manipulation on it, and send back another byte array. I figured I would pass in the frame and size then create an unmanaged unsigned char*. I would pass that back in an out IntPtr and return the size. Then later I would free that memory with another function. Everything is working ok except I cannot get the out IntPtr to work. I always just get 0 back. I created my C++ dll in qt. Here is the code I have so far.
#pragma once
#ifdef TOOLS_EXPORTS
#define TOOLS_API __declspec(dllexport)
#else
#define TOOLS_API __declspec(dllimport)
#endif
class Tools
{
public:
int TOOLS_API TestFunction(const unsigned char *inData, int inSize, unsigned char *outData);
};
#include "Tools.h"
int Tools::TestFunction(const unsigned char *inData, int inSize, unsigned char* outData)
{
outData = (unsigned char*)malloc(sizeof(unsigned char) * inSize);
memcpy(outData, inData, sizeof(unsigned char) * inSize);
return inSize;
}
[DllImport("Tools.dll", EntryPoint = "?TestFunction#Tools##QAEHPBEHPAE#Z", CallingConvention = CallingConvention.StdCall)]
public static extern int TestFunction(byte[] inData, int inSize, out IntPtr outData);
IntPtr outData;
int test = TestFunction(data, data.Length, out outData);
You have the final parameter as:
unsigned char* outData
This is a pointer, passed by value. Any modifications to the address are not seen by the caller, because the address is passed by value.
You need to return the address of the memory you allocate to the caller. So you need:
unsigned char** outData
Then in the implementation of the function in your C++ code you replace references to outData with *outData.
By definition sizeof(unsigned char) == 1 and it would be idiomatic to replace sizeof(unsigned char) * inSize with inSize.
Having the following code in C++:
nConId is Connection Identifier
pParName the parameter name
pSubName the subParameter Name (if any)
pValue_out a pointer to a char array of lenght FCL_PAR_VALUE_LENGH
nValueSize the real size of pValue_out vector (at least FCL_PAR_VALUE_LENGH)
extern "C" MY_API int ReadParameter(const ConnectionId_T nConId, const char* pParName,
const char *pSubName, char *pValue_out, const int nValueSize );
My try is:
[DllImport("mydll.dll", CharSet = CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]
public static extern int ReadParameter(ConnectionId_T pConId, IntPtr pParName,
ref IntPtr pSubName, ref IntPtr[] pValue_out, int nValueSize);
I'm using the following code to call that function:
# nConId is returned from another function and the his value is 0
public const int FCL_PAR_VALUE_LENGH = 128;
string param_string = "AUXF";
IntPtr pParName = (IntPtr)Marshal.StringToHGlobalAnsi(param_string);
string subparam_string = "T";
IntPtr pSubName = (IntPtr)Marshal.StringToHGlobalAnsi(subparam_string);
IntPtr[] aParValue = new IntPtr[FCL_PAR_VALUE_LENGH];
int returnedValue = ReadParameter(nConId, pParName, ref pSubName,
ref aParValue, FCL_PAR_VALUE_LENGH);
When I run the code I get an AccessViolationException, so I guess there's something wrong in my call.
Do I've my marshall wrong? What do I've to change in the code in order to get the good reponse?
PS: I also know that the call returns also something to aParValue.
You're working too hard with those char*s. It's perfectly legal (and encouraged) to marshal a System.String for input, and a StringBuilder for output.
[DllImport("mydll.dll", CharSet = CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)]
public static extern int ReadParameter(
ConnectionId_T pConId,
string pParName,
string pSubName,
StringBuilder pValue_out,
int nValueSize);
usage
const int sbLength = 256; //use a domain relevant value
StringBuilder sb = new StringBuilder(sbLength + 1); //for null character, hard to say if you need it without seeing the C++ code, but easier to just add it than find out.
int result = ReadParameter(conId, "paramname", "paramsubname", sb, sbLength);
You haven't given any indication about the underlying type of ConnectionId_T so I'm assuming you've ruled that out as an issue.
MSDN Reference
I have a C-DLL + header file and try to p/invoke a function from C#. I also have some example C++ code of how to use the function. Here is the function definition:
int GetData(unsigned char* buffer, long bufferSize);
The more interesting part is the example code and how the function can be called:
if(dataSize == 16)
{
unsigned short* usData = new unsigned short[m_numX * m_numY * 3 / 2];
GetData( (unsigned char*)usData, m_numX * m_numY * sizeof(unsigned short) );
}
else if (dataSize == 32)
{
unsigned long* ulData = new unsigned long[m_numX * m_numY];
GetData( (unsigned char*)ulData, m_numX * m_numY * sizeof(unsigned long) );
}
So, depending on the dataSize variable, the actual data array can be an ushort or an ulong. However, it is always passed as an unsigned char pointer.
For the sake of simpleness I just tried to get at least one of the variants to work. Here's the code I tried for dataSize = 16
[DllImport("External.dll", EntryPoint = "GetData", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern int GetData(ref ushort[] pBuffer, long lBufferSize);
long bufferSize1 = numX * numY * 3 / 2;
long bufferSize2 = numX * numY * sizeof(ushort);
ushort[] data = new ushort[bufferSize1];
GetData(ref data, bufferSize2)
If I run above code, the application quits with an 'Access Violation' Exception. That usually means, that the unsafe code tried to write over the buffer limits or that the p/invoke declaration has an error. I tried huge buffers (which would be able to hold any kind of data I'm expecting) but my guess would be, that my mistake is in the declaration.
I also tried to declare the buffer as byte[] (since the example casts it as unsigned char*) and ulong in the p/invoke declaration. same for the actual buffer I pass as reference. the error remains the same.
How can I make this work?
A couple of mistakes:
The array must not be passed as ref. That is because ref ushort[] matches unsigned short**.
C++ long does not match C# long on Windows. The former is 32 bits, the latter 64 bits.
You need to import like this:
[DllImport("External.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetData(
[Out] ushort[] pBuffer,
int lBufferSize
);
It would be perfectly reasonable, for convenience, to use an overload for the 32 bit data variant:
[DllImport("External.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetData(
[Out] uint[] pBuffer,
int lBufferSize
);
Likewise, and overload for an array of byte would also be valid should you need it.
I'm trying to call this function from unmanaged dll:
gvRenderData(GVC_t *gvc, Agraph_t* g, char *format, char **result, unsigned int *length)
I use DllImport:
[DllImport(FolderName, CallingConvention = CallingConvention.Cdecl)]
static extern int gvRenderData(IntPtr gvc, IntPtr g, string format, out IntPtr result, out int length);
and I got exception
Attempted to read or write protected memory.
I think my type for char result or for unsigned int length is invalid.
With great help of the stackoverflow community, I've managed to call a native DLL function. However, I can't modify the values of ID or intersects array. No matter what I do with it on the DLL side, the old value remains. It seems read-only.
Here are some code fragments:
C++ struct:
typedef struct _Face {
int ID;
int intersects[625];
} Face;
C# mapping:
[StructLayout(LayoutKind.Sequential)]
public struct Face {
public int ID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 625)]
public int[] intersects;
}
C++ method (type set to DLL in VS2010):
extern "C" int __declspec(dllexport) __stdcall
solve(Face *faces, int n){
for(int i =0; i<n; i++){
for(int r=0; r<625; r++){
faces[i].intersects[r] = 333;
faces[i].ID = 666;
}
}
C# method signature:
[DllImport("lib.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int solve(Face[] faces, int len);
C# method invocation:
Face[] faces = new Face[10];
faces[0].intersects = new int[625];
faces[0].ID = -1; //.. and add 9 more ..
solve(faces, faces.Length);
// faces[0].ID still equals -1 and not 666
Kindest regards,
e.
You have to tell the pinvoke marshaller explicitly that the array needs to be marshaled back. You do this with the [In] and [Out] attributes. Like this:
[DllImport("...")]
public static extern int solve([In, Out] Face[] faces, int len);
This is an output field only? To get to the bottom of this, I'd try substituting your Face[] parameter with a large-enough a byte[] and see if the byte array gets filled with anything (you'll have to change your [DllExport] a bit).
Also, one other thing I used to experience when doing this with char*'s is that I had to pre-allocate the buffer in C#. For example:
StringBuilder theString=new StringBuilder();
MyUnmanagedFunction(theString);
would not work. But assuming that returned theString was a max 256 characters, I would do this:
StringBuilder theString=new StringBuilder(256);
MyUnmanagedFunction(theString);
And I'd be in business. I'd recommend trying the byte[] substitution, if that doesn't work, try the pre-allocated byte array. Once you are seeing the byte array actually get changed by your C++ code, then you can figure out how to marshal that thing into your C# struct.