Case 1:
int i;
int* pi = &i;
Case 2:
int i;
IntPtr pi = &i;
Are both cases identical?
My purpose is that:-
I am copying a value to string variable.
Converting string to bytes array
using Marshal.Alloc(sizeofBytesArray) to get IntPtr ptrToArray
marshal.copy(array,0,ptrToArray,sizeofBytesArray)
Sending this ptrToArray to a vb6 application by using a structure and passing structure via SendMessage win32 api.
On VB6 app:-
I am picking up the value from the structure that gives me the address of the array.
using CopyMemory to copy the bytesarray data into a string..
More Code:
string aString = text;
byte[] theBytes = System.Text.Encoding.Default.GetBytes(aString);
// Marshal the managed struct to a native block of memory.
int myStructSize = theBytes.Length;
IntPtr pMyStruct = Marshal.AllocHGlobal(myStructSize); //int* or IntPtr is good?
try
{
Marshal.Copy(theBytes, 0, pMyStruct, myStructSize);
...............
According to the msdn page:-
The IntPtr type can be used by languages that support pointers, and as a common means of referring to data between languages that do and do not support pointers.
IntPtr objects can also be used to hold handles. For example, instances of IntPtr are used extensively in the System.IO.FileStream class to hold file handles.
Are both cases indentical?
No, they are not identical,
The difference is in the underlying implementation of IntPtr
forex
int i = 10 ;
int *pi = i ;
int c = *pi ; // would work
but to do the same thing , you have to cast IntPtr to int*
int i =10 ;
IntPtr pi = &i ;
int *tempPtr = (int*)pi ;
int c = *tempPtr ;
The internal representation of IntPtr is like void* but it is exposed like an integer. You can use it whenever you need to store an unmanaged pointer and don't want to use unsafe code.
It has two limitations:
It cannot be dereferenced directly (you have to cast it as a true pointer).
It doesn't know the type of the data that it points to. (because of void*)
Related
I'm attempting to follow the answer at this question
My struct looks like this in C
typedef struct drive_info_t {
unsigned char drive_alias[32];
} drive_info_t;
My function looks like this in C
unsigned int get_drive_info_list(drive_info_t **list, unsigned int *item_count) {
//fill list in native C
//print out in native C
printf("list.alias - %s\r\n",list[i]->drive_alias);
}
My C# struct looks like this
[StructLayout(LayoutKind.Sequential)]
public struct drive_info_t
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public byte[] drive_alias;
}
My C# function declaration looks like this
[DllImport("mydll.dll", EntryPoint = "get_drive_info_list", CallingConvention = CallingConvention.Cdecl)]
public static extern uint GetDriveInfoList(out System.IntPtr ptr_list_info, out System.IntPtr ptr_count);
I'm calling C# function like this
IntPtr ptr_list_info = IntPtr.Zero;
IntPtr ptr_cnt = IntPtr.Zero;
ret = api.GetDriveInfoList(out ptr_list_info, out ptr_cnt);
I'm marshaling the returned pointers like this
nAlloc = ptr_cnt.ToInt32();
int szStruct = Marshal.SizeOf(typeof(api.drive_info_t));
api.drive_info_t[] localStructs = new api.drive_info_t[nAlloc];
for (int i = 0; i < nAlloc; i++)
{
localStructs[i] = (api.drive_info_t)Marshal.PtrToStructure(ptr_list_info, typeof(api.drive_info_t));
ptr_list_info = new IntPtr(ptr_list_info.ToInt32() + (szStruct));
}
Printing the struct alias like this
for (uint i = 0; i < localStructs.Length; i++)
{
Console.WriteLine("list.alias - {0}", System.Text.Encoding.Default.GetString(localStructs[i].drive_alias));
}
Thanks for staying with me..
This is what my output looks like on a console application in C#. You can see the native C dll printing to the console it's values, but my marshaling is messing up somewhere:
======================== C values ============================
list.alias - drv1
list.alias - drv2
list.alias - drv3
list.alias - drv4
======================== C# values ============================
list.alias - drv1
list.alias - o£Q95drv2
list.alias - o£Q95drv3
list.alias - o£Q95drv4
I have no clue where this garbage text and offset is coming from.
I'm responsible for the .Net side, other team members can change the native C as needed if required, but native C changes need to be cross-platform OSX/Windows/Linux.
Thanks in advance.
This is a perfect example of why the types of the arguments do not define the interface. Consider
drive_info_t **list
This could be a pointer to an array of structs. In which case presumably the array is allocated by the callee. Which is why a pointer to array is used as opposed to an array.
Or it could be an array of pointers to struct. Here the array would be allocated by caller and the structs could be allocated by either callee or caller. No way for us to tell.
We can discern that your interface accepts an array of pointers to struct. Look at this code:
list[i]->drive_alias
Quite clearly list is an array of pointers to struct.
This tells you that your code is wrong. You have followed the wrong template because you mis-identified the semantics. Your next step is to return to the documentation for the interface and any example calling code to learn precisely what the semantics are. Who allocates and deallocated what.
Almost certainly you will use IntPtr[] to marshal the array but beyond that it is impossible for us to say what happens. I would guess that the caller allocates the structs but don't rely on guesswork. Read the documentation and example calling code.
Beyond that the second argument should be ref uint or perhaps out uint depending on the semantics of the function. Again, that's something that we cannot work out.
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;
}
}
With C/C++ DLL SDK fun,like this:
INT CmdGetAllLog( BYTE *bStream, UINT16 *nCount, const UINT8 nblk )
but in project use c#,I do it with:
[DllImport("C:\\PrBioApi.dll", EntryPoint = "CmdGetAllLog")]
private static extern bool CmdGetAllLog(IntPtr bStream, ref UInt16 nCount, byte nblk);
and I use it with:
int nMallocSize = Marshal.SizeOf(new LOG_RECORD()) * stuSystem.wLogCnt + 4096;
byte[] pRecord = new byte[nMallocSize];
IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(nMallocSize));
Marshal.Copy(pRecord, 0, p, pRecord.Length);
bGetSucc = CmdGetAllLog(p, ref nGet, nBlk++);
Marshal.FreeHGlobal(p);
but it did not work.
would anyone can help me ?thanks.
Your code which copies between the managed array, and the unmanaged pointer, is in the wrong place. It would need to be after the call to the unmanaged function.
But you may as well let the p/invoke marshaller do the work for you:
[DllImport(#"C:\PrBioApi.dll")]
private static extern bool CmdGetAllLog(
byte[] bStream,
ref ushort nCount,
byte nblk
);
int nMallocSize = ...;
byte[] pRecord = new byte[nMallocSize];
bool bGetSucc = CmdGetAllLog(pRecord, ref nGet, nBlk++);
Because a byte array is blittable then the marshaller will just pin your array during the call and hand it off to the native code.
I'm assuming that the other two parameters are passed correctly. Since you did not specify any more details of the interface, they could well be wrong. I'd guess that nGet is used to tell the function how big the buffer is, and to return how much was copied to it by the function. I cannot see where you specify nGet in the question. I'm trusting that you got that bit right.
Some other comments:
You may need to specify a calling convention in the DllImport attribute. Is the native code cdecl perhaps?
The return value is INT in the native code but you've mapped it to bool. That probably is fine if the protocol is that non-zero return means success. But if the return value indicates more than that then you'd clearly need to use int. Personally I'd be inclined to use int and stay true to the native.
From safe, managed code in C#, I would like to call a function in a C API that receives an array of pointers (void**).
I have the corresponding managed array of IntPtr objects, but the Marshal methods advertised in the documentation at MSDN do not seem sufficient to provide an IntPtr to an unmanaged block of memory with the correct content.
I had hoped to obtain an IntPtr with 'Marshal.AllocHGlobal' and then assign the correct content using 'Marshal.Copy', but it seems the function has not been overloaded for an array of IntPtr.
Any thoughts on the best way to do this?
Thanks in advance.
The P/Invoke marshaller already does this, you don't have to help. Just declare the function argument as an array:
[DllImport("blah.dll")]
private static extern void SomeFunction(IntPtr[] array);
Just in case: although you don't have to use the unsafe keyword here, there isn't anything safe about it. The C code can easily corrupt the heap when it writes past the end of the block you allocated.
Pass the array as an IntPtr[], IntPtr are by default marshaled as void*. No
need for unsafe.
[DllImport("containingFoo.dll")]
public static extern void foo( IntPtr[] ptr);
...
// some floats
float[] fpa = {7.2F, 2.3F, 3.3F, 4.5F, 6.5F};
// allocate unmanaged for float[] fpa and int (length of array)
IntPtr fptr = Marshal.AllocHGlobal(fpa.Length *
Marshal.SizeOf(typeof(float)));
IntPtr iptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(int)));
// set length of array
Marshal.WriteInt32(iptr, fpa.Length);
// copy the array
Marshal.Copy(fpa, 0, fptr, fpa.Length);
// strore both pointers in IntPtr[]
IntPtr[] pptr = {fptr, iptr};
// call foo passing the IntPtr[] to C
foo(pptr);
//C/C++
// note that stdcall is the default calling convention when using
PInvoke!!!!
void __stdcall foo(void** data)
{
float * fa = (float*)*data; // first element points to float array
int *ip = (int*)data + 1; // pointer to next element in void array
int *pp = (int*)*ip; // get pointer to int
for (int i = 0; i < *pp ; i++)
{
printf("\t: %f\n", *fa++);
}
}
in C#, is there a way to
Get the memory address stored in a
reference type variable?
Get the memory address of a
variable?
EDIT:
int i;
int* pi = &i;
How do you print out the hex value of pi?
For #2, the & operator will work in the same fashion as in C. If the variable is not on the stack, you may need to use a fixed statement to pin it down while you work so the garbage collector does not move it, though.
For #1, reference types are trickier: you'll need to use a GCHandle, and the reference type has to be blittable, i.e. have a defined memory layout and be bitwise copyable.
In order to access the address as a number, you can cast from pointer type to IntPtr (an integer type defined to be the same size as a pointer), and from there to uint or ulong (depending on the pointer size of the underlying machine).
using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
class Blittable
{
int x;
}
class Program
{
public static unsafe void Main()
{
int i;
object o = new Blittable();
int* ptr = &i;
IntPtr addr = (IntPtr)ptr;
Console.WriteLine(addr.ToString("x"));
GCHandle h = GCHandle.Alloc(o, GCHandleType.Pinned);
addr = h.AddrOfPinnedObject();
Console.WriteLine(addr.ToString("x"));
h.Free();
}
}
Number 1 is not possible at all, you can't have a pointer to a managed object. However, you can use an IntPtr structure to get information about the address of the pointer in the reference:
GCHandle handle = GCHandle.Alloc(str, GCHandleType.Pinned);
IntPtr pointer = GCHandle.ToIntPtr(handle);
string pointerDisplay = pointer.ToString();
handle.Free();
For number 2 you use the & operator:
int* p = &myIntVariable;
Pointers of course have to be done in a unsafe block, and you have to allow unsafe code in the project settings. If the variable is a local variable in a method, it's allocated on the stack so it's already fixed, but if the variable is a member of an object, you have to pin that object in memory using the fixed keyword so that it's not moved by the garbage collector.
To answer your question most correctly:
#1 is possible but a bit tricky, and should be only done for debugging reasons:
object o = new object();
TypedReference tr = __makeref(o);
IntPtr ptr = **(IntPtr**)(&tr);
This works for any object and actually returns the internal pointer to the object in memory. Remember the address can change at any moment because of the GC, which likes to move objects across the memory.
#2 Pointers aren't objects and thus don't inherit ToString from them. You have to cast the pointer to IntPtr like so:
Console.WriteLine((IntPtr)pi);