Pointers btwn C# and C++

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

I have a native C++ pointer object which I need to hold in a C# environment,
initialize in the C++ environment and the pass between the C# and C++
environments. Which is the data structure to use? I have tried to use an
IntPtr in C#, send it through C++/CLI as a System::IntPtr and convert it to a
pointer of my class in the end, but that gives compilation errors. What I
guess I need is som C# equivalence to a void* to keep in the C# environment.
But I have no clue how to do it...
 
I have tried to use an
IntPtr in C#, send it through C++/CLI as a System::IntPtr and convert it to a
pointer of my class in the end, but that gives compilation errors.

Can you post the errors and your code? Using IntPtr should work
nicely. You can also use an actual void* in C# if you enable unsafe
code.


Mattias
 
Joachim said:
I have a native C++ pointer object which I need to hold in a C#
environment,
initialize in the C++ environment and the pass between the C# and C++
environments. Which is the data structure to use? I have tried to use an
IntPtr in C#, send it through C++/CLI as a System::IntPtr and convert it
to a
pointer of my class in the end, but that gives compilation errors. What I
guess I need is som C# equivalence to a void* to keep in the C#
environment.
But I have no clue how to do it...

An IntPtr is the right thing to use, as long as this is a native pointer,
i.e. not a pointer into a garbage collected object. Use the ToPointer()
method to get the native pointer back.
 
The C# app:

IntPtr m_DVPSDK_ptr;

[DllImport(g_wrapper_dll_path)]
static extern int Initialize(
out IntPtr class_ptr,
ref int[] devices_ids,
Int32 encoder_id);

[DllImport(g_wrapper_dll_path)]
static extern string GetLastError(
IntPtr class_ptr,
Int32 encoder_id);

void Initialize()
{
int[] l_devs_ids = new int[g_max_devices];
int l_nr_of_devices = Initialize(
out m_DVPSDK_ptr,
ref l_devs_ids,
m_settings.EncoderId);

if (l_nr_of_devices == 0)
{
//TODO: throw error?
}
}


The C++/CLI code:

extern "C" int Initialize(
System::IntPtr class_ptr,
array<int>^ devices_ids,
int encoder_id)
{
return Impl::Initialize(
(DVP1412DLL*) class_ptr, //ERROR 2 BELOW
devices_ids,
encoder_id);
}

Error 2 error C2440: 'type cast' : cannot convert from 'System::IntPtr' to
'DVP1412DLL *'

extern "C" const System::String^ GetLastError(
System::IntPtr class_ptr,
int encoder_id)
{
return Impl::GetLastError(
(DVP1412DLL*) class_ptr, //ERROR 12 BELOW
encoder_id);
}


Error 12 error C2440: 'type cast' : cannot convert from 'System::IntPtr' to
'DVP1412DLL *'
 
Only declaring an IntPtr (IntPtr myPtr;) in C# and passing it as an out
parameter to the C++ dll should work just fine then?
 
Seemed like it had to do with that I declared the functions as

__declspec(dllexport)

However, even though it compiles, the value of the IntPtr is not changed in
C# even though it is in C++

Joachim said:
The C# app:

IntPtr m_DVPSDK_ptr;

[DllImport(g_wrapper_dll_path)]
static extern int Initialize(
out IntPtr class_ptr,
ref int[] devices_ids,
Int32 encoder_id);

[DllImport(g_wrapper_dll_path)]
static extern string GetLastError(
IntPtr class_ptr,
Int32 encoder_id);

void Initialize()
{
int[] l_devs_ids = new int[g_max_devices];
int l_nr_of_devices = Initialize(
out m_DVPSDK_ptr,
ref l_devs_ids,
m_settings.EncoderId);

if (l_nr_of_devices == 0)
{
//TODO: throw error?
}
}


The C++/CLI code:

extern "C" int Initialize(
System::IntPtr class_ptr,
array<int>^ devices_ids,
int encoder_id)
{
return Impl::Initialize(
(DVP1412DLL*) class_ptr, //ERROR 2 BELOW
devices_ids,
encoder_id);
}

Error 2 error C2440: 'type cast' : cannot convert from 'System::IntPtr' to
'DVP1412DLL *'

extern "C" const System::String^ GetLastError(
System::IntPtr class_ptr,
int encoder_id)
{
return Impl::GetLastError(
(DVP1412DLL*) class_ptr, //ERROR 12 BELOW
encoder_id);
}


Error 12 error C2440: 'type cast' : cannot convert from 'System::IntPtr' to
'DVP1412DLL *'


Mattias Sjögren said:
Can you post the errors and your code? Using IntPtr should work
nicely. You can also use an actual void* in C# if you enable unsafe
code.


Mattias
 
Joachim said:
Seemed like it had to do with that I declared the functions as

__declspec(dllexport)

However, even though it compiles, the value of the IntPtr is not changed
in
C# even though it is in C++

a C# ref or out parameter must be declared in C++/CLI as a reference
parameter.
Joachim said:
The C# app:

IntPtr m_DVPSDK_ptr;

[DllImport(g_wrapper_dll_path)]
static extern int Initialize(
out IntPtr class_ptr,
ref int[] devices_ids,
Int32 encoder_id);

Don't do this. To use C++/CLI functions from C#, add a reference to the
generated DLL.
[DllImport(g_wrapper_dll_path)]
static extern string GetLastError(
IntPtr class_ptr,
Int32 encoder_id);

void Initialize()
{
int[] l_devs_ids = new int[g_max_devices];
int l_nr_of_devices = Initialize(
out m_DVPSDK_ptr,
ref l_devs_ids,
m_settings.EncoderId);

if (l_nr_of_devices == 0)
{
//TODO: throw error?
}
}


The C++/CLI code:

extern "C" int Initialize(
System::IntPtr class_ptr,
array<int>^ devices_ids,
int encoder_id)
{
return Impl::Initialize(
(DVP1412DLL*) class_ptr, //ERROR 2 BELOW

Should be class_ptr->ToPointer(), but see below...

The compiler shouldn't even allow what you're doing... you have managed
types as parameters, and those are limited to member methods of managed
types. Create a ref class and make these functions static methods within
that class. Or, make them instance methods, so you don't have to pass
around class_ptr.

You're also missing a level of indirection all over the place here. You
want Impl::Initialize to fill in the pointer, right?

Then:

int Initialize( System::IntPtr% class_ptr, array<int>^ devices_ids, int
encoder_id)
{
DVP1412DLL objectPointer;
int retval = Impl::Initialize(&objectPointer, devices_ids, encoder_id);
class_ptr = gcnew System::IntPtr(objectPointer);
return retval;
}
 
Back
Top