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
Related
My problem is that I'm using the MATLAB API from C# and this is the function that is giving me trouble.
C code:
EXTERN_C char ** matGetDir(MATFile * pMF, int *num);
I expected this to work, but unfortunately it doesn't (C# code):
[DllImport("libmat.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern string[] matGetDir(IntPtr matFile, ref int num);
If I put IntPtr instead of string[] I can call the function, but then I don't know how to convert the code from IntPtr to string[]
Edit1:
I also tried using these attributes, but it fails with error: Cannot marshal 'return value': Invalid managed/unmanaged type combination.
[return: MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)]
private static extern string[] matGetDir(IntPtr matFile, ref int num);
You cannot tell the p/invoke marshaller how to marshal this for you. You need to do it manually. One thing that is interesting about your question is that you don't give any details of what this char** really is. It's really important for you to learn that a type does not fully define the semantics of a parameter, or in this case a return value.
We can guess easily enough, but it is better to look this up in the documentation: http://uk.mathworks.com/help/matlab/apiref/matgetdir.html
Arguments
mfp
Pointer to MAT-file information
num
Pointer to the variable containing the number of mxArrays in the
MAT-file
Returns
Pointer to an internal array containing pointers to the names of the
mxArrays in the MAT-file pointed to by mfp. In C, each name is a
NULL-terminated string. The num output argument is the length of the
internal array (number of mxArrays in the MAT-file). If num is zero,
mfp contains no arrays.
matGetDir returns NULL in C (0 in Fortran). If matGetDir fails, sets
num to a negative number.
So, we declare the p/invoke like this:
[DllImport("libmat.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr matGetDir(IntPtr matFile, out int num);
Call it like this:
int num;
IntPtr matFile = ...;
IntPtr namesPtr = matGetDir(mayFile, out num);
if (names == IntPtr.Zero)
// handle error
string[] names = new string[num];
for (int i = 0; i < num; i++)
{
int offset = i * Marshal.SizeOf(typeof(IntPtr));
names[i] = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(namesPtr, offset));
}
You can do it this way:
[DllImport("libmx.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void mxFree(IntPtr ptr);
[DllImport("libmat.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern IntPtr matGetDir(IntPtr matFile, ref int num);
public static string[] matGetDir(IntPtr matFile)
{
// Obtain as IntPtr
var count = 0;
var pointers = matGetDir(matFile, ref count);
// Handling errors as noticed by David Heffernan
if (count < 0) { throw new Exception("Failed to obtain list of variables in selected mat file."); }
if (pointers == IntPtr.Zero) { return new string[0]; }
// Cast into IntPtr[]
var ptrs = new IntPtr[count];
Marshal.Copy(pointers, ptrs, 0, count);
// Convert each value in IntPtr[] into string
// NB: using System.Linq;
var strs = ptrs.Select(x => Marshal.PtrToStringAnsi(x)).ToArray();
// Don't forget to free memory allocated by Matlab
// NB: Deleting global pointer only ==> see "edit([matlabroot '/extern/examples/eng_mat/matdgns.c']);" example
mxFree(pointers);
// And voilĂ
return strs;
}
So just making conversion into IntPtr in a private method as you initally tried and then adding a public method for conversion to string[]. See comments in the code for more details.
I have a problem with calling a C DLL fom C#
The C function is (I don't have a c header or a good spec for this :( )
int knr12_read ( char *kn12, char *ik9, char *wok, char *wlc,
char *plz, char *ort, char *woz );
kn12 is a ref parameter
This is what I've tried in C#
[return: MarshalAs(UnmanagedType.U4)]
[DllImport("Knr12.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "knr12_read", CharSet = CharSet.Ansi)]
unsafe public static extern int knr12_read(out IntPtr buffer, string ik9, string wok, string wlc, string plz, string ort, string woz);
int knr = knr12_read(out pBuffer, knrTemp, "11111", "", "98529", "Suhl", "1");
string data = Marshal.PtrToStringAnsi(pBuffer);
The returning int is always right, how it should be, but I have problems with the ref parameter pBuffer...
Also the sting type for the other variables is working...
When I use a ref,I always get an AccessViolation error knr12_read().In case I use out I get a pointer,but the String is always empty which can't be.I even tried out String as ref for char* but I get an AccessViolation error on knr12_read().
Please guide.
StringBuilder is often a good type to use when P/Invoking to functions with string returning parameters:
static extern int knr12_read(StringBuilder kn12, ...)
You'll need to initialise the string builder before you call the function, something like:
StringBuilder outString = new StringBuilder(100);
You shouldn't need the 'unsafe', and unless the 'C' code holds onto the pointers for longer than the duration of the call, you shouldn't need to worry about pinning - the framework is doing that for you.
Here's a SO question which should help: Marshal "char *" in C#
Probably you have not pinned the buffer. here is the example of how to pin the buffer data.
GCHandle pinnedRawData = GCHandle.Alloc(rawData,
GCHandleType.Pinned);
Pinning the object makes sure that the pointer is valid cause .Net runtime can always reallocate the memory as and when it thinks fit.
Try it out and let me know if it helps you.
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.
I need to call a function from a C api contained in a dll. Function prototype looks as follows....
int func( char* name, void* value );
where the contents of the pointer value can refer to any type dependent on the passed name. I an unsure how to set up the Dll inport to correctly marshall this void *. Ihave been experimenting with IntPtr which seems to work whe the value is an int but I cannot retrieve values correctly for floats etc.
I am trying to import the function like this...
[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func( string name, ref IntPtr value );
pls note that value is an output. A pointer to a value of any type, i.e. the address in a global region of memory of a value of a known type (known to the caller). In a c prog the caller would then be expected to cast this void * to the desired type and dereference to get the actual value stored there. The answers given so far seem to be based around an assumption that the function will write the result to pointer location passed in. My fault as I haven't been too specific. Sorry. C# is not my bag, and I don't even know if IntPtr is the way to go here...
The best way to tackle this is to provide overloads of the function so everything is squeaky clean on the C# side. Like this:
[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func(string name, out int value);
[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func(string name, out float value);
// etc, one each for each type
[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func( string name, IntPtr value );
...
// n - number of bytes which is enough to keep any type used by function
IntPtr ptr = Marshal.AllocHGlobal(n);
func(name, ptr);
// Use Marshal.ReadByte, Marshal.ReadInt32 ... or Marshal.Copy
// to copy from ptr filled by func to managed variable. For example:
byte b = Marshal.ReadByte(ptr);
Marshal.FreeHGlobal(IntPtr);
You do not need the ref - IntPtr is the way to pass void* to native code.
[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func( string name, IntPtr value );
EDIT:
The C code can use the input to write to the required memory. The problem you face is for the managed code to know how much memory to allocate for each possible return value type. Then an appropriate sized block can be allocated using Marshal.AllocHGlobal or Marshal.AllocCoTaskMem, freed (according to which allocation method you use) use via FreeHGlobal or FreeCoTaskMem, once managed code is done with the output value.
See answer from #Alex Farber for an example.
Sometimes, it might be easier to use this approach.
Declaration:
[DllImport("dllname.dll", CharSet = CharSet.Ansi)]
public static extern int func([MarshalAs(UnmanagedType.LPTStr)] string name, [MarshalAs(UnmanagedType.AsAny)] object value);
Example usage to pass a value:
func("some string", (int)12);
func("some string", (double)12);
Example usage to pass a string:
func("some string", "test");
Example usage to pass a variable:
int value = 12;
func("some string", value);
I'm trying to use kernel32.dll's lstrcpy to get a string from a pointer in C#, but it isn't working. lstrlenA IS working, it gives me the length of the string, so I'm hitting the kernel32.dll at least. lstrcpy is working in the VB6 app I'm converting, so I know it CAN work, but I don't have a clue why it isn't here.
The string s never gets filled with the actual string, it just returns the initial padded string.
[DllImport("kernel32.dll", EntryPoint = "lstrlenA", CharSet = CharSet.Ansi)]
private static extern int lstrlen( int StringPointer );
[DllImport( "kernel32.dll",EntryPoint = "lstrcpyA", CharSet = CharSet.Ansi )]
private static extern int lstrcpy(string lpString1, int StringPointer );
private static string StringFromPointer(int pointer)
{
//.....Get the length of the LPSTR
int strLen = lstrlen(pointer);
//.....Allocate the NewString to the right size
string s = "";
for (int i = 0; i < strLen; i++)
s += " ";
//.....Copy the LPSTR to the VB string
lstrcpy(s, pointer);
return s;
}
I suspect that it might be something to do with managed strings being immutable, so that whenever you think you're changing it, you're actually creating a new string and change the reference to look at the new string instead.
I'm not sure how that works when you use windows API functions, but it's possible that during the call to lstrcpy a new string is created containing the text that the pointer points to, but because lstrcpy might not be aware of System.String, it doesn't handle it properly and so it doesn't change s to reference the new string.
I think that what you want to use is a Text.StringBuilder since that's not immutable.