I have C++ library (32bit), which I would like to load and use in C#. The DLL is working (open connection, close connection and another methods without parameters). The problem is with unexpected results of all methods of C++ library with have parameters. For example:
C++ library methods specification:
unsigned int WINAPI acqStatus (WORD& stat, WORD& err);
unsigned int WINAPI getStatus (double& time, TCHAR* data);
C# source code (extern methods):
[DllImport("DllName.dll")]
public static extern uint acqStatus(ref ushort stat, ref uint err);
[DllImport("DllName.dll")]
public static extern uint getStatus(ref double time, ref StringBuilder data);
C# first method example (returns bad value, or Im doying mistake to convert IntPtr to integer):
ushort stat = 0;
uint err = 0;
TCPInterface.acqStatus(ref stat, ref err);
// Result of stat is: 0x0003 or 0x0004
// bad value 3 (expected is 0), bad value 4 (expected is 1)
Same situation I have with another method, with returns TCHAR*. Result is string (array of bytes). I dont know, how I get the string from data variable.
double time;
StringBuilder data = new StringBuilder();
getStatus(out time, out data);
// Exception:
An unhandled exception of type 'System.AccessViolationException' occurred in mscorlib.dll
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I'm using 64bit Windows 7 and Visual Studio 2015. I sets my C# project to x86.
I tried to:
Marshal.Copy(...) or Marshal.ReadByte(...) Returns following exception:
An unhandled exception of type 'System.AccessViolationException' occurred in mscorlib.dll Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Tried to run on x86 Windows 7: DLL result was the same (bad result).
Tried to change the IntPtr to byte[] I gets same exception like in Marshal.Copy()
Tried to change out with ref // not helped
Example 1 updated:
[DllImport("DllName.dll")]
public static unsafe extern int acqStatus(ref ushort* stat, ref uint* err);
// Elsewhere in source code in unsafe method
ushort* stat = 0;
uint* err = 0;
TCPInterface.acqStatus(ref stat, ref err);
// I have following exception:
An unhandled exception of type 'System.AccessViolationException' occurred in WindowsFormsApplication1.exe
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
That's because ToInt32() and ToString() don't work as you expect. These methods returns the memory address of the pointer, not the retrieved value.
For the first signature, change it to:
[DllImport("DllName.dll")]
// WORD C++ is equivalent to ushort in C#
public static extern uint acqStatus(ref ushort stat, ref ushort err);
For the second it's a little bit more complicated without the exact implementation but a good try will be :
[DllImport("DllName.dll", CharSet = CharSet.Auto)]
public static extern uint getStatus(ref double time, StringBuilder data);
Related
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.
I have a c++ native dll file and I should call some functions from Dll by c#
the c++ function that I should call is
extern NCSError NCS_CALL NCSOpenFileViewA(const char *szUrlPath, NCSFileView **ppNCSFileView,NCSReadStatus (*pRefreshCallback)(NCSFileView *pNCSFileView));
NCSFileView object is
typedef struct NCSFileViewStruct NCSFileView;
and working example c++ code is
NCS::CApplication App
NCSCompressClient *pClient;
char *szInputFilename = "C:\\testdata\\RGB_8bit.ecw";
NCSFileView *pNCSFileView;
NCSFileInfo *pNCSFileInfo;
NCSFileMetaData *pNCSFileMeta;
NCSError eError;
NCSInit();
eError = NCSOpenFileViewA(szInputFilename, &pNCSFileView, NULL);
if (eError != NCS_SUCCESS) {
ReportError("Could not open view for file:%s, Error = %s",
szInputFilename, NCSGetErrorText(eError));
exit(1);
}`
I made a c# code like this
[DllImport("C:\\NCSEcw.dll",CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr NCSOpenFileViewA ([MarshalAs(UnmanagedType.LPStr)] string path,
IntPtr a,
IntPtr c);
but i get en error like this
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
so, can you help me to write wrapper?
I'm trying to use phash library using this wrapper https://github.com/ludoviclefevre/phash-integration-in-csharp
c++ header:
int ph_dct_imagehash(const char* file,ulong64 &hash)
c# dll import:
DllImport(#"pHash.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int ph_dct_imagehash(string file, ref ulong hash);
test code:
ulong hash = 0;
foreach (var file in files)
{
ph_dct_imagehash(file, ref hash);
dictionary.Add(file, hash);
}
It works perfectly for few picture, but when ther's about 200-300 pictures i got Accesviolation exception
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
My first lead is garbage collector but i'm confused.. should i use intpr instead of string and hash parameters? I tried changing ref parameter to out but it doesn't matter..
I am trying to return a pointer from my C++ dll to C#. I tried everything, but my pointer of type double doesn't have a value.
Below is my import of the C++ dll:
[DllImport("/Resources/libfli.dll", EntryPoint = "FLIGetTemperature")]
public static extern unsafe int FLIGetTemperature(long dev, double* temperature);
// Get the temperature of a given camera. This function places the temperature of the CCD camera
// cold finger of device dev in the location pointed to by temperature.
// Return Value: Zero on success. Non-zero on failure.
// Parameters: dev Camera device to get the temperature of.
// temperature Pointer to where the temperature will be placed.
// See Also: FLISetTemperature
the defination of FliGetTemperatre from the FLI_SDK_Documentation
LIBFLIAPI FLIGetTemperature (flidev_t dev, double* temperature)
Get the temperature of a given camera.
Below is how I declare my call to the .Dll in C#:
unsafe public void GetTheTemperatureOfTheCamera()
{
int success=0;
long ldev = 0;
long* dev = &ldev;
double lTemperature = 0;
double* temperature = &lTemperature;
success = FLIGetTemperature(ldev, temperature);
}
When I run my code I get the following error below:
An unhandled exception of type 'System.AccessViolationException' occurred in myApplication.exe
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I also tried marshaling and IntPtr, but that is not working either.
I have tried Fliopen that fucntion works. It success. Below is the code for FliOpen.
[DllImport("/Resources/libfli.dll", EntryPoint = "FLIOpen")]
public static extern unsafe int FLIOpen(long* dev, string name, long domain);
unsafe public int InitDevice()
{
long ldev = 0;
long* dev = &ldev;
int success;
string deviceName = "flipro0";// this is default name for device
long domainName = 258; //this is default domain name
success = FLIOpen(dev, deviceName, domainName);
return success;
}
The FLIOpen method is a success though
Reading the documentation, I would have wrote something like this :
unsafe public void GetTheTemperatureOfTheCamera()
{
int success=0;
long dev = 0;
long* ldev = &dev;
success = FLIOpen(ldev, deviceName, domainName); //Get the device handle.
if (success != 0)
throw new Exception("Cannot open device");
double lTemperature = 0;
double* temperature = &lTemperature;
success = FLIGetTemperature(dev, temperature);
}
There is a mismatch between your C# long parameter and the unmanaged type flidev_t. The key point is that long is a 64 bit type. Assuming your code is running in Windows then flidev_t, which is an alias of C++ long, is 32 bits wide.
You also have no need for unsafe code here. You can declare the function like this:
[DllImport("/Resources/libfli.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int FLIGetTemperature(int dev, out double temperature);
I'm also assuming that the calling convention is cdecl, but you probably need to check the C++ header file to be sure about that.
You'll need to make other changes in your other p/invoke declarations. Essentially everywhere you have translated flidev_t to C# long you should change it to int. Or perhaps even IntPtr. Then you'd be one step ahead of the unmanaged code which currently is not fit for a 64 bit port I suspect!
Starting with FLIOpen that is going to be:
[DllImport("/Resources/libfli.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int FLIOpen(out int dev, string name, int domain);
Well, the domain parameter might be better translated as a C# enum with int base type.
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();
}