C# pointer on unmanaged data from C++ CLI project - c#

I have a SDK written in C++ which manages a device. My program controlling the device is written in C#, so naturally a CLI wrapper class does the translation between both languages. My C# project includes the wrapper as a DLL.
My issue is that the C++ SDK is using pointers to head to arrays of data. These pointers are also available in the wrapper.
Wrapper .cpp code:
Wrapper::Wrapper()
{
myData = new DataAquis(); //initiate C++ class's instance
}
int Wrapper::Start()
{
//(..)
int num = myData->Start();
ptr = (myData->img);
return num;
}
This code initializes the device and creates a pointer to a data structure (array of unsigned char).
Wrapper SDK .cpp code:
int DataAquis::Start()
{
// (...)
// Pointer from SDK
img = pBuffer;
// (...)
return FAILED(nError) ? -1 : 0;
}
Wrapper .h code:
public ref class Wrapper
{
public:
Wrapper();
// (...)
unsigned char *ptr;
private:
// (...)
};
Code C#:
public static Wrapper myDataAqui;
// (...)
private static void DataAquisition()
{
// call the start function in wrapper
myDataAqui.Start();
// Unsafe code for pointer use
unsafe
{
// point to aquired data
byte* imgptr1 = myDataAqui.ptr;
// AccesViolationException in above line.
// Data processing
for (y = 0; y < 256; y++)
{
for (x = 0; x < 320; x++)
{
int temp = x * 256 + 255 - y;
Spectrum1.X_Data_brut[bufferIndex][temp] = (UInt16)(*imgptr1++ + *imgptr1++ * 256);
aquirData[temp] = Spectrum1.X_Data_brut[bufferIndex][temp];
}
}
// (...)
}
}
As shown, an AccessViolationException is triggered at the line where I cast the Wrapper pointer to a local byte pointer.
If I put a breakpoint on that line, I can see that the Wrapper pointer correctly points to a memory address, but says that it is unable to read memory, so the pointed data is never gathered in C#.
I have read that the C# equivalent of an unsigned char in C++ is a byte, so normally I should read the same amount of data and never go outside the boundaries of my data structure.
Additionnal information that could be useful:
This project has been copied from another PC and the same code is functional on that PC.
Both PC have same Visual Studio, same .Net version, same SDK, both 64 bit compiled. Only Windows versions differ (working on Windows 8 and not working on Windows 7).
I unsuccessfully tried using Marshal functions.
Do you have any ideas how to fix this ?

I'm not sure why you're getting an exception but I'd marshal it into a CLR array on the C++/CLI side so no unsafe code is needed on the C# side.
C++/CLI:
#include <vcclr.h>
#include <algorithm>
#pragma unmanaged
const int data_size = 100;
unsigned char * foo()
{
auto p = new unsigned char[data_size];
for (int i = 0; i < data_size; ++i)
p[i] = (unsigned char)i;
return p;
}
#pragma managed
public ref class Wrapper
{
public:
array<unsigned char>^ data;
void Start()
{
auto p = foo();
data = gcnew array<unsigned char>(data_size);
pin_ptr<unsigned char> data_pin = &data[0];
std::copy(p, p+data_size, (unsigned char *)data_pin);
}
};
C#:
class Program
{
static void Main(string[] args)
{
var wrapper = new Wrapper();
wrapper.Start();
System.Console.WriteLine($"{wrapper.data[15]}");
}
}
This will contain any possible problems close to the source and make debugging a lot less confusing. If it crashes in std::copy then you're just using your C++ object wrong.

Related

Passing struct array pointer from c# to c++

I have a c++ dll from a third party, I can't modify.
I've made a c++ wrapper to call the functions inside that c++ dll.
That wrapper is called from a C# class with IJW.
It works well with natives types, but I ran into a struct array problem.
This is the c++ wrapper class :
int Helper::GetCameras(cameraStruct * cameraArray, size_t * size)
{
return originalC++Dll_getCameraList(cameraArray, size);
}
And the C# call :
var ret = m_Helper.GetCameras(pointer, size);
Do I need to redefine the "cameraStruct" in c# like this ?
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class aivusdk_cameraInfo
{
public int blabla;
}
To use it like with a pointer in the C# function, or is it another way of doing it (mashalling), or am I completly wrong ?
Forgive my bad english and thanks for your response.
The primary reason to use IJW is to avoid all the hassle of trying to define parallel structures in your C# code using StructLayout and calling all that stuff in the Marshal class. If you are going to go to that trouble, you may as well do everything using P/Invoke.
One approach would be to define, in your C# assembly, an interface that will be implemented by the c++/clr assembly and a managed class containing properties corresponding to the interesting fields of the cameraStruct structure.
public interface IHelper
{
public void GetCameras(IList<Camera> cameras);
public Picture TakePicture(Camera camera);
public void LoseCamera(Camera camera);
}
public class Camera
{
public string Name { get; set; }
public int BlaBla { get; set; }
}
The Helper class in your c++/clr assembly would implement the IHelper interface. The implementation of the GetCameras method would call originalDLL_getCameraList with a locally allocated array of cameraStruct. It would then iterate over the returned cameras and gcnew a Camera object for each one. It would copy the information from the cameraStruct to the Camera object and then add the Camera object to the cameras list. The locally allocated cameraStruct array can then be freed.
One issue is that the c++/clr project must have a build dependency (reference) to the C# project. The C# project cannot have a reference to the c++/clr project. For your C# code to get a Helper object, you could dynamically load the c++/clr assembly from the C# assembly and use reflection to create a Helper object that you cast to an IHelper.
You can turn that relationship around, but that means defining more in c++/clr and less in C#. (For example, you would need to define the Camera class in the c++/clr assembly.) I prefer to define things using C# when practical.
You need to allocate unmanaged memory for the array that you can pass to the API.
This is how I would do that:
// the input here is a managed C# array
public IntPtr AllocateNativeArray(CameraInfo[] myArray)
{
if (myArray == null || myArray.Count == 0)
return IntPtr.Zero;
// building native structures
var nativeArray = new Aivusdk_CameraInfo[myArray.Length];
for (int i = 0; i < myArray.Length; i++)
{
// TODO: fill properties
}
// allocating unmanaged memory and marshaling elements
int elementSize = Marshal.SizeOf(typeof(Aivusdk_CameraInfo));
IntPtr result = Marshal.AllocHGlobal(nativeArray.Length * elementSize);
for (int i = 0; i < nativeArray.Length; i++)
{
Marshal.StructureToPtr(nativeArray[i], new IntPtr((long)result + i * elementSize), false);
}
return result;
}
After calling the C++ API you might need to free the array (unless the C++ part does that):
private static void FreeNativeArray(IntPtr address, uint length)
{
if (address== IntPtr.Zero)
return;
int elementSize = Marshal.SizeOf(typeof(Aivusdk_CameraInfo));
for (int i = 0; i < count; i++)
{
Marshal.DestroyStructure(new IntPtr((long)address + i * elementSize), typeof(Aivusdk_CameraInfo));
}
Marshal.FreeHGlobal(address);
}

Passing parameters from C# to C++ CLI DLL

I'm not good at all with C++, so please excuse me if this is easy. I have had to write 2 C++ DLL's in order to call functions in an SDK that we have to use. The first is a native C++ DLL, which imports the SDK functionality. The second is a CLI C++ DLL, which calls the first DLL. The second DLL is referenced in a C# project and is then called in order to communicate with the SDK from within C#, as I have no other way of talking to the C++ SDK from C# (P/Invoke, etc, nothing works). My main problem is how to pass values from C# to the C++ DLL. When I debug, my values come through empty Here are small snippets which summarize what I've done.
C++ Native DLL.h
namespace N
{
class __declspec(dllexport) C
{
public:
int Write(char a[14], char* B);
}
}
C++ Native DLL.cpp
namespace N
{
int N::C::Write(char a[14], char* B)
{
// Here I need to add a 1 in front and \0 at the end of variable a
char t[16] = {0};
t[0] = 0x31;
strcpy(&t[1], a);
}
}
C++ CLI DLL.h
namespace N
{
class C;
namespace NN
{
public ref class CC
{
public:
int Write(char a[14], char* B);
}
}
}
C++ CLI DLL.cpp
N::NN::C::CC() : _impl(new N::NN::C())
{
}
int Write(char a[14], char* B)
{
return _impl->Write(a, B);
}
My big problem is how to pass the values from C# to the CLI dll so they are usable. I cannot change the data types or definitions in the C++ dll's. At the moment this is what I have in C#, after referencing the CLI dll:
C# method (partial):
N.NN.C.CC c = new N.NN.C.CC();
byte[] ba = Encoding.ASCII.GetBytes("11111111111111");
byte[] bB = Encoding.ASCII.GetBytes("BBBBBB");
unsafe
{
sbyte* bpa;
sbyte* bpB;
fixed (byte* pa = ba)
{
bpa = (sbyte*)pa;
}
fixed (byte* pB = bB)
{
bpB = (sbyte*)pB;
}
int iResult = c.Write(bpa, bpB);
}
This is all done in VS 2012. My problem is getting the first parameter a to be usable in the first DLL. Please let me know if I should give more details anywhere or try explain better, but I hope this will suffice.

System.AccessViolationException thrown by Visual C# on call to Lapack inside wrapped C++\CLI dll

I have written some code in native C++ which makes calls to fortran routines of the blas and lapack library (linked as dll's). The native code runs fine, with no compiler or runtime errors or warnings.
Now, I was trying to wrap the native C++ into a dll to use in the .NET framework, so I started a Visual C++ project, wrote a wrapper ref class, compiled this into a dll and included that dll into a C# project.
After I finally managed to get the C# project to compile, I tried to run it. But I get a runtime error at the first call to a lapack routine! (i.e. at the line dpbtrf_( &LOWER, &n, &izero, D.data(), &ione, &info ); in Test.cpp)
The error says:
System.AccessViolationException was unhandled
Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Source=DotNetTest
Am I missing some compiler option?
Or is there really corrupt memory which the native C++ compiler\runtime did not see?
Any tips on how to debug this?
Thanks a lot!
Here's a tiny reproduction that generates the same problem as the original code (which is too extensive to post here):
the native C++ Test.h:
#ifndef TEST_H_INCLUDED
#define TEST_H_INCLUDED
#include <vector>
namespace NativeTest
{
class Test
{
public:
Test( int size );
~Test();
void set( int i, double d );
double get( int i ) const;
int chol();
private:
std::vector<double> D;
int n;
}; // class Test
} // namespace NativeTest
#endif // TEST_H_INCLUDED
the native C++ Test.cpp (included in the VC++ project) (!EDITTED with __cdecl):
#include "Test.h"
using namespace NativeTest;
const int ione = 1;
const int izero = 0;
const char LOWER = 'L';
extern "C"
{
// factorization for banded matrix
void __cdecl dpbtrf_( const char *UPLO, const int *N, const int *KD,
double *AB, const int *LDAB, int *INFO );
}
Test::Test( int size ) : n( size )
{ D.resize( n ); }
Test::~Test() { }
void Test::set( int i, double d ) { D[ i ] = d; }
double Test::get( int i ) const { return D[ i ]; }
int Test::chol()
{
int info = 0;
dpbtrf_( &LOWER, &n, &izero, D.data(), &ione, &info );
return info;
}
The C++\CLI wrapper:
// TestNet.h
#pragma once
#include "Test.h"
using namespace System;
namespace DotNetTest {
public ref class TestNet
{
public:
TestNet( int size ) { test = new NativeTest::Test( size ); }
~TestNet() { this->!TestNet(); }
!TestNet() { delete test; test = NULL; }
void set( int i, double d ) { test->set( i, d ); }
double get( int i ) { return test->get( i ); }
int chol() { return test->chol(); }
protected:
NativeTest::Test * test;
};
}
The C# call:
DotNetTest.TestNet t = new DotNetTest.TestNet(2);
t.set(0, 2);
t.set(1, 3);
int info = t.chol();
I got it fixed, at least for the test code posted here. I wrongly added the gcc-compiled lapack\blas binaries, the same as I used for my original project (which also used gcc).
Today, I downloaded the precompiled binaries for windows from here, put those in the bin folder of my C# project (together with the 3 MinGW dll's: libgcc_s_dw2-1.dll, libgfortran-3.dll and libquadmath-0.dll) and made sure the Visual C++\CLI project used the same libraries to link to (by setting the Additional Link Directory to the C# bin folder). This fixed it, and I got the result I wanted (no access violation and the calculation was correct).
I also removed the /clr flag in the compile options for the Test.cpp file, and switch the 'Use precompiled headers' off for that file.

Passing a byte pointer to a C# method via reverse PInvoke

In the past, I have passed a byte array from a C# method to an unmanaged C++ function. I am now trying to pass a pointer to a buffer of type unsigned char from a C++ method back into a C# method using reverse PInvoke, which uses a callback to get back to the C# code. I have tried several different ideas - like passing Ref Byte, Byte *, and IntPtr for the 2nd argument, but none of them seem to work. Here is my test code for using IntPtr:
C# code:
namespace TestPInvoke
{
class Program
{
static void Main(string[] args)
{
foo f = new foo();
f.DispMsg();
}
}
unsafe public class foo
{
public delegate void callback(int NumBytes, IntPtr pBuf);
public static void callee(int NumBytes, IntPtr pBuf)
{
System.Console.WriteLine("NumBytes = " + NumBytes.ToString() + ", pBuf = ");
String s = "";
Byte* p = (Byte*)pBuf.ToPointer();
for (int Loop = 0; Loop < 50; Loop++)
{
s += p++->ToString() + " ";
}
System.Console.WriteLine(s);
}
public void DispMsg()
{
caller(new callback(foo.callee));
}
[DllImport(#"C:\Users\Bob\Documents\Visual Studio 2008\Projects\AttackPoker1\Win32Client\TestPInvoke\bin\Debug\TestPInvokeDLLCPP.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void caller(callback call);
}
}
C++ code:
#include <stdio.h>
#include <string.h>
typedef unsigned char Byte;
typedef void (__stdcall *callback)(const int bytesInMsg, Byte* pintBuf);
extern "C" __declspec(dllexport) void __stdcall caller(callback call)
{
// Debug Test on how to pass a pointer to a byte buffer to a C# method.
Byte* pBuf = new Byte[50];
// Initialize the buffer to something.
Byte* p = pBuf;
for (Byte Loop = 0; Loop < 50; Loop++)
*p = Loop;
// Initiate the callback into the C# code.
call(50, pBuf);
// Delete pBuf later.
}
When the C++ code calls the C# callback callee method, the bytesInMsg argument is correct. But, the returned pointer does not point to the start of the buffer. Dereferencing the pointer always seems to point to the last value in the buffer (49 or 0x31), but after looking at it in the memory window, the rest of the bytes both before and after are garbage.
Does anyone have any suggestions on how I can get this to work without marshaling a large array? What I'm hoping to do is pass a pointer to a large buffer created on the C++ side one time to a C# class that will then be able to read data from that buffer efficiently.
If this can not be done, then I will have to allocate the memory buffers from C#, pin them, and pass them into the C++ methods.
All the pinvoke is fine and works properly. You just have a silly bug in your C++ code, you are forgetting to increment the pointer so you only ever set the first element of the array. Use
*p++ = Loop;
Or the more sane version that simply indexes the array:
// Initialize the buffer to something.
for (int ix = 0; ix < 50; ++ix)
pBuf[ix] = ix;

Simplest way to move an array from C++ to C#, modify it, and pass it back to C++

I have a C# class library that contains methods that need to be used with an external application. Unfortunately this external application only supports external APIs in C/C++.
Now I've managed to get a very simple COM example working between a C++ dll and a C# DLL, but I am stuck on how I can move around array data.
This is what I've got so far, just as a little example I found on the web of communicating via COM:
DLL_EXPORT(void) runAddTest(int add1,long *result) {
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
// Create the interface pointer.
IUnitModelPtr pIUnit(__uuidof(UnitModel));
long lResult = 0;
// Call the Add method.
pIUnit->Add(5, 10, &lResult);
*result = lResult;
// Uninitialize COM.
CoUninitialize();
}
This works fine to call the add method in my C# class. How can I modify this to take and return an array of doubles? (ill also need to do it with strings down the line).
I need to take an unmanaged array , pass this array to a C# class for some calculations, and then pass it back the results to the array reference specified in the original function call (unmanaged) C++.
I'll need to expose a function like this:
*calcin - reference to array of doubles
*calcOut - reference to array of doubles
numIN - value of size of input array
DLL_EXPORT(void) doCalc(double *calcIn, int numIn, double *calcOut)
{
//pass the calcIn array to C# class for the calcuations
//get the values back from my C# class
//put the values from the C# class
//into the array ref specified by the *calcOut reference
}
I think I can use a C++\CLI DLL for the external application so if this is easier than straight COM then i'll be willing to look at that.
Please be gentle as I am primarily a C# developer but have been thrown in the deep end of Interop and C++ .
I experimented with this a while ago but have unfortunately forgotten how it all fitted together... for my purpose it turned out to be woefully slow so I cut out the C# and went back to all C++. When you say you're primarily a C# developer I hope you understand pointers because if you don't there's no way to be gentle.
Passing arrays basically came down to using CoTaskMemAlloc family of functions on the C++ side (http://msdn.microsoft.com/en-us/library/ms692727(VS.85).aspx) and the Marshal class on the C# side (http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.aspx - which has methods like AllocCoTaskMem). For C# I ended up with a utility class:
public class serviceUtils
{
unsafe public long stringToCoTaskPtr( ref str thestring )
{
return (long)Marshal.StringToCoTaskMemAnsi(thestring.theString).ToPointer();//TODO : what errors occur from here? handle them
}
unsafe public long bytesToCoTaskPtr( ref bytes thebytes, ref short byteCnt)
{
byteCnt = (short)thebytes.theArray.Length;
IntPtr tmpptr = new IntPtr();
tmpptr = Marshal.AllocCoTaskMem(byteCnt);
Marshal.Copy(thebytes.theArray, 0, tmpptr, byteCnt);
return (long)tmpptr.ToPointer();
}
public void freeCoTaskMemPtr(long ptr)
{
Marshal.FreeCoTaskMem(new IntPtr(ptr));//TODO : errors from here?
}
public string coTaskPtrToString(long theptr)
{
return Marshal.PtrToStringAnsi(new IntPtr(theptr));
}
public byte[] coTaskPtrToBytes(long theptr, short thelen)
{
byte[] tmpbytes = new byte[thelen];
Marshal.Copy(new IntPtr(theptr), tmpbytes, 0, thelen);
return tmpbytes;
}
}
Just to throw some more code at you:
this c++
#import "..\COMClient\bin\Debug\COMClient.tlb" named_guids raw_interfaces_only
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL); //Initialize all COM Components
COMClient::IComCalculatorPtr pCalc;
// CreateInstance parameters
HRESULT hRes = pCalc.CreateInstance(COMClient::CLSID_ComCalculator);
if (hRes == S_OK) {
long size = 5;
LPVOID ptr = CoTaskMemAlloc( size );
if( ptr != NULL )
{
memcpy( ptr, "12345", size );
short ans = 0;
pCalc->changeBytes( (__int64*)&ptr, &size, &ans );
CoTaskMemFree(ptr);
}
}
CoUninitialize (); //DeInitialize all COM Components
return 0;
}
called this c#
public short changeBytes(ref long ptr, ref int arraysize)
{
try
{
IntPtr interopPtr = new IntPtr(ptr);
testservice.ByteArray bytes = new testservice.ByteArray();
byte[] somebytes = new byte[arraysize];
Marshal.Copy(interopPtr, somebytes, 0, arraysize);
bytes.theArray = somebytes;
CalculatorClient client = generateClient();
client.takeArray(ref bytes);
client.Close();
if (arraysize < bytes.theArray.Length)
{
interopPtr = Marshal.ReAllocCoTaskMem(interopPtr, bytes.theArray.Length);//TODO : throws an exception if fails... deal with it
}
Marshal.Copy(bytes.theArray, 0, interopPtr, bytes.theArray.Length);
ptr = interopPtr.ToInt64();
arraysize = bytes.theArray.Length;
//TODO : do we need to free IntPtr? check all code for memory leaks... check for successful allocation
}
catch(Exception e)
{
return 3;
}
return 2;
}
Sorry, but I don't have the time to work all this out and explain it properly, hopefully this will give you pointers in the right direction, at the very least some things to google. Good Luck
PS : I got all the info to write this stuff off the net, so it is out there.
I think I can use a C++\CLI DLL for the external application so if this is easier than straight COM then i'll be willing to look at that.
If you don't have much COM experience (and arrays are significantly not simple in COM) then C++/CLI wrapper around the 3rd party will likely be easier.
It will also only involve a single marshalling stage (native <-> managed) rather than the extra step the necessary COM Callable Wrapper you will need for managed <-> COM interfacing).
Would this also work?
In C#,
1. Call Marshal.PtrToStructure
2. Modify the value
3. Call Marshal.StructureToPtr

Categories