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

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

Related

RunmtimeTypeHandle to type

Here is a sample program that wraps up a managed enum for unmanaged use. I want to be able to get wrapped enums back from unmanaged code and convert them back to managed enums with as little overhead as possible. My current scheme requires creating a GCHandle to the Type object. I was thinking maybe there is a way to use the RuntimeTypeHandle value instead to remove this overhead. Is that possible? Or any other efficiency suggestions anyone has.
public value class Stuff {
public:
enum class Modes { A, B };
};
struct enum_wrap {
intptr_t type;
int value;
};
void g(enum_wrap e);
enum_wrap h();
void f(Stuff::Modes mode)
{
enum_wrap wrap;
wrap.value = (int)mode;
wrap.type = static_cast<intptr_t>(GCHandle::ToIntPtr(GCHandle::Alloc(Stuff::Modes::typeid)));
g(wrap);
// ...
enum_wrap ret = h();
Type^ ret_type = safe_cast<Type^>(GCHandle::FromIntPtr(static_cast<IntPtr>(ret.type)).Target);
Enum^ e_obj = (Enum^)Enum::ToObject(ret_type, ret.value);
Console::Write(e_obj->GetType());
Console::Write(": ");
Console::WriteLine(e_obj);
}
#pragma unmanaged
enum_wrap g_enum;
void g(enum_wrap e) {
g_enum = e;
}
enum_wrap h() {
return g_enum;
}
I know this leaks because of never freeing GCHandles. I already have code that can deal with that if this is the best approach I can find. But I'm hoping to not need the Type objects at all.
Edit 1: Put Modes enum in a class so it works right.

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.

Type casting in C++\CLI project

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);
}

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);
}

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.