I want to use a function in a C++ DLL in my C# application using DLLImport:
BOOL IsEmpty( DWORD KeyID, BOOL *pFlag )
I tried many combinations but in vain, like:
public extern static bool IsEmpty(int KeyID, ref bool pFlag);
The method returns false (that means an error).
Any idea how to do that?
To quote "Willy" (with amendments):
Beware the booleans!
Win32 defines different versions of booleans.
1) BOOL used by most Win32 API's, is an unsigned int a signed int (4 bytes)
2) BOOLEAN is a single byte, only used by a few win32 API's!!
3) and C/C++ has it's builtin 'bool' which is a single byte
...and to add what #tenfour pointed out:
4) the even more bizarre VARIANT_BOOL
typedef short VARIANT_BOOL;
#define VARIANT_TRUE ((VARIANT_BOOL)-1)
#define VARIANT_FALSE ((VARIANT_BOOL)0)
The signed or unsigned nature shouldn't matter for BOOL, as the only "false" pattern is 0. So try treating it as a 4 byte quantity...however you interface with a DWORD may be satisfactory, (I've not dealt with Windows 64-bit conventions.)
BOOL in Win32 is a typedef of int, so you should just change bool to Int32, so the definition is int IsEmpty(uint KeyID, ref int pFlag)
because in c++ BOOL is defined as int. You should use
[return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)]
public static extern bool IsEmpty(uint KeyID, ref int pFlag) ;
Thank you for your help!
finally this works for me
public extern static int IsEmpty(
int KeyID,
int[] pFlag)
Related
I am given a Delphi DLL that contains functions that I need to call in C#. One of the functions takes two char arrays, where one is an encrypted password and the other is the key.
TCString = array[0..254] of Char;
...
function Decrypt(const S, Key: TCString): TCString; stdcall;
I tried to figure out how to call this function on my own but I keep getting "Cannot marshal 'return value': Invalid managed/unmanaged type combination." I am using byte since the Char type in Delphi is AnsiChar which is 8 bits.
[DllImport("path", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern byte[] Decrypt(byte[] S, byte[] Key);
What is the correct way to call this in C#?
I think I would be inclined to wrap the fixed length array in a C# struct.
public struct CString
{
[UnmanagedType.ByValArray(SizeConst=255)]
byte[] value;
}
This allows the size to be specified in one place only.
The next hurdle is the return value. The Delphi ABI treats a return value that cannot fit into a register as an additional hidden var parameter. I'll translate that as a C# out parameter.
Finally the two input parameters are declared as const. That means that they are passed by reference.
So the function would be:
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
public static extern void Decrypt(
[In] ref CString S,
[In] ref CString Key,
out CString Result
);
I've intentionally avoided any use of text in this because this would appear to be a function that operates on binary data. Many Delphi programmers treat AnsiChar arrays interchangeably with byte arrays in such situations which is often confusing.
I've been struggling on how to migrate this VB6 code into C#. It involves calling a function inside a DLL passing an array of structure, among other things.
So in VB6, the "struct" declaration is like this:
'Define structure for RGETDAT_STR procedure call
Type rgetdat_str_data_str
type As Integer 'data type (set internally)
file As Integer 'file in database
rec As Integer 'record in file
word As Integer 'word offset in record
start_bit As Integer 'UNUSED
length As Integer 'length of string
flags As Integer 'flags
padding1 As Integer 'UNUSED
value As String 'database value
status As Integer 'return status
padding2 As Integer 'UNUSED
End Type
and one function that uses this "struct" has a method declared as this:
Public Declare Function rgetdat_str Lib "hscnetapi.dll" _
Alias "rgetdat_str_vb" _
(ByVal Server As String, ByVal num_points As Integer, _
getdat_str_data() As rgetdat_str_data_str) As Integer
So, I attempted to convert these 2 pieces of code into C#. I had tried so many variations, but I will post here the latest one that I have. The idea is to call the function via P/Invoke.
The C# struct (so far):
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct rgetdat_str_data_str
{
public short type;
public short file;
public short rec;
public short word;
public short start_bit;
public short length;
public short flags;
public short padding1;
[MarshalAs(UnmanagedType.LPStr)]
public string value;
public short status;
public short padding2;
}
and the function import (so far):
[DllImport("hscnetapi.dll", EntryPoint = "rgetdat_str_vb")]
public static extern short rgetdat_str(
[MarshalAs(UnmanagedType.LPTStr)]
string Server,
short num_points,
[In,Out, MarshalAs(UnmanagedType.LPArray)]
ref rgetdat_str_data_str[] getdat_str_data);
Nothing worked so far in my various experiments on marshalling attributes with the parameters.
I managed to find the C header file for this DLL, and the declaration looks like this:
EXTERN_C short __loadds CALLBACK rgetdat_str_vb_ansi
_DECLARE((char *szHostname, short cRequests, SAFEARRAY **ppsa));
and the "struct" in the C world is declared like this:
/* define union used in rgetdat_value in RGETDAT procedure call */
typedef union rgetdat_value_str
{
n_short int2;
n_long int4;
n_float real4;
n_double real8;
n_char *str;
n_ushort bits;
} rgetdat_value;
/* define structure for RGETDAT procedure call */
typedef struct rgetdat_data_str
{
n_ushort type;
n_ushort file;
n_ushort rec;
n_ushort word;
n_ushort start_bit;
n_ushort length;
n_short flags;
rgetdat_value value;
n_short status;
} rgetdat_data;
In my frustration, I tried to open this DLL with the ITypeLib Viewer tool. I was surprised that the DLL file can be opened, even though I cannot add this DLL as a Reference in my project. Anyway, a couple of things that I observed within the viewer.
The function has this signature:
[entry("rgetdat_str_vb"), helpstring("...")]
short _stdcall rGetdat_Str(
[in] LPSTR Server,
[in] short num_points,
[in, out] SAFEARRAY(rGetdat_Str_Data_Str)* getdat_str_data);
and the "struct" looked like this:
typedef struct tagrGetdat_Str_Data_Str {
short type;
short file;
short rec;
short word;
short start_bit;
short length;
short flags;
short padding1;
BSTR value;
short status;
short padding2;
} rGetdat_Str_Data_Str;
Based on these observations, I played around with the marshalling attributes of the C# struct, for example,
1.) Changing the struct's value attribute to [MarshalAs(UnmanagedType.BStr)]
2.) Changing the function's getdat_str_data parameter attribute to MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_RECORD)
and still nothing works.
There's an blog/article talking about a similar topic here: http://limbioliong.wordpress.com/2012/02/28/marshaling-a-safearray-of-managed-structures-by-pinvoke-part-1/ but I can't just wrap my head around it.
It seems that VB6 can do it very simply compared to C# (.Net) with this DLL function call. Any hints or ideas out there on how to DLLImport declare this function in C# (.Net)?
You need to use MarshalAs with UnmanagedType.SafeArray to tell the marshaller that you want the array marshalled as a SAFEARRAY.
[DllImport("hscnetapi.dll", EntryPoint = "rgetdat_str_vb")]
public static extern short rgetdat_str(
[MarshalAs(UnmanagedType.LPStr)]
string Server,
short num_points,
[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType=VT_USERDEFINED)]
ref rgetdat_str_data_str[] getdat_str_data
);
In your C# struct, you handle the BSTR member incorrectly. It should be
[MarshalAs(UnmanagedType.BStr)]
public string value;
With C/C++ DLL SDK fun,like this:
INT CmdGetAllLog( BYTE *bStream, UINT16 *nCount, const UINT8 nblk )
but in project use c#,I do it with:
[DllImport("C:\\PrBioApi.dll", EntryPoint = "CmdGetAllLog")]
private static extern bool CmdGetAllLog(IntPtr bStream, ref UInt16 nCount, byte nblk);
and I use it with:
int nMallocSize = Marshal.SizeOf(new LOG_RECORD()) * stuSystem.wLogCnt + 4096;
byte[] pRecord = new byte[nMallocSize];
IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(nMallocSize));
Marshal.Copy(pRecord, 0, p, pRecord.Length);
bGetSucc = CmdGetAllLog(p, ref nGet, nBlk++);
Marshal.FreeHGlobal(p);
but it did not work.
would anyone can help me ?thanks.
Your code which copies between the managed array, and the unmanaged pointer, is in the wrong place. It would need to be after the call to the unmanaged function.
But you may as well let the p/invoke marshaller do the work for you:
[DllImport(#"C:\PrBioApi.dll")]
private static extern bool CmdGetAllLog(
byte[] bStream,
ref ushort nCount,
byte nblk
);
int nMallocSize = ...;
byte[] pRecord = new byte[nMallocSize];
bool bGetSucc = CmdGetAllLog(pRecord, ref nGet, nBlk++);
Because a byte array is blittable then the marshaller will just pin your array during the call and hand it off to the native code.
I'm assuming that the other two parameters are passed correctly. Since you did not specify any more details of the interface, they could well be wrong. I'd guess that nGet is used to tell the function how big the buffer is, and to return how much was copied to it by the function. I cannot see where you specify nGet in the question. I'm trusting that you got that bit right.
Some other comments:
You may need to specify a calling convention in the DllImport attribute. Is the native code cdecl perhaps?
The return value is INT in the native code but you've mapped it to bool. That probably is fine if the protocol is that non-zero return means success. But if the return value indicates more than that then you'd clearly need to use int. Personally I'd be inclined to use int and stay true to the native.
I have some problems working with boolean types and marshalling this in a struct back and forth between C# and C. I am very rusty in C but hopefully there's nothing crucially wrong in that part.
As far as I've read/seen, .NET Boolean and C# bool type is 4 bytes long whilst the C type bool is only 1 byte. For memory footprint reasons, I do not which to use the defined BOOL 4 bytes version in the C code.
Here is some simple test code that hopefully will make my questions clear:
C code:
typedef struct
{
double SomeDouble1;
double SomeDouble2;
int SomeInteger;
bool SomeBool1;
bool SomeBool2;
} TestStruct;
extern "C" __declspec(dllexport) TestStruct* __stdcall TestGetBackStruct(TestStruct* structs);
__declspec(dllexport) TestStruct* __stdcall TestGetBackStruct(TestStruct* structs)
{
return structs;
}
I call this code in C# using the following definitions:
[StructLayout(LayoutKind.Explicit)]
public struct TestStruct
{
[FieldOffset(0)]
public double SomeDouble1;
[FieldOffset(8)]
public double SomeDouble2;
[FieldOffset(16)]
public int SomeInteger;
[FieldOffset(17), MarshalAs(UnmanagedType.I1)]
public bool SomeBool1;
[FieldOffset(18), MarshalAs(UnmanagedType.I1)]
public bool SomeBool2;
};
[DllImport("Front.dll", CharSet = CharSet.Auto, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr TestGetBackStruct([MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] TestStruct[] structs);
and here is the actual test function in C#:
[Test]
public void Test_CheckStructParsing()
{
var theStruct = new TestStruct();
theStruct.SomeDouble1 = 1.1;
theStruct.SomeDouble2 = 1.2;
theStruct.SomeInteger = 1;
theStruct.SomeBool1 = true;
theStruct.SomeBool2 = false;
var structs = new TestStruct[] { theStruct };
IntPtr ptr = TestGetBackStruct(structs);
var resultStruct = (TestStruct)Marshal.PtrToStructure(ptr, typeof(TestStruct));
}
This works in the sense that I do get a struct back (using the debugger to inspect it), but with totally wrong values. I.e. the marshalling does not work at all. I've tried different version of the C# struct without success. So here are my questions (1 & 2 most important):
Is the C function correct for this purpose?
How is the struct to be written correctly in order to get me the correct values in the struct back to C#? (Is it even necessary to define the struct with the StructLayout(LayoutKind.Explicit) attribute using the FieldOffset values or can I use StructLayout(LayoutKind.Sequential) instead)?
Since I am returning a pointer to the TestStruct in C, I guess it should be possible to get back an array of TestStructs in C#. But this does not seem to be possible using the Marshal.PtrToStructure function. Would it be possible in some other way?
Apparantly it is possible to use something called unions in C by having multiple struct fields point to the same memory allocation using the same FieldOffset attribute value. I understand this, but I still don't get yet when such scenario would be useful. Please enlighten me.
Can someone recommend a good book on C# P/Invoke to C/C++? I am getting a bit tired of getting pieces of information here and there on the web.
Much obliged for help with these questions. I hope they were not too many.
Stop using LayoutKind.Explicit and get rid of the FieldOffset attributes and your code will work. Your offsets were not correctly aligning the fields.
public struct TestStruct
{
public double SomeDouble1;
public double SomeDouble2;
public int SomeInteger;
[MarshalAs(UnmanagedType.I1)]
public bool SomeBool1;
[MarshalAs(UnmanagedType.I1)]
public bool SomeBool2;
};
Declare the function in C# like this:
public static extern void TestGetBackStruct(TestStruct[] structs);
The default marshalling will match your C++ declaration (your code is C++ rather than C in fact) but you must make sure that your allocate the TestStruct[] parameter in the C# code before calling the function. Normally you would also pass the length of the array as a parameter so that the C++ code knows how many structs there are.
Please don't try to return the array of structures from the function. Use the structs parameter as an in/out parameter.
I know of no book with an emphasis on P/Invoke. It appears to be something of a black art!
My question has to do with trying to call a function written in C from C#. I've looked in a header file that came with the C library to understand the functions as they exist in the C dll. Here's what I see:
C code (for a function called "LocGetLocations"):
typedef enum {
eLocNoError,
eLocInvalidCriteria,
eLocNoMatch,
eLocNoMoreLocations,
eLocConnectionError,
eLocContextError,
eLocMemoryError
} tLocGetStatus;
typedef void *tLocFindCtx;
typedef void *tLocation;
PREFIX unsigned int POSTFIX LocGetLocations
(
tLocFindCtx pCtx,
tLocation *pLoc,
unsigned int pNumLocations,
tLocGetStatus *pStatus
);
In C#, I have this:
[DllImport(#"VertexNative\Location.dll")]
public static extern uint LocGetLocations(IntPtr findContext, out byte[] locations, uint numberLocations, out int status);
The problem is that I don't quite know how to handle the pLoc parameter in C#. I'm bringing it over as a byte array, although I'm not sure if that is correct. The C library's documentation says that that parameter is a pointer to an array of handles.
How can I get an array back on the C# side and access its data?
The example I was given in C, looks like this:
tLocation lLocation[20];
// other stuff
LocGetLocations(lCtx, lLocation, 20, &lStatus)
Any help would be much appreciated!
Generally, the only thing that matters is the size of the parameters. As I recall enums are integers in C, so you can simply use that. Or better, recreate the same enum in C#, I think it would work. One thing to remember is that when dealing with complex structs, one needs to use attributes to tell the framework about the desired alignment of members.
In the end, this signature works:
[DllImport(#"VertexNative\Location.dll")]
public static extern uint LocGetLocations(IntPtr findContext, [Out] IntPtr[] locations, uint numberLocations, out int status);
And I can call it like this (some refactoring needed):
IntPtr[] locations = new IntPtr[20];
int status;
// findContext is gotten from another method invocation
uint result = GeoCodesNative.LocGetLocations(findContext, locations, 20, out status);
Thanks for the help!