I have a functiong which look somethink like this:
virtual int64_t Read(uint8_t* buffer, int64_t bufferLength) = 0
which we want to overide (as this is an abstract class) and implament from c# code (using director). using
%include "arrays_csharp.i"
%apply uint8_t INPUT[] {uint8_t* buffer}
I was able to get a c# translation with the signature of
public virtual long Read(byte[] buffer, long bufferLength)
which is what I wanted. but when trying to compile the SWIG code we are getting errors as follows:
error C2440: '=': cannot convert from 'void *' to 'unsigned char *'
note: Conversion from 'void*' to pointer to non-'void' requires an explicit cast
because of this lins in the generated swig cc file:
int64_t c_result = SwigValueInit< int64_t >() ;
long long jresult = 0 ;
unsigned char* jbuffer = 0 ;
long long jbufferLength ;
long long jbufferOffset ;
if (!swig_callbackRead__SWIG_0) {
throw Swig::DirectorPureVirtualException("mip::Stream::Read");
} else {
jbuffer = (void *) buffer; <========= FAIL LINE
jbufferLength = bufferLength;
jresult = (long long) swig_callbackRead__SWIG_0(jbuffer, jbufferLength);
c_result = (int64_t)jresult;
}
How can I tell SWIG not to cast to (void *)?
Looks like the directorin typemap isn't quite right here. I think this should be sufficient to fix it, on the line after your %apply directive:
%typemap(directorin) uint8_t *buffer "$input = $1;"
The above typemap only matches non-const uint8_t* parameters named buffer. We can make that much more generic if we want to, e.g.:
%typemap(directorin) SWIGTYPE * "$input = $1;"
Will match anything that doesn't have a better match, const or non-const alike. I can't figure out why that isn't the default anyway, so there's a risk here that it might break something elsewhere that I've not thought of, so you may prefer to continue being selective in where this gets applied..
You can also do something like:
%typemap(directorin) SWIGTYPE * "$input = ($1_ltype)$1;"
Or add another variant:
%typemap(directorin) const SWIGTYPE * "$input = const_cast<$1_ltype>($1);"
The error message from compiler is quite plain. You should explicitly convert void * to unsigned char *.
Try:
jresult = (long long) swig_callbackRead__SWIG_0(static_cast<unsigned char *>(jbuffer), jbufferLength);
Related
After searching, I heard that UInt32 was the C# equivalent of C++ DWORD.
I tested results by performing the arithmetic
*(DWORD*)(1 + 0x2C) //C++
(UInt32)(1 + 0x2C) //C#
They produce completely different results. Can someone please tell me the correct match for DWORD in C#?
Your example is using the DWORD as a pointer, which is most likely an invalid pointer. I'm assuming you meant DWORD by itself.
DWORD is defined as unsigned long, which ends up being a 32-bit unsigned integer.
uint (System.UInt32) should be a match.
#import <stdio.h>
// I'm on macOS right now, so I'm defining DWORD
// the way that Win32 defines it.
typedef unsigned long DWORD;
int main() {
DWORD d = (DWORD)(1 + 0x2C);
int i = (int)d;
printf("value: %d\n", i);
return 0;
}
Output: 45
public class Program
{
public static void Main()
{
uint d = (uint)(1 + 0x2C);
System.Console.WriteLine("Value: {0}", d);
}
}
Output: 45
DWord definition from microsoft:
typedef unsigned long DWORD, *PDWORD, *LPDWORD;
https://msdn.microsoft.com/en-us/library/cc230318.aspx
Uint32 definition from microsoft
typedef unsigned int UINT32;
https://msdn.microsoft.com/en-us/library/cc230386.aspx
now you can see the difference.... one is unsigned long and the other is unsigned int
Your two snippets do completely different things. In your C++ code, you are, for some strange reason, converting the value (1 + 0x2C) (a strange way to write 45) to a DWORD*, and then dereferencing it, as if that address is actually a valid memory location. With the C#, you are simply converting between integer types.
How is it possible to write Rust code like the C code below? This is my Rust code so far, without the option to marshal it:
pub struct PackChar {
id: u32,
val_str: String,
}
#[no_mangle]
pub extern "C" fn get_packs_char(size: u32) -> Vec<PackChar> {
let mut out_vec = Vec::new();
for i in 0..size {
let int_0 = '0' as u32;
let last_char_val = int_0 + i % (126 - int_0);
let last_char = char::from_u32(last_char_val).unwrap();
let buffer = format!("abcdefgHi{}", last_char);
let pack_char = PackChar {
id: i,
val_str: buffer,
};
out_vec.push(pack_char);
}
out_vec
}
The code above tries to reproduce the following C code which I am able to interoperate with as is.
void GetPacksChar(int size, PackChar** DpArrPnt)
{
int TmpStrSize = 10;
*DpArrPnt = (PackChar*)CoTaskMemAlloc( size * sizeof(PackChar));
PackChar* CurPackPnt = *DpArrPnt;
char dummyString[]= "abcdefgHij";
for (int i = 0; i < size; i++,CurPackPnt++)
{
dummyString[TmpStrSize-1] = '0' + i % (126 - '0');
CurPackPnt->IntVal = i;
CurPackPnt->buffer = strdup(dummyString);
}
}
This C code could be accessed via DLL import in C# like this:
[Dllimport("DllPath", CallingConvention = CallingConvention.Cdecl)]
public static extern void GetPacksChar(uint length, PackChar** ArrayStructs)
PackChar* MyPacksChar;
GetPacksChar(10, &MyPacksChar);
PackChar* CurrentPack = MyPacksChar;
var contLst = new List<PackChar>();
for (uint i = 0; i < ArrL; i++, CurrentPack++)
contlist.Add(new PackChar() {
IntVal = CurrentPack->IntVal, buffer = contLst->buffer
});
Let's break this down into the various requirements that your Rust code needs to meet:
The DLL needs to expose a function with the correct name GetPacksChar. This is because you declare it with the name GetPacksChar from C# and the names must match.
The function needs the correct calling convention, in this case extern "C". This is because you declare the function as CallingConvention = CallingConvention.Cdecl from C#, which matches the extern "C" calling convention in Rust.
The function needs the correct signature, in this case taking the Rust equivalent of a uint and a PackChar** and returning nothing. This matches the function signature fn (u32, *mut *mut PackChar).
The declaration of PackChar needs to match between C# and Rust. I'll go over this below.
The function needs to the replicate the behavior of the original C function. I'll go over this below.
The easiest part will be declaring the function in Rust:
#[no_mangle]
pub extern "C" fn GetPacksChar(length: u32, array_ptr: *mut *mut PackChar) {}
Next we need to address PackChar. Based on how it's used in the C# code, it looks like it should be declared:
#[repr(C)]
pub struct PackChar {
pub IntVal: i32,
pub buffer: *mut u8,
}
Breaking this down, #[repr(C)] tells the Rust compiler to arrange PackChar in memory the same way a C compiler would, which is important since you're telling C# that it's calling into C. IntVal and buffer are both used from C# and the original C version. IntVal is declared as an int in the C version, so we use i32 in the Rust version, and buffer is treated as an array of bytes in C, so we use a *mut u8 in Rust.
Note that the definition of PackChar in C# should match the declaration in C/Rust, so:
public struct PackChar {
public int IntVal;
public char* buffer;
}
Now all that's left is to reproduce the original behavior of the C function in Rust:
#[no_mangle]
pub extern "C" fn GetPacksChar(len: u32, array_ptr: *const *mut PackChar) {
static DUMMY_STR: &'static [u8] = b"abcdefgHij\0";
// Allocate space for an array of `len` `PackChar` objects.
let bytes_to_alloc = len * mem::size_of::<PackChar>();
*array_ptr = CoTaskMemAlloc(bytes_to_alloc) as *mut PackChar;
// Convert the raw array of `PackChar` objects into a Rust slice and
// initialize each element of the array.
let mut array = slice::from_raw_parts(len as usize, *array_ptr);
for (index, pack_char) in array.iter_mut().enumerate() {
pack_char.IntVal = index;
pack_char.buffer = strdup(DUMMY_STR as ptr);
pack_char.buffer[DUMMY_STR.len() - 1] = b'0' + index % (126 - b'0');
}
}
Important points from the above:
We have to manually include the null terminating character (\0) in DUMMY_STR because it's meant to be a C string.
We call CoTaskMemAlloc() and strdup(), which are both C functions. strdup() is in the libc crate, and you can probably find in the ole32-sys crate.
The function is declared as unsafe because we have to do a number of unsafe things, like calling C functions and doing str::from_raw_parts().
Hope that helps!
interoping nim dll from c# i could call and execute the code below
if i will add another function (proc) that Calls GetPacks() and try to echo on each element's buffer i could see the output in the C# console correctly
but i could not transfer the data as it is, i tried everything but i could not accomplish the task
proc GetPacksPtrNim(parSze: int, PackArrINOUT: var DataPackArr){.stdcall,exportc,dynlib.} =
PackArrINOUT.newSeq(parSze)
var dummyStr = "abcdefghij"
for i, curDataPack in PackArrINOUT.mpairs:
dummyStr[9] = char(i + int8'0')
curDataPack = DataPack(buffer:dummyStr, intVal: uint32 i)
type
DataPackArr = seq[DataPack]
DataPack = object
buffer: string
intVal: uint32
when i do same in c/c++ the type i am using is either an IntPtr or char*
that is happy to contain returned buffer member
EXPORT_API void __cdecl c_returnDataPack(unsigned int size, dataPack** DpArr)
{
unsigned int dumln, Index;dataPack* CurDp = {NULL};
char dummy[STRMAX];
*DpArr = (dataPack*)malloc( size * sizeof( dataPack ));
CurDp = *DpArr;
strncpy(dummy, "abcdefgHij", STRMAX);
dumln = sizeof(dummy);
for ( Index = 0; Index < size; Index++,CurDp++)
{
CurDp->IVal = Index;
dummy[dumln-1] = '0' + Index % (126 - '0');
CurDp->Sval = (char*) calloc (dumln,sizeof(dummy));
strcpy(CurDp->Sval, dummy);
}
}
c# signature for c code above
[DllImport(#"cdllI.dll", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
private static extern uint c_returnDataPack(uint x, DataPackg.TestC** tcdparr);
C# Struct
public unsafe static class DataPackg
{
[StructLayout(LayoutKind.Sequential)]
public struct TestC
{
public uint Id;
public IntPtr StrVal;
}
}
finally calling the function like so:
public static unsafe List<DataPackg.TestC> PopulateLstPackC(int ArrL)
{
DataPackg.TestC* PackUArrOut;
List<DataPackg.TestC> RtLstPackU = new List<DataPackg.TestC>(ArrL);
c_returnDataPack((uint)ArrL, &PackUArrOut);
DataPackg.TestC* CurrentPack = PackUArrOut;
for (int i = 0; i < ArrL; i++, CurrentPack++)
{
RtLstPackU.Add(new DataPackg.TestC() { StrVal = CurrentPack->StrVal, Id = CurrentPack->Id });
}
//Console.WriteLine("Res={0}", Marshal.PtrToStringAnsi((IntPtr)RtLstPackU[1].StrVal));//new string(RtLstPackU[0].StrVal));
return RtLstPackU;
}
how could i produce similar c code as above from Nim ?
it doesn't have to be same code, but same effect, that in c# i would be able to read the content of the string. for now, the int is readable but the string is not
Edit:
this is what i tried to make things simple
struct array of int members
Update:
it seem that the problem is to do with my settings of nim in my windows OS.
i will be updating as soon as i discover what exactly is wrong.
The string type in Nim is not equivalent to the C's const char* type. Strings in Nim are represented as pointers, pointing into a heap-allocated chunk of memory, which has the following layout:
NI length; # the length of the stored string
NI capacity; # how much room do we have for growth
NIM_CHAR data[capacity]; # the actual string, zero-terminated
Please beware that these types are architecture specific and they are really an implementation detail of the compiler that can be changed in the future. NI is the architecture-default interger type and NIM_CHAR is usually equivalent to a 8-bit char, since Nim is leaning towards the use of UTF8.
With this in mind, you have several options:
1) You can teach C# about this layout and access the string buffers at their correct location (the above caveats apply). An example implementation of this approach can be found here:
https://gist.github.com/zah/fe8f5956684abee6bec9
2) You can use a different type for the buffer field in your Nim code. Possible candidates are ptr char or the fixed size array[char]. The first one will require you to give up the automatic garbage collection and maintain a little bit of code for manual memory management. The second one will give up a little bit of space efficiency and it will put hard-limits on the size of these buffers.
EDIT:
Using cstring may also look tempting, but it's ultimately dangerous. When you assign a regular string to a cstring, the result will be a normal char * value, pointing to the data buffer of the Nim string described above. Since the Nim garbage collector handles properly interior pointers to allocated values, this will be safe as long as the cstring value is placed in a traced location like the stack. But when you place it inside an object, the cstring won't be traced and nothing prevents the GC from releasing the memory, which may create a dangling pointer in your C# code.
Try to change your struct to:
public unsafe static class DataPackg
{
[StructLayout(LayoutKind.Sequential)]
public struct TestC
{
public uint Id;
[MarshalAs(UnmanagedType.LPStr)]
public String StrVal;
}
}
I am mainly a C++ programmer, but in my spare time I am attempting to come up to speed on C#. I have the following C++ function that I would like to convert-
#define COMPUTE_CRC32(cp,crc) (crc32lookup_table[((unsigned long)crc^(unsigned char)cp)&0xff]^(((unsigned long)crc>>8)&0x00FFFFFF))
unsigned long ComputeCRC32::Update(const void* ptrBytes, long numBytes)
{
const unsigned char* ptr_data = (const unsigned char*) ptrBytes;
while ( --numBytes >= 0 )
{
unsigned char data_byte = *ptr_data++ ;
m_ulCRC = COMPUTE_CRC32( data_byte, m_ulCRC );
}
return m_ulCRC;
}
I know there are many ways to do this but would like to see what the best way was to do it. This is what I have created so far -
public uint Update(object ptrBytes, int numBytes)
{
byte * ptr_data = (byte) ptrBytes;
while (--numBytes >= 0)
{
byte data_byte = *ptr_data++;
m_ulCRC = (GlobalMembersComputeCRC32.crc32lookup_table[((uint)m_ulCRC ^ (byte)data_byte) & 0xff] ^ (((uint)m_ulCRC >> 8) & 0x00FFFFFF));
}
return m_ulCRC;
}
What would be the best way to convert the pointers? Is there a better way to rewrite this in C#?
C# is a language that has pointers, but also has references (and references are not necessarily addresses). An array such as byte[] in C# is the usual way of representing something you might use pointers for in C++.
To use pointers, you use unsafe. If you are thinking in C#, people tend to avoid unsafe as it is generally "unsafe"; the runtime instead enforces checks to avoid things like buffer overruns in arrays. Instead, the psuedo code for Crc32 might be:
public uint Crc32(byte[] data) {
uint result;
for (int i= 0; i < data.Length; i++) {
byte data_byte = data[i];
result = doCrc(...stuff with data_byte...);
}
return result;
}
Note that the for loop uses data.Length as its limit check (ref: Eric Gunnerson: Efficiency of iteration over arrays), as this can be optimised by the JIT against the array length. If you use a separate length parameter it can't be, so this is to be avoided (or combined with length, if the required iteration count might be less than the array length).
These pointers are not doing anything tricky. They're just array iterators.
public uint Update( byte[] ptrBytes, int numBytes )
{
for( int i = 0; i < numBytes; i++ )
{
byte data_byte = ptr_data[i];
m_ulCRC = ...
}
return m_ulCRC;
}
I am creating a new question here as I now know how to ask this question, but I'm still a newb in PInvoke.
I have a C API, with the following structures in it:
typedef union pu
{
struct dpos d;
struct epo e;
struct bpos b;
struct spos c;
} PosT ;
typedef struct dpos
{
int id;
char id2[6];
int num;
char code[10];
char type[3];
} DPosT ;
and the following API function:
int addPos(..., PosT ***posArray,...)
the way I call this in C like this:
int main(int argc, const char *argv[])
{
...
PosT **posArray = NULL;
...
ret_sts = addPos(..., &posArray, ...);
...
}
inside addPos() memory will be allocated to posArray and it will also be populated. allocation is like this using calloc:
int addPos(..., PosT ***posArray, ...)
{
PosT **ptr;
...
*posArray = (PosT **) calloc(size, sizeof(PosT *));
*ptr = (PosT *)calloc(...);
...
(*posArray)[(*cntr)++] = *ptr;
...
/* Population */
...
}
I have another function that will be called to deallocate that memory.
Now I want to do the same in C#,
I have created this in my C# class:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DPositionT
{
public int Id;
[MarshalAs(UnmanagedType.LPStr, SizeConst = Constants.Id2Len)]
public string Id2;
public int Num;
[MarshalAs(UnmanagedType.LPStr, SizeConst = Constants.CodeLen)]
public string Code;
[MarshalAs(UnmanagedType.LPStr, SizeConst = Constants.TypeLen)]
public string type;
}
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit )]
struct PosT
{
[System.Runtime.InteropServices.FieldOffset(0)]
DPosT d;
};
I have only defined d, as I am only going to use this member of the union in my client code.
Now in order to call addPos() how should I create and pass posArray ?
your help is very much appreciated.
One of the really important thing to note is that the third * from the PosT*** is there just to provide possibility to return the allocated pointer to the caller. This means that in fact the type is PosT** and that in C# parameter will have to be declared either ref or out modifier.
The fragment you've provided is incomplete, but tells few things:
*posArray = (PosT **) calloc(size, sizeof(PosT *)); // A
*ptr = (PosT *)calloc(...); // B1
...
(*posArray)[(*cntr)++] = *ptr; // B2
in (A) an array, PosT* is created, then in (B1)+(B2) some cells of that array are initialized to some new arrays of undisclosed size. Please note that with your code snippet, the first 'some' and the second 'some' may be unrelated.
Also, the sizes of those arrays are unknown, except for the top-most "size" that probably comes from parameters.
This means that from C# point of view, the datastructre you want to actually receive is "PosT[][]", so the P/Invoke signature would be like:
[...]
... addPos(..., out PosT[][] posArray, ....)
so, even in C# it would be an 2-dimensional jagged array of returned by parameter, just like in C/C++.
However, unlike C where you can have loose pointers that points to unspecific blocks of data, in C#, every array must have a precisely known length. As the "size" is probably known to you, you can tell the marshaller what is the size of the first dimension.
If the outer "size" is a constant:
[...]
... addPos(..., [MarshalAs(SizeConst=100)]out PosT[][] posArray, ....)
or, if it is passed by parameter:
[...]
... addPos(int size, ...., [MarshalAs(SizeParamIndex=0)]out PosT[][] posArray, ....)
of course assuming that "size" is really the very first parameter.
However, all of this does not specify the size of the inner arrays. What's more, with the code snippet you've provided, those inner arrays may differ in their lengths. Let me play with the code snippet you have presented:
*posArray = (PosT **) calloc(size, sizeof(PosT *)); // A
(*cntr) = 0
for(int idx = 0; idx<size; ++idx)
{
*ptr = (PosT *)calloc(5+40*(*cntr)); // B1
(*posArray)[(*cntr)++] = *ptr; // B2
}
Even worse, your snippet does not even show whether the inner arrays are unique or not, so even this is allowed with your snippet:
*posArray = (PosT **) calloc(size, sizeof(PosT *)); // A
(*cntr) = 0
*ptr = (PosT *)calloc(5); // B1
*ptr2 = (PosT *)calloc(25);
for(int idx = 0; idx<size / 2; ++idx)
{
(*posArray)[(*cntr)++] = *ptr; // B2
(*posArray)[(*cntr)++] = *ptr2;
}
Note that here I allocate only 2 inner arrays, and I set all cells of the outer array to point to one of those two inner arrays - outer array may have 500 cells, but there are only 2 inner ones. That's also completely correct datastructure and it is possible with your snippet.
In either of those two cases, there is no pretty way of telling the .Net Marshaller about the layout of such data structure. You'd have to obtain the outer array as an array of pointers:
[...]
... addPos(int size, ...., [MarshalAs(SizeParamIndex=0)]out IntPtr[] posArray, ....)
which you can imagine as casting your (PosT**) into (void*) and later then, somewhere in your program, you'd have to manually unpack those various IntPtrs into PosT[] of proper lengths. Of course, you'd have to actually somehow guess what is the correct length.
Here's how to read an array of structs from IntPtr: Getting Array of struct from IntPtr
EDIT:
ah, and I completely forgot that of course, on C# side, you can just obtain the parameter as a raw pointer, so instead of out PosT[][] posarray you can just PosT*** posarray - this one however will require you to add the 'unsafe' modifier to the signature of the "addPos" function on the C# side. Here's some primer on unsafe modifier:
http://www.codeproject.com/Articles/1453/Getting-unsafe-with-pointers-in-C
[Update]
OK, I have made some progress here,
I couldn't get it to work, when I pass it as out IntPtr[] as when returning from unmanaged code it throws exception. But I came across this link and so I tried to pass it using out IntPtr and that seems to work. at least now I get the pointer back. I now need to start working on Marshalling it all to get what I need out of it.