Mapping DataType from C (Unmanaged) to C# (Managed) - marshalling

I need to get Data from a C DLL to a C# Application. Here is the error :
Cannot marshal field 'Counters' of type 'KnittingWago.Common.WAGO_DATA_TO_USER_T': Invalid managed/unmanaged type combination (Array fields must be paired with ByValArray or SafeArray).
Here is the DLL .h
#define WAGO_NB_COUNTERS_C 80
#define WAGO_NB_ENCODERS_C 10
struct WAGO_DATA_TO_USER_T
{
unsigned int Counters[WAGO_NB_COUNTERS_C];
int Encoders[WAGO_NB_ENCODERS_C];
unsigned int Weight;
bool CalibrationValid;
bool LastCalibrationFailed;
};
Here is the C# Struct declaration :
const int WAGO_NB_COUNTERS_C = 80;
const int WAGO_NB_ENCODERS_C = 10;
struct WAGO_DATA_TO_USER_T
{
[MarshalAs(UnmanagedType.U4, SizeConst = GlobalConstant.WAGO_NB_COUNTERS_C)]
UInt32[] Counters;
[MarshalAs(UnmanagedType.I4, SizeConst = GlobalConstant.WAGO_NB_ENCODERS_C)]
Int32[] Encoders;
UInt32 Weight;
Boolean CalibrationValid;
Boolean LastCalibrationFailed;
};
How do I declare the C# struct to get the rigth data without error ?
Thanks
EDIT :
I've rewrite the Struct as :
internal struct WAGO_DATA_TO_USER_T
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = GlobalConstant.WAGO_NB_COUNTERS_C)]
UInt32[] Counters;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = GlobalConstant.WAGO_NB_ENCODERS_C)]
Int32[] Encoders;
UInt32 Weight;
Boolean CalibrationValid;
Boolean LastCalibrationFailed;
};
No more Error, but the Values are not as expected.
Should be like :
Counter[0] = ~ 5000
Counter[1] = ~ 30000
Counter[2-79] = 0
Really are :
Counter[0] = 40
Counter[1] = 1080228
Counter[2] = 82964616
Counter[3-79] = All have a non-zero value

The Marshalling was correct but the Device I was calling was not initialized by the external unmanaged dll. So I was reading uninitialisez memory that lead to that unvalid data

Related

'cli::interior_ptr<const wchar_t> PtrToStringChars(__const_String_handle)': cannot convert argument 1 from 'SystemString *' to '__const_String_handle'

I am working on an old project, and no developer is available for that project. I have added cstringt.inl file, as it was required and was not present in project folder. Now when I try to build it, I am getting an error, as described in the title and attached image.
In Cstringt.h file below is the constructor:
template<class SystemString>
CStringT( SystemString * pString ) :
CThisSimpleString( StringTraits::GetDefaultManager() )
{
const wchar_t __pin* psz = PtrToStringChars( pString );
*this = psz;
}
And here pString is of type SystemString *. Now, when I use it in PtrToStringChars( pString), it requires a different type.
#ifdef __cplusplus_cli
typedef cli::interior_ptr<const System::Char> __const_Char_ptr;
typedef cli::interior_ptr<const System::Byte> __const_Byte_ptr;
typedef cli::interior_ptr<System::Byte> _Byte_ptr;
typedef const System::String^ __const_String_handle;
#define _NULLPTR nullptr
#else
typedef const System::Char* __const_Char_ptr;
typedef const System::Byte* __const_Byte_ptr;
typedef System::Byte* _Byte_ptr;
typedef const System::String* __const_String_handle;
#define _NULLPTR 0
#endif
inline __const_Char_ptr PtrToStringChars(__const_String_handle s) {
_Byte_ptr bp = const_cast<_Byte_ptr>(reinterpret_cast<__const_Byte_ptr>(s));
if( bp != _NULLPTR ) {
bp += System::Runtime::CompilerServices::RuntimeHelpers::OffsetToStringData;
}
return reinterpret_cast<__const_Char_ptr>(bp);
}
I am not able to understand the type: typedef const System::String^ __const_String_handle;
How to convert the type from SystemString * to typedef const System::String^ __const_String_handle;?

Arduino - passing values by reference from lamda to singleton

Hello i am bigginer in programing and i have specific problem.
I have been learning a new ways to write a code in small Arduino project.
that project have multiple objects like distance measuring Senzor, led diods , temperature senzor, etc. And all this objects have its own menu where you can, for example, start a calibration or just get values.
What i need is singleton class that has a function enter_esc() that need a int (*funct)() parameter basically function pointer.
That enter_esc(int (*funct)()) function just looping function until you press escape pin which is defined.
function Calibration() have inside some private: object data types like value or cali_value.
so i tried to insert function Calibration() right into enter_esc(Calibration) but it won't compile becouse i didnt pass that vlaues by reference or copy.
but what i found is lambda.
i made a lamda similar to a Calibration() function and i passed values by reference &{//domething;}
but i had to use enter_esc(std::function<int()>& funct) whitch is only int C++ standard library and not in Arduino C/C++ so my qestion is:
[is there some way how to pass values by reference by using lambda to a singleton class in Arduino ?]
(i konw it can be done differently but like i said i want to learn some new ways to program, also if you have some different way to make it i will by very happy to see it)
10Q for your time :)
//Class.h
#pragma once
class events {
private:
static events e_instance;
int p_menu, p_enter, p_esc, p_up, p_down;
int menuValue;
events();
public:
events(const events&) = delete;
static events& Get();
int ArrowUpDown(int maxVal);
int ArrowUpDown(int p_up, int p_down, int maxVal);
int enter_esc(const std::function<int()>& funct);
};
events events::e_instance;
class deviceBase : public Printables
{
public:
const char* a_pin;
int d_pin;
String type;
String deviceName;
bool inUse;
int actualCount;
public:
String getType() override;
int getActualCount() override;
String getName() override;
String getInUse() override;
};
class senzor : public deviceBase
{
private:
int Value;
int triggValue;
public:
int p_triggValue = 10;
static int allSenzors;
friend events;
senzor();
~senzor();
public:
int getValue();
int Calibration();
void changeTriggVal(int x);
void Reset();
void nullCalibration();
void Menu(int x);
void setName(String deviceName);
void setInUse(bool x);
int getPin();
};
int senzor::allSenzors = 0;
if you have some good advice to my code writing i will be also very glad
//Class.cpp
#include <iostream>
#include <string>
#include <functional>
#define LOG(x) std::cout << x << std::endl;
#define PINMENU 12
#define PINENTER 8
#define PINESC 9
#define PINUP 11
#define PINDOWN 13
using String = std::string;
struct Printables
{
virtual String getType() = 0;
virtual int getActualCount() = 0; ;
virtual String getName() = 0;
virtual String getInUse() = 0;
};
#include "Class.h"
events& events::Get() {
return e_instance;
}
int events::ArrowUpDown(int maxVal) {
if (maxVal) {
menuValue = menuValue < maxVal ? menuValue++ : menuValue;
}
if (maxVal) {
menuValue = menuValue > 0 ? menuValue-- : menuValue;
}
return menuValue;
}
int events::enter_esc(const std::function<int()>&funct) {
if (1) {
while (!p_esc) {
auto f = funct;
}
}
return 1;
}
int events::ArrowUpDown(int p_up, int p_down, int maxVal) { return 666; }
events::events() {};
String deviceBase::getType() { return type; }
int deviceBase::getActualCount() { return actualCount; }
String deviceBase::getName() { return deviceName; }
String deviceBase::getInUse() {
String Status;
Status = inUse == 1 ? "Active" : "Deactive";
return Status;
}
senzor::senzor() : Value(0), triggValue(1) {
a_pin = "xx";
type = "[SENZOR]";
deviceName = "[UNKNOWN]";
inUse = 0;
allSenzors++;
actualCount = allSenzors;
a_pin = 0;
}
senzor::~senzor() {
allSenzors = 0;
}
int senzor::getValue() {
Value = 4;
return Value;
}
int senzor::Calibration() {
triggValue = triggValue < getValue() ? getValue() : triggValue;
p_triggValue = triggValue;
return p_triggValue;
}
void senzor::changeTriggVal(int x) {
p_triggValue = x;
}
void senzor::Reset() {
p_triggValue = triggValue;
}
void senzor::nullCalibration() {
triggValue = 1;
}
void senzor::setName(String deviceName) {
this->deviceName = deviceName;
}
void senzor::setInUse(bool x) {
inUse = x;
}
int senzor::getPin() {
return 4;
}
int printsss() {
return 1;
}
////////////////////////////////this what i was writing about//////////////////////////////
void senzor::Menu(int x) {
events::Get().enter_esc([&]() { triggValue = triggValue < getValue() ? getValue() : triggValue;
p_triggValue = triggValue;
return p_triggValue; });
}
but if i use lambda in arduino with enter_esc(int (*funct)()) i get this kind of error
no matching function for call to 'events::enter_esc(senzor::Menu(int)::<lambda()>)'

Initialize C++/CLI array of structures

I wish to partially initialize an array of structures like in a C++ POD type. The String^ would normally be a char* but managed C++ doesn't allow that.
#include "stdafx.h"
using namespace System;
ref struct Field
{
String^ name;
int fences;
int length;
};
int main(array<System::String ^> ^args)
{
array<Field^>^ farm =
{
{ "eenie", 10 },
{ "meenie", 20 },
{ "miny", 4 }
};
for each (Field^ field in farm)
{
field->length = field->fences * 22;
}
return 0;
}
This results in
1>arrayinit.cpp(18): error C2440: 'initializing' : cannot convert from 'const char [6]' to 'Field ^'
1> Reason: cannot convert from 'const char *' to 'Field ^'
1> No user-defined-conversion operator available, or
1> Cannot convert an unmanaged type to a managed type
So I tried
#include "stdafx.h"
using namespace System;
ref struct Field
{
String^ name;
int fences;
int length;
};
int main(array<System::String ^> ^args)
{
array<Field^>^ farm =
{
{ String("eenie"), 10 },
{ String("meenie"), 20 },
{ String("miny"), 4 }
};
for each (Field^ field in farm)
{
field->length = field->fences * 22;
}
return 0;
}
Now I get
1>arrayinit.cpp(18): error C2440: 'initializing' : cannot convert from 'System::String' to 'Field ^'
1> No user-defined-conversion operator available, or
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>arrayinit.cpp(18): error C2078: too many initializers
Almost every example I've looked at only tells how to initialize an array of strings or integers. I haven't found out a way of initializing an array of structures containing strings.
Is there a simple way of doing this or do I have to create a special constructor and gcnew every element?
I found that I can gcnew every element with a special constructor. Is there a simpler way of doing this similar to a POD initialization?
#include "stdafx.h"
using namespace System;
ref struct Field
{
String^ name;
int fences;
int length;
Field(String^ x, int in_fences)
{
name = x;
fences = in_fences;
}
};
int main(array<System::String ^> ^args)
{
array<Field^>^ farm =
{
gcnew Field("eenie", 10 ),
gcnew Field("meenie", 20 ),
gcnew Field("miny", 4 )
};
for each (Field^ field in farm)
{
field->length = field->fences * 22;
}
return 0;
}
Alternatively, if Field is changed to a value instead of a reference,
#include "stdafx.h"
using namespace System;
value struct Field
{
String^ name;
int fences;
int length;
Field(String^ x, int in_fences)
{
name = x;
fences = in_fences;
}
void Init()
{
length = fences * 22;
}
};
int main(array<System::String ^> ^args)
{
array<Field>^ farm =
{
Field("eenie", 10 ),
Field("meenie", 20 ),
Field("miny", 4 )
};
for each (Field% field in farm)
{
field.Init();
}
return 0;
}
This is slightly better than gcnewing every field.

c++/clr StructureToPtr Stack buffer overflow

as I have asked in this question c++/clr StructureToPtr exit application without any exception or error
and thanks to the comment, I found out the exit code is -1073740791, which means Stack buffer overflow / overrun. So let me post my two structures,
[StructLayout(LayoutKind::Sequential)]
public ref struct ThostFtdcInputOrderField
{
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 11)]
String^ BrokerID;
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 13)]
String^ InvestorID;
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 31)]
String^ InstrumentID;
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 13)]
String^ OrderRef;
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 16)]
String^ UserID;
EnumOrderPriceTypeType OrderPriceType;
EnumDirectionType Direction;
EnumOffsetFlagType CombOffsetFlag;
EnumOffsetFlagType CombHedgeFlag;
double LimitPrice;
int VolumeTotalOriginal;
EnumTimeConditionType TimeCondition;
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 9)]
String^ GTDDate;
EnumVolumeConditionType VolumeCondition;
int MinVolume;
EnumContingentConditionType ContingentCondition;
double StopPrice;
EnumForceCloseReasonType ForceCloseReason;
int IsAutoSuspend;
[MarshalAs(UnmanagedType::ByValTStr, SizeConst = 21)]
String^ BusinessUnit;
int RequestID;
int UserForceClose;
};
The Enums
public enum struct EnumOrderPriceTypeType
{
AnyPrice = (Byte)'1',
LimitPrice = (Byte)'2',
BestPrice = (Byte)'3',
LastPrice = (Byte)'4',
LastPricePlusOneTicks = (Byte)'5',
LastPricePlusTwoTicks = (Byte)'6',
LastPricePlusThreeTicks = (Byte)'7',
AskPrice1 = (Byte)'8',
AskPrice1PlusOneTicks = (Byte)'9',
AskPrice1PlusTwoTicks = (Byte)'A',
AskPrice1PlusThreeTicks = (Byte)'B',
BidPrice1 = (Byte)'C',
BidPrice1PlusOneTicks = (Byte)'D',
BidPrice1PlusTwoTicks = (Byte)'E',
BidPrice1PlusThreeTicks = (Byte)'F'
};
public enum struct EnumDirectionType
{
Buy = (Byte)'0',
Sell = (Byte)'1'
};
public enum struct EnumOffsetFlagType
{
Open = (Byte)'0',
Close = (Byte)'1',
ForceClose = (Byte)'2',
CloseToday = (Byte)'3',
CloseYesterday = (Byte)'4',
ForceOff = (Byte)'5',
LocalForceClose = (Byte)'6'
};
public enum struct EnumTimeConditionType
{
IOC = (Byte)'1',
GFS = (Byte)'2',
GFD = (Byte)'3',
GTD = (Byte)'4',
GTC = (Byte)'5',
GFA = (Byte)'6'
};
public enum struct EnumVolumeConditionType
{
AV = (Byte)'1',
MV = (Byte)'2',
CV = (Byte)'3'
};
public enum struct EnumContingentConditionType
{
Immediately = (Byte)'1',
Touch = (Byte)'2',
TouchProfit = (Byte)'3',
ParkedOrder = (Byte)'4',
LastPriceGreaterThanStopPrice = (Byte)'5',
LastPriceGreaterEqualStopPrice = (Byte)'6',
LastPriceLesserThanStopPrice = (Byte)'7',
LastPriceLesserEqualStopPrice = (Byte)'8',
AskPriceGreaterThanStopPrice = (Byte)'9',
AskPriceGreaterEqualStopPrice = (Byte)'A',
AskPriceLesserThanStopPrice = (Byte)'B',
AskPriceLesserEqualStopPrice = (Byte)'C',
BidPriceGreaterThanStopPrice = (Byte)'D',
BidPriceGreaterEqualStopPrice = (Byte)'E',
BidPriceLesserThanStopPrice = (Byte)'F',
BidPriceLesserEqualStopPrice = (Byte)'H'
};
public enum struct EnumForceCloseReasonType
{
NotForceClose = (Byte)'0',
LackDeposit = (Byte)'1',
ClientOverPositionLimit = (Byte)'2',
MemberOverPositionLimit = (Byte)'3',
NotMultiple = (Byte)'4',
Violation = (Byte)'5',
Other = (Byte)'6',
PersonDeliv = (Byte)'7'
};
The C++ struct
struct CThostFtdcInputOrderField
{
TThostFtdcBrokerIDType BrokerID;
TThostFtdcInvestorIDType InvestorID;
TThostFtdcInstrumentIDType InstrumentID;
TThostFtdcOrderRefType OrderRef;
TThostFtdcUserIDType UserID;
TThostFtdcOrderPriceTypeType OrderPriceType;
TThostFtdcDirectionType Direction;
TThostFtdcCombOffsetFlagType CombOffsetFlag;
TThostFtdcCombHedgeFlagType CombHedgeFlag;
TThostFtdcPriceType LimitPrice;
TThostFtdcVolumeType VolumeTotalOriginal;
TThostFtdcTimeConditionType TimeCondition;
TThostFtdcDateType GTDDate;
TThostFtdcVolumeConditionType VolumeCondition;
TThostFtdcVolumeType MinVolume;
TThostFtdcContingentConditionType ContingentCondition;
TThostFtdcPriceType StopPrice;
TThostFtdcForceCloseReasonType ForceCloseReason;
TThostFtdcBoolType IsAutoSuspend;
TThostFtdcBusinessUnitType BusinessUnit;
TThostFtdcRequestIDType RequestID;
TThostFtdcBoolType UserForceClose;
};
The types
typedef char TThostFtdcBrokerIDType[11];
typedef char TThostFtdcInvestorIDType[13];
typedef char TThostFtdcInstrumentIDType[31];
typedef char TThostFtdcOrderRefType[13];
typedef char TThostFtdcUserIDType[16];
typedef char TThostFtdcOrderPriceTypeType;
typedef char TThostFtdcDirectionType;
typedef char TThostFtdcCombOffsetFlagType[5];
typedef char TThostFtdcCombHedgeFlagType[5];
typedef double TThostFtdcPriceType;
typedef int TThostFtdcVolumeType;
typedef char TThostFtdcTimeConditionType;
typedef char TThostFtdcDateType[9];
typedef char TThostFtdcVolumeConditionType;
typedef char TThostFtdcContingentConditionType;
typedef char TThostFtdcForceCloseReasonType;.
typedef int TThostFtdcBoolType;
typedef char TThostFtdcBusinessUnitType[21];
typedef int TThostFtdcRequestIDType;
typedef int TThostFtdcBoolType;
As I dont know much of c++, so I have present the whole demo I was given. The demo calls this function to do the transform,
class MNConv
{
public:
/// Native to Managed
static M N2M(N* pNative){
return safe_cast<M>(Marshal::PtrToStructure(IntPtr(pNative), M::typeid));
};
// Managed to Native
static void M2N(M managed, N* pNative){
Marshal::StructureToPtr(managed, IntPtr(pNative), true);
};
};
MNConv<ThostFtdcInputOrderField^, CThostFtdcInputOrderField>::M2N(pInputOrder, &native);
The problem is this exit with exit code is -1073740791, with no exception. I am not sure what went wrong, as the same demo actually convert a few other structure with success.
When you use UnmanagedType::ByValTStr you MUST also specify the encoding.
Because your C++ typedef lines all use char, not wchar_t, you need
[StructLayout(LayoutKind::Sequential, CharSet = CharSet::Ansi)]
If the marshaller incorrectly uses Unicode, it will write twice as much data for each string as it should, overflowing the buffer and corrupting nearby memory.

Marshaling a reference to a char** array

I am having trouble marshaling an array of char* by reference. The data is filled in correctly on the C++ unmanaged side. But when its returned by referernce to the managed side, I end up with a single pointer to the first array element.
//The function in C++
extern "C" DATAACCESSLAYERDLL_API void __stdcall DB_SchemaField_GetKeyValues(Schema::TSchemaFieldHandle hField, const char** &keys, const char ** &values)
{
Schema::CSchemaField *pField = CDataObjectFactory::GetObjectTpl<Schema::CSchemaField>(hField);
if (!pField) return;
Schema::TSchemaKeyValuePair::iterator itor = pField->GetKeyValues().begin();
int index = 0;
for (itor; itor != pField->GetKeyValues().end(); ++itor)
{
keys[index] = (*itor).first.c_str();
values[index] = (*itor).second.c_str();
index++;
}
return;
}
The pInvoke declaration
[System.Security.SuppressUnmanagedCodeSecurity()]
[DllImport("DataCore.dll")]
static private extern void DB_SchemaField_GetKeyValues(Int64 pField,
[In, Out] ref IntPtr[] keys, [In, Out] ref IntPtr[] values);
And finally.... the code which marshals
int keyValueCount = DB_SchemaField_GetKeyValuesCount(GetHandle());
if (keyValueCount > 0)
{
IntPtr[] KeysPtr = new IntPtr[keyValueCount];
IntPtr[] ValuesPtr = new IntPtr[keyValueCount];
DB_SchemaField_GetKeyValues(GetHandle(), ref KeysPtr, ref ValuesPtr);
for (int i = 0; i < keyValueCount; i++)
{
string key = Marshal.PtrToStringAnsi(KeysPtr[i]);
string value = Marshal.PtrToStringAnsi(ValuesPtr[i]);
if (!String.IsNullOrEmpty(key))
{
KeyValues.Add(key, value);
}
}
}
It is a mistake to pass the two const char* arrays by reference. That's one level of indirection too far for the marshaller. You need the following:
C++
extern "C" DATAACCESSLAYERDLL_API void __stdcall DB_SchemaField_GetKeyValues(
Schema::TSchemaFieldHandle hField, const char** keys, const char ** values)
C#
[System.Security.SuppressUnmanagedCodeSecurity()]
[DllImport("DataCore.dll")]
static private extern void DB_SchemaField_GetKeyValues(Int64 pField,
[Out] IntPtr[] keys, [Out] IntPtr[] values);
You'd better make sure you use the pointers that are returned immediately, because the C string returned by c_str() is only valid until the next modification of the std::string object.