Type casting in C++\CLI project - c++-cli

I have project which I am compiling with /clr. I have a class like below..
ref class A
{
public:
void CheckValue(void * test);
typedef ref struct val
{
std::string *x;
}val_t;
};
in my implementation I ahve to use something like below..
void A::CheckValue(void *test)
{
a::val_t^ allVal = (a::val_t^)test;
}
in my main I have used like..
int main()
{
A^ obj = gcnew A();
a::val_t valObj = new std::string("Test");
obj->CheckValue((void*)valObj);
}
I am getting type cast error and two places -
obj->CheckValue((void*)valObj);
and at
obj->CheckValue((void*)valObj);
error C2440: 'type cast' : cannot convert from 'void*' to 'A::val_t ^'
This snippet is just to show behavior at my end and I ahve to use it this way only. Earlier I was running it using non /clr so it compiled fine.
Now question I have how can I make this type casting work in C++/CLI type project?

Replace void * with Object^. You can also write a generic version of CheckValue but then there is not much point of having a weak-typed parameter when you have the type in the generic parameter.
A reference handle represents an object on the managed heap. Unlike a native pointer, CLR could move the object around during a function call, so the behavior of a pointer and a reference handle is different, and a type cast would fail. You can also pin the object being referenced using pin_ptr if you really need a void* so CLR would not be moving the object during the function call.

Here is how I would get around the limitation you are seeing, just remove the struct from the managed object, since it contains native pointer types.
struct val_t
{
char* x;
};
ref class A
{
public:
void CheckValue(void* test);
};
void A::CheckValue(void* test)
{
val_t* allVal = (val_t*)test;
}
int main()
{
A^ obj = gcnew A();
val_t valObj;
valObj.x = "Test";
obj->CheckValue((void*)&valObj);
}
Now, if you absolutely need the struct to be managed, here is how to do it:
ref class A
{
public:
void CheckValue(void * test);
value struct val_t
{
char* x;
};
};
void A::CheckValue(void *test)
{
a::val_t* allVal = (a::val_t*)test;
}
int main()
{
A^ obj = gcnew A();
a::val_t valObj;
valObj.x = "Test";
pin_ptr<a::val_t> valPin = &valObj;
obj->CheckValue((void*)valPin);
}

Related

C++/cli regarding setting a unmanaged pointer in a managed property

Say I have a c++ struct:
typedef struct CStruct {
char * data;
} CStruct;
Say I have a pointer to the C++ struct in a managed class in C++/cli, which has a property that references the struct's member:
public ref class ManagedClass
{
public:
CStruct * theStruct;
ManagedClass(CStruct * strucPtr){
this->theStruct = strucPtr;
}
property String ^ data {
String ^ get() {
return gcnew String(theStruct->data);
}
void set(String ^ val)
{
IntPtr marshalToCharStar = Marshal::StringToHGlobalAnsi(val);
char * strPtr = static_cast<char *>(marshalToCharStar.ToPointer());
delete theStruct->data;
theStruct->data = strPtr;
Marshal::FreeHGlobal(marshalToCharStar);
}
}
}
First of all, is the marshaling done correctly? Should not have problems with the garbage collector here right? Secondly, is getting/setting an unmanaged object using properties a good idea? Any downfalls to that?
Right now I have an object similar to the one described, but member values are disappearing/becoming garbage, and I am trying to eliminate possible causes.
Appreciate the help

How to marshal this nested, and Pointer Used C structure in C#

typedef struct pt_input_bir
{
PT_BYTE byForm;
union {
PT_BIR *pBIR; ///< Used when byForm = PT_FULLBIR_INPUT */
PT_LONG lSlotNr; ///< Used when byForm = PT_SLOT_INPUT */
PT_BYTE abyReserved[20]; /** For future use */
} InputBIR;
} PT_INPUT_BIR
typedef struct pt_bir {
PT_BIR_HEADER Header;
PT_BYTE Data[1];
} PT_BIR
typedef struct pt_bir_header {
PT_DWORD Length;
PT_BYTE HeaderVersion;
PT_BYTE Type;
PT_WORD FormatOwner;
PT_WORD FormatID;
PT_CHAR Quality;
PT_BYTE Purpose;
PT_DWORD FactorsMask;
} PT_BIR_HEADER
and the C function is
PT_STATUS StoreFinger (
IN PT_CONNECTION hConnection,
IN PT_INPUT_BIR *pTemplate,
OUT PT_LONG *plSlotNr
)
Now I need to do the wrapper for the above C function in C#.
How should I marshal the PT_INPUT_BIR* structure and how should I unmarshal it after return of this function?
Please help me to solve this.
/********************** FOR MORE DETAIL ABOUT THIS QUESTION**************************/
C struct and function are defined in above. pls refer there.
C# Struct :
For C# struct declaration i have maintatined two struct for the one C struct. bcz one is for setting the values and another one id for passing to c function.
C# app struct:
[StructLayout(LayoutKind.Sequential)]//for app
public struct FPINPUTBIR
{
public byte byForm;
public InputBIRType InputBIR;
}
[StructLayout(LayoutKind.Sequential)] // here when i use explicit it throws exception so i removed it.
public struct InputBIRType
{
// [FieldOffset(0)]
public FPBIR pBIR;
//[FieldOffset(0)]
public int lSlotNr;
//[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public byte[] abyReserved;
}
C# wrapper struct:
[StructLayout(LayoutKind.Sequential)]
public struct FP_INPUTBIR
{
public byte byForm;
public IntPtr mIPBIR;
}
[StructLayout(LayoutKind.Explicit, Size = 20, CharSet = CharSet.Ansi)]
public struct Input_BIRType
{
[FieldOffset(0)]
public IntPtr mBIR;
[FieldOffset(0)]
public int lSlotNr;
//[FieldOffset(8)]
//[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
//public byte[] abyReserved;
}
finally i will copy the value from the C# app struct to wrapper struct before the call the C fun()
2a) C# App Side Code is :
//here mAppMemory is already known value
FPINPUTBIR lfipdata = new FPINPUTBIR();
FPDATA lfpdata = new FPDATA();
lfipdata.byForm = (byte)eFPVerifyBy.FULLBIR_INPUT;
lfipdata.InputBIR = new InputBIRType();
lfipdata.InputBIR.abyReserved = new byte[20];
lfipdata.InputBIR.pBIR.Data = new byte[mAppMemory[listBox2.SelectedIndex].Header.Length];
Array.Copy(mAppMemory[listBox2.SelectedIndex].Data, lfipdata.InputBIR.pBIR.Data, mAppMemory[listBox2.SelectedIndex].Header.Length);
lfipdata.InputBIR.pBIR.Header = mAppMemory[listBox2.SelectedIndex].Header;
Verify(ref lfipdata); //calling from C# APP side to C# wrapper
C# wrapper side:
public int Verify(ref FPINPUTBIR apStoredTemplate )
{
// i passed the args (apStoredTemplate ) but throws exception struct mismatch with C struct.
//here i don't know what should i do.
CDLL.StoreFinger(..,ref apStoredTemplate,.. ); //pls refer the C function above
}
Questions:
Do i really need two C# structures for this.
what should i do inside the C# wrapper function. please remeber i have two C# struct with diff members.
Thanks.
You just need a little extension on what you used in the previous question for PT_BIR. There we marshalled that variable length struct as byte[]. You can use the same code to generate the byte array, and I won't revisit that.
Next you need the union. That is:
[StructLayout(LayoutKind.Explicit, Size = 20)]
public struct PT_INPUT_BIR_UNION
{
[FieldOffset(0)]
public IntPtr pBIR;
[FieldOffset(0)]
public int lSlotNr; // I'm guessing what PT_LONG is
}
No need to declare the reserved part of the union. The size takes care of that.
Then PT_INPUT_BIR is
[StructLayout(LayoutKind.Sequential)]
public struct PT_INPUT_BIR
{
Byte byForm;
PT_INPUT_BIR_UNION InputBirUnion;
}
Then you need to use GCHandle to pin the PT_BIR byte array. Let's keep to the same naming as used at that question, and assume that the PT_BIR is held in a byte[] variable named data.
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
PT_INPUT_BIR inputBir;
inputBir.byForm := ...;
inputBir.InputBirUnion.pBIR = handle.AddrOfPinnedObject();
// now call StoreFinger passing ref inputBir
}
finally
{
handle.Free();
}
When you declare StoreFinger the PT_BIR* parameter should be declared as ref PT_BIR.

C++/CLI: Passing C++ class ptr to unmanaged method

I've been given a third party C/C++ library (.dll, .lib, .exp and .h) that I need to use in our C# app.
ThirdPartyLibrary.h contains...
class AClass {
public:
typedef enum {
green = 7,
blue = 16
} Color;
virtual int GetData()=0;
virtual int DoWork(Color, char *)=0;
};
void * Func1(int, AClass **aClass);
In my C++/CLI code I have done this...
#include "ThirdPartyLibrary.h"
using namespace System;
using namespace System::Runtime::InteropServices;
namespace Wrapper {
public ref class MyBridgeClass
{
private:
AClass* pAClass;
public:
// C# code will call this method
void AMethod (int x)
{
int y = x+10;
Func1 (y, &(this->pAClass)); // <-- error!
}
}
}
I get a build error that reads...
cannot convert parameter 2 from 'cli::interior_ptr<Type>' to 'AClass **'
with
[
Type=AClass *
]
Cannot convert a managed type to an unmanaged type
Any ideas? Maybe I need #pragma manage/unmanged tags in my C++/CLI?
The reason you're getting that error is because of how managed memory works.
In your managed class, you've got a pointer defined. The address of that pointer is part of the managed object, and can change when the garbage collector runs. That's why you can't just pass &pAClass to the method, the GC can change what that address actually is.
There's a couple things you can do to fix this:
You could create an unmanaged helper class to hold the AClass* member. I'd do this if that pointer needs to stay valid beyond the invocation of this method, or if you have a lot of unmanaged pointers to hold.
struct UnmanagedHolder
{
AClass* pAClass;
};
public ref class MyBridgeClass
{
private:
// must create in constructor, delete in destructor and finalizer.
UnmanagedHolder* unmanaged;
public:
// C# code will call this method
void AMethod (int x)
{
int y = x+10;
Func1 (y, &(this->unmanaged->pAClass));
}
};
If you only need the pointer to be valid within AMethod, and the pointer doesn't need to remain valid after the call to Func1, then you can use a pin_ptr.
void AMethod (int x)
{
int y = x+10;
pin_ptr<AClass*> pin = &(this->pAClass);
Func1 (y, pin);
}

C++/CLI, "constant" reference to a tracking handle

I have spotted something like this in code:
void foo(IList<int>^ const & list ) { ... }
What does this ^ const& mean? I looked in the C++/CLI specification, but found no comments on making constant tracking references, nor the ^& combo.
Is this legal?
This code was probably written by a C++ programmer that used common C++ idiom to write C++/CLI. It is quite wrong, passing a reference to tracking handle is only possible if the handle is stored on the stack. It cannot work if the passed List<> reference is stored in a field of an object on the heap, the garbage collector can move it and make the pointer invalid. The compiler will catch it and generate an error. The ^ is already a reference, no additional reference is needed.
Without the reference, the const keyword doesn't make a lot of sense anymore either. Not that it ever did before, the CLR cannot enforce it. Not that this mattered much here, this code could not be called from any other .NET language. They won't generate a pointer to the tracking handle.
Just fix it, there's little point in keeping bad code like this:
void foo(IList<int>^ list ) { ... }
Example of code that shows that the reference cannot work:
using namespace System;
using namespace System::Collections::Generic;
ref class Test {
public:
IList<int>^ lst;
void foo(IList<int> const &list) {}
void wontcompile() {
foo(lst); // C3699
IList<int>^ okay;
foo(okay);
}
};
It's a reference which is constant to a tracking handle.
It allows you to pass the handle by reference instead of by value. Presumably the author thinks it's more efficient than copying the handle.
If the author meant to make the handle constant he should have used either of
Method(TestClass const ^ const & parameter)
Method(TestClass const^ parameter)
Or alternatively
Method(TestClass const^& parameter) - but the caller must const up the handle first
with
TestClass const^ constHandle = nonConstHandle
An example of each:
// test.cpp : Defines the entry point for the console application.
#include "stdafx.h"
ref class TestClass
{
public:
void setA(int value)
{
a = value;
}
TestClass() :
a(10)
{
}
private:
int a;
};
class TakesHandle
{
public:
void methodX1(TestClass const ^ const & parameter)
{
// Un-commenting below causes compiler error
// parameter->setA(11);
}
void methodX2(TestClass const^ parameter)
{
// Un-commenting below causes compiler error
// parameter->setA(11);
}
void methodX3(TestClass const^& parameter)
{
// Un-commenting below causes compiler error
// parameter->setA(11);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
TakesHandle takes;
TestClass ^ test1 = gcnew TestClass();
// compiles
takes.methodX1(test1);
// compiles
takes.methodX2(test1);
TestClass const ^ constHandle = test1;
takes.methodX3(constHandle);
return 0;
}

converting System::String^ to std::string inside ref class member

I'm trying to write a wrapper for a very simple std::pair<std::string, float> in C++/CLI but I get the error message: no instance of constructor "std::pair<_Ty1, _Ty2>::pair [with _Ty1=std::string, _Ty2=float]" matches the argument list, argument types are: (std::string, float)
What am I doing wrong and why doesn't std::string match std::string?
#include <msclr\marshal_cppstd.h>
#include <string>
using namespace System;
using namespace System::Runtime::InteropServices;
typedef std::pair<std::string, float> Parameter;
static std::string StringToNative(String^ str)
{
msclr::interop::marshal_context context;
return context.marshal_as<std::string>(str);;
}
public ref class CLIParameter
{
public:
CLIParameter(System::String^ name, float value) : _name(name), _value(value) {};
Parameter toNativeParameter()
{
return Parameter(StringToNative(_name), _value);
}
private:
System::String^ _name;
float _value;
};
int main()
{
CLIParameter^ par = gcnew CLIParameter("test", 1);
}
Your method toNativeParameter() is incorrect. It should be defined as follows:
Parameter toNativeParameter()
{
// copy the floating point value from the managed heap to the local stack
float value = _value;
// create the pair
return std::make_pair(StringToNative(_name), value);
}
Notice that you should use std::make_pair to create the actual pair. In addition, a key step to make this work is copying the floating point value from the managed heap into the local stack. The reason is that native functions such as std::make_pair cannot create native references to an object from the managed (garbage collected) heap i.e. a member of a managed class.