The C dll header is this:
HRESULT App_Process(char *FileName, char *Output, const bool& LogInformation);
My C# DllImport looks like this:
[DllImport("App.dll")]
public static extern Int32 App_Process(
[MarshalAs(UnmanagedType.LPStr)]string FileName,
[MarshalAs(UnmanagedType.LPStr)]string Output,
[MarshalAs(UnmanagedType.Bool)]bool LogInformation);
The exception is:
var result = App_Process("MyFile.txt", "Output.txt", true);
System.AccessViolationException: Attempted to read or write protected
memory. This is often an indication that other memory is corrupt.
Now the strange this is that the method is successfully doing everything its supposed to do.
Any ideas?
Original answer
The last parameter of the extern method should be a ref bool rather than bool, given that the DLL header has a parameter of type const bool&:
// Parameter names changed to be idiomatic for C#
[DllImport("App.dll")]
public static extern Int32 App_Process(
[MarshalAs(UnmanagedType.LPStr)] string fileName,
[MarshalAs(UnmanagedType.LPStr)] string output,
[MarshalAs(UnmanagedType.Bool)] ref bool logInformation);
With C# 7.2 I suspect you could use in instead of ref, which would make the method simpler to call:
// Parameter names changed to be idiomatic for C#
[DllImport("App.dll")]
public static extern Int32 App_Process(
[MarshalAs(UnmanagedType.LPStr)] string fileName,
[MarshalAs(UnmanagedType.LPStr)] string output,
[MarshalAs(UnmanagedType.Bool)] in bool logInformation);
Update
(From the comment from Hans Passant)
This is not C code, as bool& is only valid in C++. That makes it very likely that the argument needs to be marshalled as [MarshalAs(UnmanagedType.U1)]. Double-check with sizeof(bool) in the native code. But you should talk to the author of the DLL, as const bool& makes no sense at all. There's no point in passing a bool by reference but then not allowing the code to update it.
Related
I want to call in C# a function from unmanaged library with following signature:
DLL_EXPORT int xli_open(char *, int , struct t_info *);
In legacy code on Windows 7 the function is improrted as:
[DllImport(DRIVER_FILENAME, EntryPoint = "xli_open", CallingConvention = CallingConvention.Cdecl)]
public static extern int xli_open(string device, int hndl, ref t_info tInfo);
On Windows 10 I get an AccessViolationException for calling the function and I import the function as:
[DllImport(DRIVER_FILENAME, EntryPoint = "xli_open", CallingConvention = CallingConvention.Cdecl)]
public static extern int xli_open(ref string device, int hndl, ref t_info tInfo);
I don't get AccessViolationException anymore, but it seems that the function gets an empty string. Is the declaration right? And why does the pass of the ref parameter work (would string not be passed by reference anyway?)?
Assuming that you are passing the text to the function, then plain by value string is correct. The access violation is likely because of some other error. Perhaps the structure definition does not match, perhaps the calling convention is wrong. Or perhaps some other mistake, but the string argument appears to be correct.
P/Invoke declarations:
[DllImport("kernel32.dll")]
static extern bool UpdateResource(IntPtr hUpdate, IntPtr lpType, IntPtr lpName, ushort wLanguage, byte[] lpData, uint cbData);
[DllImport("kernel32.dll")]
static extern bool UpdateResource(IntPtr hUpdate, string lpType, int lpName, ushort wLanguage, byte[] lpData, uint cbData);
[DllImport("kernel32.dll")]
static extern IntPtr BeginUpdateResource(string pFileName, bool bDeleteExistingResources);
[DllImport("kernel32.dll")]
static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);
My code:
var hUpdate = BeginUpdateResource(FilePath, false);
var BMP = File.ReadAllBytes(BmpPath);
UpdateResource(hUpdate, "2", 123, 1033, BMP, (uint)BMP.Length);
UpdateResource(hUpdate, "#2", 123, 1033, BMP, (uint)BMP.Length);
UpdateResource(hUpdate, "RT_BITMAP", 123, 1033, BMP, (uint)BMP.Length);
UpdateResource(hUpdate, "BITMAP", 123, 1033, BMP, (uint)BMP.Length);
EndUpdateResource(hUpdate, false);
None of the above UpdateResource calls work. They add the new resource under a new resource type named #2, RT_BITMAP, BITMAP instead of updating the existing resource.
In the P/Invoke declaration of UpdateResource, if I overload string lpType to IntPtr lpType and pass it a new IntPtr(2), everything works but I don't want to use this solution because sometimes I also need string lpType for custom resource types and overloading will require too many changes in my current code design.
MSDN:
lpType [in]
Type: LPCTSTR
The resource type to be updated. Alternatively, rather than a pointer,
this parameter can be MAKEINTRESOURCE(ID), where ID is an integer
value representing a predefined resource type. If the first character
of the string is a pound sign (#), then the remaining characters
represent a decimal number that specifies the integer identifier of
the resource type. For example, the string "#258" represents the
identifier 258.
Any idea why can't I update the existing bitmap by passing lpType a string? I am doing exactly what's stated in MSDN.
PS: I absolutely need to pass lpType a string, can't use IntPtr by overloading because of the reason stated above (too many changes required for current code design).
Most likely MSDN is wrong.
The documentation for the lpName parameter says "when creating a new resource do not use a string that begins with a '#' character for this parameter". I imagine the same restriction applies to lpType.
You can fix this without altering the structure of the rest of your code.
Define both overloads of UpdateResource but make them private and rename them (maybe to UpdateResourceW).
Then define your own public UpdateResource function in C#. This should inspect the lpType parameter. If the type begins with a # convert it to an integer and call the IntPtr lpType overload, otherwise use the string lpType overload.
Thus you can use strings for resource types throughout your code, and handle this detail in a single place.
I'm currently trying to integrate a C++ DLL into our C# application, but I'm not able to identify what's the correct way to call one of their methods. In two different places of the documentation the method definition are not equal:
ImageAndScanError GetMicrInfo(unsigned char *ptrCodeline,int* iLength)
ImageAndScanError WINAPI GetMicrInfo(char* cMicrInfo,int* iInfoLength);
/*
ImageAndScanError GetMicrInfo(unsigned char *ptrCodeline,int* iLength)
Parameters:
ptrCodeline: a pointer to the output buffer that will receive the code line read by the MICR algorithm. The ptrCodeline should allocate room for 96 characters.
iLength: the number of characters contained in the code line
Function: Read MICR line on the check. This function must be called after StartScan .
Returns: ErrorNone is returned upon success. Otherwise, an enum ImageAndScanError value that indicates the reason for failure is returned.
*/
This is how I'm including the dll method
[DllImport("ScanDll.dll", CallingConvention = CallingConvention.Winapi)]
And this are all the combinations that I've made so far
public static extern ImageAndScanError GetMicrInfo(out IntPtr cMicrInfo, out int iInfoLength);
public static extern ImageAndScanError GetMicrInfo(out byte[] cMicrInfo, out int iInfoLength);
public static extern ImageAndScanError GetMicrInfo(out string cMicrInfo, out int iInfoLength);
public static extern ImageAndScanError GetMicrInfo(out StringBuilder cMicrInfo, out int iInfoLength);
IntPtr cMicrInfoTMP;
byte[] cMicrInfoTMP= new byte[96];
string cMicrInfoTMP;
StringBuilder cMicrInfoTMP;
GetMicrInfo(out cMicrInfoTMP, out iInfoLengthTMP);
When I use IntPtr, the value that the debug gives me in VS2010 is 859256727 with a size of 4, and when I do
string myString = Marshal.PtrToStringAnsi(cMicrInfoTMP);
I always get an empty string.
When I try any of the others (byte[], string, StringBuilder) I get
The runtime has encountered a fatal error. The address of the error was at
0x53e6716a, on thread 0x1084. The error code is 0xc0000005. This error may
be a bug in the CLR or in the unsafe or non-verifiable portions of user
code. Common sources of this bug include user marshaling errors for COM-interop
or PInvoke, which may corrupt the stack.
What am I missing here?
Thanks
You can allocate a buffer, then pass to the native function.
//error handling omitted
[DllImport("your.dll", CharSet = CharSet.Ansi)]
ImageAndScanError GetMicrInfo(IntPtr ptrCodeline,ref int bytesCopied);
IntPtr ip = Marshal.AllocCoTaskMem(bufferLen);
Win32API.ZeroMemory(ip, (uint)(bufferLen));
int bytesCopied=0;
GetMicrInfo(ip, ref bytesCopied);
string info= Marshal.PtrToStringAnsi(bytesCopied);
Marshal.FreeCoTaskMem(ip);
If you do not need to reuse the buffer during multiple calls of GetMicrInfo, you can use the default marshaler for StringBuilder:
[DllImport("your.dll", CharSet = CharSet.Ansi)]
ImageAndScanError GetMicrInfo(StringBuilder ptrCodeline,ref int bytesCopied);
StringBuilder ptrCodeline(bufferLen);
int bytesCopied=0;
GetMicrInfo(ptrCodeline, ref bytesCopied);
It comes with a performance hit if you call GetMicrInfo multiple times, because on each call the default CLR marshaller creates a marshalling buffer for pinning and for unicode-ANSI conversion. This hit may be negligible if the function isn't being called frequently or does not return a lot of data.
Reference:
Default Marshaling for Strings
Marshaling between Managed and Unmanaged Code
In .NET, out parameters are used when the callee creates the object. You need to provide an existing buffer to the function so you should initialize the StringBuilder first. The marshaller then passes a pointer to object's the internal character buffer to the function.
You do have to figure out which character set and encoding is being used for the MICR string. It could be UTF-16, in which case, change the declaration to CharSet.Unicode.
Try this:
[DllImport("ScanDll.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Ansi)]
private static extern ImageAndScanError GetMicrInfo(StringBuilder cMicrInfo, out int iInfoLength);
public String GetMicrInfo()
{
StringBuilder info = new StringBuilder(96);
int length;
ImageAndScanError error = GetMicrInfo(info, out length);
if (error != ImageAndScanError.ErrorNone) throw new Exception(String.Format("GetMicrInfo error: {0}", error));
return info.ToString();
}
I have a function made in C++ that calls a COM interface's function
Its signature:
BOOL func(LPWSTR strIn, __out LPWSTR strOut)
{
//initcom
//do something
// release pointers
}
In C#:
[DllImport("funcdll.dll")]
static extern bool func(String strIn, ref String strOut);
// use it
for(int i=0;i<10;i++)
{
if(func(strin, strout))
{
//do something with strout
}
}
I have tested my dll in a C++ console application, it works, but in C# it crashes with an unknown error.
You've got three problems that I can see.
The calling conventions don't match. Your C++ code is cdecl and your C# code is stdcall.
The C++ code uses wide strings, but the C# code marshals ANSI strings.
The second parameter doesn't match. Your C# code assumes that the C++ code returns a new pointer to a C string which the C# code then deallocates with the COM allocator. Your C++ code doesn't do this.
Now, dealing with these in more detail.
Calling conventions
This is pretty easy to fix. Simple change the C++ code to stdcall, or the C# code to cdecl. But don't do both. I'd change the C# code:
[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl]
Unicode/ANSI strings
I presume you are wanting to use Unicode strings since you have explicitly selected them in the C++ code. But P/invoke defaults to marshalling ANSI strings. You can change this again in the DllImport like so:
[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl,
CharSet=CharSet.Unicode]
Returning a string from C++ to C#
Your current C++ function declaration is so:
BOOL func(LPWSTR strIn, __out LPWSTR strOut)
The __out decorator has no real effect, other than documenting that you want to modify the buffer pointed to by strOut and have those modifications returned to the caller.
Your C# declaration is:
static extern bool func(String strIn, ref String strOut);
Now, ref String strOut simply does not match. A ref string parameter matches this in C++:
BOOL func(LPWSTR strIn, LPWSTR *strOut)
In other words the C# code is expecting you to return a new pointer. In fact it will then proceed to deallocate the buffer you returned in strOut by calling CoTaskMemFree. I'm confident that's not what you want.
Your original C++ code can only return a string to the C# code by modifying the buffer that was passed to it. That code would look like this:
BOOL func(LPWSTR strIn, __out LPWSTR strOut)
{
...
wcscpy(strOut, L"the returned string");
...
}
If this is what you want then you should allocate a sufficient buffer in C# in a StringBuilder object.
[DllImport("funcdll.dll"), CallingConvention=CallingConvention.Cdecl,
CharSet=CharSet.Unicode]
static extern bool func(string strIn, StringBuilder strOut);
...
StringBuilder strOutBuffer = new StringBuilder(128);
bool res = func("input string", strOutBuffer);
string strOut = StringBuilder.ToString();
If you simply cannot decide in the C# code how big a buffer you need then your best bet is to use a BSTR to marshal strOut. See this answer for details.
It's hard for me to tell without seeing the details of your C++ method... but, I've never had much luck using String with P/Invoke.
Try using IntPtr instead of String, and use Marshal.PtrToStringUni for the outgoing string, and marshal your managed string into unmanaged land with Marshal.StringToHGlobalUni and, after the function call, make sure to free the unmanaged string with Marshal.FreeHGlobal.
Also, coming from C99 land, my bools don't work with .NET bools... I don't know why. I have to use byte and check == 1. Don't know if you'll run into this with C++... but, if you want my advice on a place to start which seems least-breakable in my experience, here ya go:
[DllImport("funcdll.dll")]
static extern byte func(IntPtr strIn, out IntPtr strOut);
// use it
string myString = "Testing";
IntPtr stringOut;
IntPtr stringIn = Marshal.StringToHGlobalUni(myString);
if(func(stringIn, out stringOut) == 1)
{
//do something with strout
string stringOutValue = Marshal.PtrToStringUni(stringOut);
// Depending on how you dealt with stringOut in your
// unmanaged code, you may have to: Marshal.FreeCoTaskMem(stringOut);
// or Marshal.FreeHGlobal(stringOut) if you're returning
// an owning reference to a copied string.
}
Marshal.FreeHGlobal(stringIn);
I beleive you're having a calling convention mismatch. The default calling convention in C++ is cdecl while for .NET it's stdcall. try
[DllImport("funcdll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern bool func(String strIn, ref String strOut);
Also you might have to specifically tell the marshallar that you want to marshal the strings as LPWSTR using [MarshalAs(UnmanagedType.LPWStr)] attributes.
[DllImport("funcdll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern bool func([MarshalAs(UnmanagedType.LPWStr)]String strIn
,[MarshalAs(UnmanagedType.LPWStr)]ref String strOut);
have a look at http://msdn.microsoft.com/en-us/library/s9ts558h.aspx
In C++ make sure you have something like this (at least for the second argument)
extern "C" BOOL __stdcall func( BSTR * pBstr )
{
*pBstr = SysAllocString( L"Foobar" );
return 0;
}
In C# write something like this (for the second argument) :
static extern bool func( [MarshalAs(UnmanagedType.BStr)] ref String strOut);
Sorry I do not have a big answer but just remember something from my experience.. did you try to use StringBuilder eg changing your c# import function signature as
[System.Runtime.InteropServices.DllImport("funcdll.dll")]
static extern bool func(String strIn, System.Text.StringBuilder strOut);
I am making a wrapper to read TDM and TDMS files but i have a problem
[DllImport(lib, CharSet = CharSet.Auto)]
static extern int DDC_OpenFileEx(
[MarshalAs(UnmanagedType.LPStr)]
string filePath,
[MarshalAs(UnmanagedType.LPStr)]
string fileType,
int read_only,
ref long file);
works fine but
[DllImport(lib, CharSet = CharSet.Auto, SetLastError = true)]
static extern int DDC_GetNumChannelGroups(long file,
[MarshalAs(UnmanagedType.U4)]
ref int numChannelGroups);
int numGru = 0;
errCode = ReadTDM.DDC_GetNumChannelGroups(file,ref numGru);
System.Console.WriteLine("Error Code {0} GetNumChannelGroups", errCode);
gives an error -6202, // An invalid argument was passed to the library.
i have tried ref uint, uint * (unsafe), UIntPtr. The def from .h file
int __stdcall DDC_GetNumChannelGroups (DDCFileHandle file,unsigned int *numChannelGroups);
the second parametr is the problem.
it seems that unsigned int* != uint.
Does anyone have an idea how to call this function form the dll?
http://forums.ni.com/ni/board/message?board.id=60&thread.id=11821
It is the 1st argument that's declared wrong. That throws off the stack frame and prevents the unmanaged code from properly reading the pointer for the 2nd argument. "long" is 64-bits, DDCFileHandle is almost certainly a pointer, 32-bits on a 32-bit operating system.
Change the argument declaration to IntPtr. You'll also need to change the declaration of the function that returns that handle.