Marshaling a reference to a char** array - dll

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.

Related

Infinite printing on read command of character device (through cat command) [duplicate]

I am working on simple character device driver. I have implemented read and write functions in the module, the problem is when I try to read the device file using cat /dev/devicefile it is going into infinite loop i.e. reading the same data repeatedly. Can someone suggest me any solution to this problem? Below is my driver code.
#include<linux/module.h>
#include<linux/fs.h>
#include<linux/string.h>
#include<asm/uaccess.h>
#include<linux/init.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("character device driver");
MODULE_AUTHOR("Srinivas");
static char msg[100]={0};
static int t;
static int dev_open(struct inode *, struct file *);
static int dev_rls(struct inode *, struct file *);
static ssize_t dev_read(struct file *, char *,size_t, loff_t *);
static ssize_t dev_write(struct file *, const char *, size_t,loff_t *);
static struct file_operations fops =
{
.read = dev_read,
.open = dev_open,
.write = dev_write,
.release = dev_rls,
};
static int himodule( void )
{
t = 0;
t = register_chrdev(0, "chardevdriver", &fops);
if (t < 0)
printk(KERN_ALERT"device registration failed\n");
else
printk(KERN_ALERT"device registered successfully\n");
printk(KERN_ALERT"major number is %d", t);
return 0;
}
static void byemodule(void)
{
unregister_chrdev(t, "chardevdriver");
printk(KERN_ALERT"successfully unregistered\n");
}
static int dev_open(struct inode *inod, struct file *fil)
{
printk(KERN_ALERT"inside the dev open");
return 0;
}
static ssize_t dev_read(struct file *filp, char *buff, size_t len, loff_t *off)
{
short count = 0;
while (msg[count] != 0) {
put_user(msg[count], buff++);
count++;
}
return count;
}
static ssize_t dev_write(struct file *filp, const char *buff, size_t len, loff_t *off)
{
short count = 0;
printk(KERN_ALERT"inside write\n");
memset(msg,0,100);
printk(KERN_ALERT" size of len is %zd",len);
while (len > 0) {
msg[count] = buff[count];
len--;
count++;
}
return count;
}
static int dev_rls(struct inode *inod,struct file *fil)
{
printk(KERN_ALERT"device closed\n");
return 0;
}
module_init(himodule);
module_exit(byemodule);
.read function should also correctly process its len and off arguments. The simplest way to implement reading from memory-buffered file is to use simple_read_from_buffer helper:
static ssize_t dev_read(struct file *filp, char *buff, size_t len, loff_t *off)
{
return simple_read_from_buffer(buff, len, off, msg, 100);
}
You can inspect code of that helper (defined in fs/libfs.c) for educational purposes.
BTW, for your .write method you could use simple_write_to_buffer helper.
You are not respecting the buffer size passed into the dev_read function, so you may be invoking undefined behaviour in cat. Try this:
static ssize_t dev_read( struct file *filp, char *buff, size_t len, loff_t *off )
{
size_t count = 0;
printk( KERN_ALERT"inside read %d\n", *off );
while( msg[count] != 0 && count < len )
{
put_user( msg[count], buff++ );
count++;
}
return count;
}
This problem can be solved by correctly setting *off (fourth parameter of my_read()).
You need to return count for the first time and zero from second time onwards.
if(*off == 0) {
while (msg[count] != 0) {
put_user(msg[count], buff++);
count++;
(*off)++;
}
return count;
}
else
return 0;

IJW C# to MC++ to C++ transfer array of strings to char**

Does anyone know how to pin the following string array parameter:
function signature int TestMethod(int argc, array<String^>^ data)
{
pin_ptr<char> p1 = &data[0];
char** p2 = &p1[0];
// char** argv = (char**)calloc(argc+1, sizeof(char*));
}
I want to pin an array<String^>^ object and convert it to the following:
char** argv = (char**)calloc(argc+1, sizeof(char*));
so I can pass it to a native c++ function
any ideas?
What about the following?
UPDATE: just curious? what about the following?
char** argv = (char**)calloc(argc+1, sizeof(char*));
for (int i = 0; i < data->Length; i++)
{
argv[i] = (char*)Marshal::StringToHGlobalAnsi(data[i]).ToPointer();
}
// Use argv as needed here...
// Cleanup
for (int i = 0; i < data->Length; i++)
{
Marshal::FreeHGlobal((IntPtr)argv[i]);
}
You can't directly pin a managed String^ into a char*. .NET Strings are actually 2 bytes per character, so you need to marshal the data manually. This will likely require building up the character arrays, copying data into them, then cleaning up after you're done.
In this case, you likely need to copy the data into your char**, use it, then clean up after yourself. This can be done via something like:
// Requires #include <msclr\marshal.h>
marshal_context context;
char** argv = new char*[data->Length];
for (int i=0;i<Length;++i)
{
const char* tmp = context.marshal_as<const char*>(clrString);
int length = strlen(tmp);
argv[i] = new char[length+1]();
strncpy(argv[i],tmp,length);
}
// Use argv as needed here...
// Cleanup
for (int i=0;i<Length;++i)
delete[] argv[i];
delete[] argv;

C++/CLI String^ to char* 'error: cannot obtain value'

So, basically I have a c++ code which I wrap into CLI.
Wrapper:
public ref class Managed
{
Native* native;
public:
Managed();
~Managed();
bool FooManaged(String^ param1, String^ param2);
};
bool Managed::FooManaged(String^ param1, String^ param2)
{
return native->Foo(StringToChar(param1), StringToChar(param2));
}
char* StringToChar(String^ str)
{
char *ch;
pin_ptr<const wchar_t> wch = PtrToStringChars(str);
size_t convertedChars = 0;
size_t sizeInBytes = ((str->Length + 1) * 2);
ch = (char *)malloc(sizeInBytes);
wcstombs_s(&convertedChars, ch, sizeInBytes, wch, sizeInBytes);
return ch;
}
Native code:
Native::Foo(char* param1, char* param2)
{
// param1 error: cannot obtain value char*
}
Everything's OK on managed side, but on unmanaged I get this error. Any suggestions?
EDIT
Guys, this sample works just fine (I've put up a brief snippet). And I do know several other ways to perform the same thing. BUT! Every method I use doesn't work for letters:
String^ "1234567890" ---> char* 0x0000000 "1234567890"
String^ "name" ---> error
String^ "192.168. ..." ---> char* 0x0000000 "192.168. ..."
Simple way to do this is to use library functions:-
using System::Runtime::InteropServices::Marshal;
char *charString = (char*)Marshal::StringToHGlobalAnsi(managedString);
There was no issue with conversion itself. The full signature of function was:
Native::Foo(char* param1, short param2, char* param3, char* param4)
{
// param3 error: cannot obtain value char*
}
Here error appeared on 'param3'. Changing params order solved my problem:
Native::Foo(char* param1, char* param3, char* param4, short param2)
{
// everything's cool
}

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

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

Creating a custom CFType

Is it possible to create opaque types not derived from CFTypeRef which can be retained/released with CFRetain/CFRelease? Or how do I derive a new type from a CFType?
I've never done this, but it is possible using private API. In all likelihood it will be dependent on a specific dot release of OS X, since the CF runtime could change from release to release. In any case, CF is open source so I took a look at what CFRuntime does. I was happy to see Apple included an example:
// ========================= EXAMPLE =========================
// Example: EXRange -- a "range" object, which keeps the starting
// location and length of the range. ("EX" as in "EXample").
// ---- API ----
typedef const struct __EXRange * EXRangeRef;
CFTypeID EXRangeGetTypeID(void);
EXRangeRef EXRangeCreate(CFAllocatorRef allocator, uint32_t location, uint32_t length);
uint32_t EXRangeGetLocation(EXRangeRef rangeref);
uint32_t EXRangeGetLength(EXRangeRef rangeref);
// ---- implementation ----
#include <CoreFoundation/CFBase.h>
#include <CoreFoundation/CFString.h>
struct __EXRange {
CFRuntimeBase _base;
uint32_t _location;
uint32_t _length;
};
static Boolean __EXRangeEqual(CFTypeRef cf1, CFTypeRef cf2) {
EXRangeRef rangeref1 = (EXRangeRef)cf1;
EXRangeRef rangeref2 = (EXRangeRef)cf2;
if (rangeref1->_location != rangeref2->_location) return false;
if (rangeref1->_length != rangeref2->_length) return false;
return true;
}
static CFHashCode __EXRangeHash(CFTypeRef cf) {
EXRangeRef rangeref = (EXRangeRef)cf;
return (CFHashCode)(rangeref->_location + rangeref->_length);
}
static CFStringRef __EXRangeCopyFormattingDesc(CFTypeRef cf, CFDictionaryRef formatOpts) {
EXRangeRef rangeref = (EXRangeRef)cf;
return CFStringCreateWithFormat(CFGetAllocator(rangeref), formatOpts,
CFSTR("[%u, %u)"),
rangeref->_location,
rangeref->_location + rangeref->_length);
}
static CFStringRef __EXRangeCopyDebugDesc(CFTypeRef cf) {
EXRangeRef rangeref = (EXRangeRef)cf;
return CFStringCreateWithFormat(CFGetAllocator(rangeref), NULL,
CFSTR("<EXRange %p [%p]>{loc = %u, len = %u}"),
rangeref,
CFGetAllocator(rangeref),
rangeref->_location,
rangeref->_length);
}
static void __EXRangeEXRangeFinalize(CFTypeRef cf) {
EXRangeRef rangeref = (EXRangeRef)cf;
// nothing to finalize
}
static CFTypeID _kEXRangeID = _kCFRuntimeNotATypeID;
static CFRuntimeClass _kEXRangeClass = {0};
/* Something external to this file is assumed to call this
* before the EXRange class is used.
*/
void __EXRangeClassInitialize(void) {
_kEXRangeClass.version = 0;
_kEXRangeClass.className = "EXRange";
_kEXRangeClass.init = NULL;
_kEXRangeClass.copy = NULL;
_kEXRangeClass.finalize = __EXRangeEXRangeFinalize;
_kEXRangeClass.equal = __EXRangeEqual;
_kEXRangeClass.hash = __EXRangeHash;
_kEXRangeClass.copyFormattingDesc = __EXRangeCopyFormattingDesc;
_kEXRangeClass.copyDebugDesc = __EXRangeCopyDebugDesc;
_kEXRangeID = _CFRuntimeRegisterClass((const CFRuntimeClass * const)&_kEXRangeClass);
}
CFTypeID EXRangeGetTypeID(void) {
return _kEXRangeID;
}
EXRangeRef EXRangeCreate(CFAllocatorRef allocator, uint32_t location, uint32_t length) {
struct __EXRange *newrange;
uint32_t extra = sizeof(struct __EXRange) - sizeof(CFRuntimeBase);
newrange = (struct __EXRange *)_CFRuntimeCreateInstance(allocator, _kEXRangeID, extra, NULL);
if (NULL == newrange) {
return NULL;
}
newrange->_location = location;
newrange->_length = length;
return (EXRangeRef)newrange;
}
uint32_t EXRangeGetLocation(EXRangeRef rangeref) {
return rangeref->_location;
}
uint32_t EXRangeGetLength(EXRangeRef rangeref) {
return rangeref->_length;
}
#endif
CoreFoundation itself does not provide any such mechanism, but all Cocoa objects will work with CFRetain and CFRelease. So the only supported answer is: Create a class based on Foundation and CoreFoundation will recognize it as a CFTypeRef.