no matter what I try. I appear to get garbage results when I marshal the data across! The data after the marshal copy just contains an array of what looks like uninitialized data. Just pure garbage.
Thanks for your help in advance!
C++
typedef uint64_t TDOHandle;
extern "C" DATAACCESSLAYERDLL_API const TDOHandle * __stdcall DB_GetRecords()
{
const Database::TDBRecordVector vec = Database::g_Database.GetRecords();
if (vec.size() > 0)
{
return &vec[0];
}
return nullptr;
}
C#
The declaration
[System.Security.SuppressUnmanagedCodeSecurity()]
[DllImport("DataCore.dll")]
static private extern IntPtr DB_GetRecords();
//The marshalling process
IntPtr ptr_records = DB_GetRecords();
if (ptr_records != null)
{
Byte[] recordHandles = new Byte[DB_GetRecordCount()*sizeof (UInt64)];
Marshal.Copy(ptr_records, recordHandles, 0, recordHandles.Length);
Int64[] int64Array = new Int64[DB_GetRecordCount()];
Buffer.BlockCopy(recordHandles, 0, int64Array, 0, recordHandles.Length);
}
You are returning the address of memory owned by a local variable. When the function returns, the local variable is destroyed. Hence the address you returned is now meaningless.
You need to allocate dynamic memory and return that. For instance, allocate it with CoTaskMemAlloc. Then the consuming C# can deallocate it with a call to Marshal.FreeCoTaskMem.
Or allocate the memory using new, but also export a function from your unamanaged code that can deallocate the memory.
For example:
if (vec.size() > 0)
{
TDOHandle* records = new TDOHandle[vec.size()];
// code to copy content of vec to records
return records;
}
return nullptr;
And then you would export another function that exposed the deallocator:
extern "C" DATAACCESSLAYERDLL_API void __stdcall DB_DeleteRecords(
const TDOHandle * records)
{
if (records)
delete[] record;
}
All that said, it seems that you can obtain the array length before you call the function to populate the array. You do that with DB_GetRecordCount(). In that case you should create an array in your managed code, and pass that to the unmanaged code for it to populate. That side steps all the issues of memory management.
I'll add that there is another way to do it:
public sealed class ULongArrayWithAllocator
{
// Not necessary, default
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr AllocatorDelegate(IntPtr size);
private GCHandle Handle;
private ulong[] allocated { get; set; }
public ulong[] Allocated
{
get
{
// We free the handle the first time the property is
// accessed (we are already C#-side when it is accessed)
if (Handle.IsAllocated)
{
Handle.Free();
}
return allocated;
}
}
// We could/should implement a full IDisposable interface, but
// the point of this class is that you use it when you want
// to let C++ allocate some memory and you want to retrieve it,
// so you'll access LastAllocated and free the handle
~ULongArrayWithAllocator()
{
if (Handle.IsAllocated)
{
Handle.Free();
}
}
// I'm using IntPtr for size because normally
// sizeof(IntPtr) == sizeof(size_t) and vector<>.size()
// returns a size_t
public IntPtr Allocate(IntPtr size)
{
if (allocated != null)
{
throw new NotSupportedException();
}
allocated = new ulong[(long)size];
Handle = GCHandle.Alloc(allocated, GCHandleType.Pinned);
return Handle.AddrOfPinnedObject();
}
}
[DllImport("DataCore.dll", CallingConvention = CallingConvention.StdCall)]
static private extern IntPtr DB_GetRecords(ULongArrayWithAllocator.AllocatorDelegate allocator);
and to use it:
var allocator = new ULongArrayWithAllocator();
DB_GetRecords(allocator.Allocate);
// Here the Handle is freed
ulong[] allocated = allocator.Allocated;
and C++ side
extern "C" DATAACCESSLAYERDLL_API void __stdcall DB_GetRecords(TDOHandle* (__stdcall *allocator)(size_t)) {
...
// This is a ulong[vec.size()] array, that you can
// fill C++-side and can retrieve C#-side
TDOHandle* records = (*allocator)(vec.size());
...
}
or something similar :-) You pass a delegate to the C++ function that can allocate memory C#-side :-) And then C# side you can retrieve the last memory that was allocated. It is important that you don't make more than one allocation C++-side in this way in a single call, because you are saving a single LastAllocated reference, that is "protecting" the allocated memory from the GC (so don't do (*allocator)(vec.size());(*allocator)(vec.size());)
Note that it took me 1 hour to write correctly the calling conventions of the function pointers, so this isn't for the faint of heart :-)
Related
Actually I have many questions, so I try to describe them in detail, thanks for your patience.
I want to write a some generic methods for wrapping native APIs, just like ReadProcessMemory and WriteProcessMemory.
Take WriteProcessMemory for example, I found some ways to do them:
1.GCHandle
// Write memory: Non-Array
var lengthInBytes = (IntPtr)Marshal.SizeOf<T>();
var tHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
// Call native api;
}
finally
{
tHandle.Free();
}
// Write memory: Array
var lengthInBytes = (IntPtr)(data.Length * Marshal.SizeOf<T>());
var tHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
// Call native api;
}
finally
{
tHandle.Free();
}
This way is very simple, but the only problem is the object that to be pinned must be a built-in value type ( except fixed array, but the type of the element in array is subject to the same requirements ), . So I found another way: Marshal.AllocHGlobal method.
2. The members in Marshal class
//Write memory: Non-Array
var lengthInBytes = Marshal.SizeOf<T>();
var memPtr = Marshal.AllocHGlobal(lengthInBytes);
try
{
Marshal.StructureToPtr<T>(data, memPtr, false);
// Call native API
}
finally
{
Marshal.DestroyStructure<T>(memPtr);
Marshal.FreeHGlobal(memPtr);
}
// Write memory: Array
var perSize = Marshal.SizeOf<T>();
var arrayLength = data.Length;
var lengthInBytes = arrayLength * perSize;
var memPtr = Marshal.AllocHGlobal(lengthInBytes);
var baseAdr = memPtr;
try
{
foreach (var item in data)
{
Marshal.StructureToPtr<T>(item, baseAdr, false);
baseAdr += perSize;
}
// call native API.
}
finally
{
for (int i = 0; i < arrayLength; i++, baseAdr -= perSize)
{
Marshal.DestroyStructure<T>(baseAdr);
}
Marshal.FreeHGlobal(memPtr);
}
This way will alloc some memory, copy data, and even traverse array twice, But it supports the reference types, and I was satisfied with it, But then I got a idea: how about using Byte[] instead of IntPtr?
Specifically,The extern method is:
[DllImport("Kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern Boolean WriteProcessMemory([In] HandleRef hProcess, [In]IntPtr lpBaseAddress, [In, MarshalAs(UnmanagedType.LPArray)] Byte[] lpBuffer, [In, MarshalAs(UnmanagedType.SysInt)]IntPtr nSize, [Out, Optional, MarshalAs(UnmanagedType.SysInt)] out IntPtr lpNumberOfBytesWritten);
And the code in method is:
var perSize = Marshal.SizeOf<T>();
var buffer = new Byte[perSize];
var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
var pBuffer = handle.AddrOfPinnedObject();
try
{
Marshal.StructureToPtr<T>(data, pBuffer, false);
//Call native API.
}
finally
{
// Question!!!!!!!!!!!!!!
Marshal.DestroyStructure<T>(pBuffer);
handle.Free();
}
So there is a question: do I need to call Marshal.DestroyStructure method?
MSDN says
StructureToPtr(T, IntPtr, Boolean) copies the contents of structure
to the pre-allocated block of memory that the ptr parameter points to.
If structure contains reference types that marshal to COM interface
pointers (interfaces, classes without layout, and System.Object), the
managed objects are kept alive with reference counts. All other
reference types (for example, strings and arrays) are marshaled to
copies. To release these managed or unmanaged objects, you must call
the DestroyStructure(IntPtr) method before you free the memory
block.
And this method is "Marshals data from a managed object of a specified type to an unmanaged block of memory."
But variable buffer is a Byte[](In managed memory), so if I wont call DestroyStructure, can GC release the reference types in T? ( By the way, in my test, this way is much faster than second way, even though Marshal.DestroyStructure method has also been called too... )
And more questions is, will there be more ways to get the pointer of generic T? And if there are any mistake in the solution above?
I am trying to create and pass an array of pointers to an unmanaged DLL function using the following C# code.
[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint AnCtx_Init(IntPtr ctx);
//create context
this.ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
AnCtx_Init(ptr);//returns 0 (non-error)
this.ctx = (IntPtr)Marshal.PtrToStructure(ptr, typeof(IntPtr));
[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AnDevice_GetList(IntPtr ctx, out IntPtr outdevs, out int outndevs);
IntPtr devs, ndevs;
AnDevice_GetList(ctx, out devs, out ndevs); //exception occurs here
However upon my last call I receive an AccessViolationException. I think it has to do with the array pointer I am passing however I have not been able to find a solution.
The end goal I am trying to achieve here is to pass a pointer to AnDevice_GetList and with the parameter outdevs be left with an array that has been populated by the DLL.
Let me know if you need any further info or have any ideas for me to try.
Edit:
Here is the function I am trying to call.
Header file:
An_DLL AnError AnDevice_GetList(AnCtx *ctx, AnDeviceInfo ***outdevs,
size_t *outndevs);
typedef struct AnDevice AnDevice;
typedef int AnError;
typedef struct AnCtx AnCtx;
And implementation:
AnError AnDevice_GetList(AnCtx *ctx, AnDeviceInfo ***outdevs, size_t *outndevs)
{
An_LOG(ctx, AnLog_DEBUG, "enumerate devices...");
libusb_device **udevs;
ssize_t ndevs = libusb_get_device_list(ctx->uctx, &udevs);
if (ndevs < 0) {
An_LOG(ctx, AnLog_ERROR, "libusb_get_device_list: %s",
libusb_strerror(ndevs));
return AnError_LIBUSB;
}
AnDeviceInfo **devs = malloc((ndevs + 1) * sizeof *devs);
if (!devs) {
An_LOG(ctx, AnLog_ERROR, "malloc: %s", strerror(errno));
return AnError_MALLOCFAILED;
}
memset(devs, 0, (ndevs + 1) * sizeof *devs);
size_t j = 0;
for (ssize_t i = 0; i < ndevs; ++i) {
libusb_device *udev = udevs[i];
AnDeviceInfo info;
An_LOG(ctx, AnLog_DEBUG, "device: bus %03d addr %03d",
libusb_get_bus_number(udev), libusb_get_device_address(udev));
if (populate_info(ctx, &info, udev))
continue;
An_LOG(ctx, AnLog_DEBUG, "vid 0x%04x pid 0x%04x",
info.devdes.idVendor, info.devdes.idProduct);
if (!match_vid_pid(info.devdes.idVendor, info.devdes.idProduct)) {
An_LOG(ctx, AnLog_DEBUG, " does not match Antumbra VID/PID");
continue;
}
devs[j] = malloc(sizeof *devs[j]);
if (!devs[j]) {
An_LOG(ctx, AnLog_ERROR, "malloc: %s", strerror(errno));
continue;
}
libusb_ref_device(udev);
*devs[j] = info;
++j;
}
libusb_free_device_list(udevs, 1);
*outdevs = devs;
*outndevs = j;
return AnError_SUCCESS;
}
Your unmanaged function is declared like this:
AnError AnDevice_GetList(AnCtx *ctx, AnDeviceInfo ***outdevs, size_t *outndevs)
You should translate that as:
[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AnDevice_GetList(IntPtr ctx, out IntPtr outdevs,
out IntPtr outndevs);
And this is almost exactly as you have done. The only differences are that the return value is int and the outndevs parameter is of type IntPtr. That's because size_t is pointer sized on the platforms that I am aware of.
Call it like this:
IntPtr ctx = ...; // create a context somehow
IntPtr devs;
IntPtr ndevs;
int retval = AnDevice_GetList(ctx, out devs, out ndevs);
if (retval != AnError_SUCCESS)
// handle error
So, where could your code be going wrong? One likely explanation is that the context that you pass is invalid. Another possibility is that you execute 64 bit code and the incorrect size of outndevs in your translation caused the error.
This is a pretty hard API to call using p/invoke. What can you do now with devs. You can copy the values into an IntPtr[] array easily enough. And presumably the library has functions that operate on these opaque device pointers. But you have to keep hold of devs and pass it back to the library to deallocate it. Presumably the library exports a function to do that?
Based on your comments and various updates, it looks like you are not getting a proper context. We can only guess, but I expect that AnCtx_Init is declared as
AnError AnCtx_Init(AnCtx **octx)
That is a pointer to opaque context AnCtx*. Translate that as:
[DllImport("libantumbra.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int AnCtx_Init(out IntPtr octx);
Call it like this:
IntPtr ctx;
int retval = AnCtx_Init(out ctx);
if (retval != AnError_SUCCESS)
// handle error
The big thing that you have to do now is start checking for errors. Unmanaged code won't throw exceptions. You need to do error checking yourself. It is laborious, but it must be done. Take it one function at a time. Once you are sure a function call is working, move on to the next.
Some things don't make sense in your example. You create ptr2 and allocate space for it but never copy anything into that space, and you don't pass it to your AnDevice_GetList function so that seems completely unnecessary. You create ptArray but never use it anywhere either.
In this code, you're creating a managed array of IntPtr structures, and allocating memory for each of them to point to, and the size of what they are pointing to is the size of a single pointer:
IntPtr[] ptArray = new IntPtr[] {
Marshal.AllocHGlobal(IntPtr.Size),
Marshal.AllocHGlobal(IntPtr.Size)
};
To really help we need a clear understanding of exactly what AnDevice_GetList is going to do. If AnDevice_GetList is populating an array of pointers, what do they point to? Do they point to structures that were allocated by AnDevice_GetList? If so, then what you want to do is to create an array of IntPtr and pin it while you make the unmanaged call. Since you're creating an array for the call to fill, do NOT pass the array as an out parameter.
[DllImport("libsomething.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint AnDevice_GetList(IntPtr outdevs);
IntPtr[] ptArray = new IntPtr[numberOfPointersRequired];
GCHandle handle = GCHandle.Alloc(ptArray);
try
{
AnDevice_GetList(handle.AddrOfPinnedObject());
}
finally
{
handle.Free();
}
I left off the other parameters, because I have no idea what you're doing with them or how you're expecting them to be handled.
Unmanaged and Managed Memory Regions
I am attempting to execute unmanaged code from a C-library. One of the methods takes a void* as a parameter but under the hood it's cast to a struct of type nc_vlen_t
C struct for nc_vlen_t
/** This is the type of arrays of vlens. */
typedef struct {
size_t len; /**< Length of VL data (in base type units) */
void *p; /**< Pointer to VL data */
} nc_vlen_t;
Executing the method is correct and it works, I am concerned more about the pinning and safe handling of managed and unmanaged memory regions. I want to be as certain as possible that I am not going to cause memory leaks or a SEGFAULT. I wrote a struct that will be marshalled to and from the nc_vlen_t when I execute the C-library method calls.
C# struct
[StructLayout(LayoutKind.Sequential)]
public struct VlenStruct {
public Int32 len;
public IntPtr p; // Data
}
The struct consists of a size_t that indicates the array length and a void * to the data. Inside the library it has attributes that allow it to cast the (void*) to the appropriate numeric types and I've had great success with that so far.
What I want to understand is the best way to handle the memory regions. After reading some articles and other SO questions this is my best guess for how to handle it. I have a class that acts as an arbiter for creating and managing the structs and their memory. I rely on a destructor to free the handle which will unpin the array so that the GC can do it's job.
C# Vlen Helper
public class Vlen {
private GCHandle handle;
private VlenStruct vlen_t;
public Vlen() {
isNull = true;
}
public Vlen(Array t) {
isNull = false;
handle = GCHandle.Alloc(t, GCHandleType.Pinned); // Pin the array
vlen_t.len = t.Length;
vlen_t.p = Marshal.UnsafeAddrOfPinnedArrayElement(t, 0); // Get the pointer for &t[0]
}
~Vlen() {
if(!isNull) {
handle.Free(); // Unpin the array
}
}
public VlenStruct ToStruct() {
VlenStruct retval = new VlenStruct();
retval.len = vlen_t.len;
retval.p = vlen_t.p;
return retval;
}
private bool isNull;
}
C Method Declaration
//int cmethod(const int* typep, void *data)
// cmethod copies the array contents of the vlen struct to a file
// returns 0 after successful write
// returns -1 on fail
[DllImport("somelib.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true, CallingConvention=CallingConvention.Cdecl)]
public static extern Int32 cmethod(ref Int32 typep, ref VlenStruct data);
If I use this class to create the struct is there a possibility that the GC will clean the array before the C-library is called in this situation:
C# Use-case
{
double[] buffer vlenBuffer = new double[] { 0, 12, 4};
Vlen data = new Vlen(vlenBuffer); // The instance now pins buffer
VlenStruct s = data.ToStruct()
Int32 type = VLEN_TYPE;
cmethod(ref type, ref s);
}
Is it possible for the data instance to be cleaned and thereby unpin buffer which could cause unpredictable behavior when executing the external library method?
Yes, you certainly have a problem here. As far as the jitter is concerned, the lifetime of your "data" object ends just before the ToStruct() method returns. Check this answer for the reason why. Which permits the finalizer to run while your unmanaged code is running. Which unpins your array. It would actually take another garbage collection to corrupt the data that the unmanaged code uses. Very rare indeed but not impossible. You are not likely to get an exception either, just random data corruption.
One workaround is to extend the lifetime of the Vlen object beyond the call, like this:
Vlen data = ...
...
cmethod(ref type, ref s);
GC.KeepAlive(data);
Which works but doesn't win any prizes, easy to forget. I would do this differently:
public static void CallHelper<T>(int type, T[] data) {
var hdl = GCHandle.Alloc(data, GCHandleType.Pinned);
try {
var vlen = new nc_vlen();
vlen.len = data.Length;
vlen.data = hdl.AddrOfPinnedObject();
cmethod(ref type, ref vlen);
}
finally {
hdl.Free();
}
}
Usage:
var arr = new int[] { 1, 2, 3 };
CallHelper(42, arr);
Which, beyond avoiding the early collection problem, also keeps the array pinned as short as possible. Do note that ref on the first argument of this function is pretty strange, you would not expect this function to alter the data type.
The question in short words is :
How to free memory returned from Native DLL as ItrPtr in managed code?
Details :
Assume we have simple function takes two parameters as OUTPUT, The first one is Reference Pointer to byte array and the second one is Reference Int .
The function will allocate amount of bytes based on some rules and return the pointer of memory and the size of bytes and the return value (1 for success and 0 for fail) .
The code below works fine and I can get the byte array correctly and the count of bytes and the return value, but when I try to free the memory using the pointer (IntPtr) I get exception :
Windows has triggered a breakpoint in TestCppDllCall.exe.
This may be due to a corruption of the heap, which indicates a bug in TestCppDllCall.exe or any of the DLLs it has loaded.
This may also be due to the user pressing F12 while TestCppDllCall.exe has focus.
The output window may have more diagnostic information.
To make things clear :
The next C# code work correctly with other DLL function have the same signature and freeing the memory works without any problem .
Any modification in (C) code accepted if you need to change allocation memory method or adding any other code .
All the functionality I need is Native DLL function accept Two Parameter by reference (Byte array and int , In c# [IntPtr of byte array and int]) fill them with some values based on some rules and return the function result (Success or Fail) .
CppDll.h
#ifdef CPPDLL_EXPORTS
#define CPPDLL_API __declspec(dllexport)
#else
#define CPPDLL_API __declspec(dllimport)
#endif
extern "C" CPPDLL_API int writeToBuffer(unsigned char *&myBuffer, int& mySize);
CppDll.cpp
#include "stdafx.h"
#include "CppDll.h"
extern "C" CPPDLL_API int writeToBuffer(unsigned char*& myBuffer, int& mySize)
{
mySize = 26;
unsigned char* pTemp = new unsigned char[26];
for(int i = 0; i < 26; i++)
{
pTemp[i] = 65 + i;
}
myBuffer = pTemp;
return 1;
}
C# code :
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace TestCppDllCall
{
class Program
{
const string KERNEL32 = #"kernel32.dll";
const string _dllLocation = #"D:\CppDll\Bin\CppDll.dll";
const string funEntryPoint = #"writeToBuffer";
[DllImport(KERNEL32, SetLastError = true)]
public static extern IntPtr GetProcessHeap();
[DllImport(KERNEL32, SetLastError = true)]
public static extern bool HeapFree(IntPtr hHeap, uint dwFlags, IntPtr lpMem);
[DllImport(_dllLocation, EntryPoint = funEntryPoint, CallingConvention = CallingConvention.Cdecl)]
public static extern int writeToBuffer(out IntPtr myBuffer, out int mySize);
static void Main(string[] args)
{
IntPtr byteArrayPointer = IntPtr.Zero;
int arraySize;
try
{
int retValue = writeToBuffer(out byteArrayPointer, out arraySize);
if (retValue == 1 && byteArrayPointer != IntPtr.Zero)
{
byte[] byteArrayBuffer = new byte[arraySize];
Marshal.Copy(byteArrayPointer, byteArrayBuffer, 0, byteArrayBuffer.Length);
string strMyBuffer = Encoding.Default.GetString(byteArrayBuffer);
Console.WriteLine("Return Value : {0}\r\nArray Size : {1}\r\nReturn String : {2}",
retValue, arraySize, strMyBuffer);
}
}
catch (Exception ex)
{
Console.WriteLine("Error calling DLL \r\n {0}", ex.Message);
}
finally
{
if (byteArrayPointer != IntPtr.Zero)
HeapFree(GetProcessHeap(), 0, byteArrayPointer);
}
Console.ReadKey();
}
}
}
When I debug this code i set break point in the line (return 1) and the value of the buffer was :
myBuffer = 0x031b4fc0 "ABCDEFGHIJKLMNOPQRSTUVWXYZ««««««««î"
And I got the same value in C# code when the function call return and the value was :
52121536
The result I Got the correct Memory pointer and i am able to get the byte array value , how to free these memory blocks with this pointer in C# ?
Please let me know if there anything is not clear or if there any typo, I am not native English speaker .
Short answer: you should add a separate method in the DLL that frees the memory for you.
Long answer: there are different ways in which the memory can be allocated inside your DLL implementation. The way you free the memory must match the way in which you have allocated the memory. For example, memory allocated with new[] (with square brackets) needs to be freed with delete[] (as opposed to delete or free). C# does not provide a mechanism for you to do it; you need to send the pointer back to C++.
extern "C" CPPDLL_API void freeBuffer(unsigned char* myBuffer) {
delete[] myBuffer;
}
If you are allocating your own memory in native code, use CoTaskMemAlloc and you can free the pointer in managed code with Marshal.FreeCoTaskMem. CoTaskMemAlloc is described as "the only way to share memory in a COM-based application" (see http://msdn.microsoft.com/en-us/library/windows/desktop/aa366533(v=vs.85).aspx )
if you need to use the memory allocated with CoTaskMemAlloc with a native C++ object, you can use placement new to initialize the memory as if the new operator was used. For example:
void * p = CoTaskMemAlloc(sizeof(MyType));
MyType * pMyType = new (p) MyType;
This doesn't allocate memory with new just calls the constructor on the pre-allocated memory.
Calling Marshal.FreeCoTaskMem does not call the destructor of the type (which isn't needed if you just need to free memory); if you need to do more than free memory by calling the destructor you'll have to provide a native method that does that and P/Invoke it. Passing native class instances to managed code is not supported anyway.
If you need to allocate memory with some other API, you'll need to expose it in managed code through P/Invoke in order to free it in managed code.
HeapFree(GetProcessHeap(), 0, byteArrayPointer);
No, that can't work. The heap handle is wrong, the CRT creates its own heap with HeapCreate(). It's buried in the CRT data, you can't get to it. You could technically find the handle back from GetProcessHeaps(), except you don't know which one it is.
The pointer can be wrong too, the CRT may have added some extra info from the pointer returned by HeapAlloc() to store debugging data.
You'll need to export a function that calls delete[] to release the buffer. Or write a C++/CLI wrapper so you can use delete[] in the wrapper. With the extra requirement that the C++ code and the wrapper use the exact same version of the CRT DLL (/MD required). This almost always requires that you can recompile the C++ code.
I'm trying to write plugin dll which has to implement the following function:
int GetFunctionTable(FuncDescStruct **ppFunctionTable);
So my plugin code in C# declares this:
public static unsafe int GetFunctionTable(IntPtr functionTablePtr);
This function will be called and expected to fill functionTablePtr with pointer to array of structs describing set of callback functions.
In plain C/C++ it looks something like:
// declare func table
// VExampleF1, VExampleF2 - are function pointers
FuncDescStruct funcTable[] = {
"ExampleF1", { VExampleF1, 0, 0, 0, 0, NULL }, //filling descriptions.
"ExampleF2", { VExampleF2, 1, 0, 1, 0, NULL }
};
int GetFunctionTable(FuncDescStruct **ppFunctionTable)
{
*ppFunctionTable = funcTable; // how to do this correctly in C#?
// must return the number of functions in the table
return funcTableSize;
}
I'm trying to do the following:
static unsafe FunctionTag[] funcTable;
static List<IntPtr> allocatedMemory;
public static unsafe int GetFunctionTable(IntPtr functionTablePtr)
{
//create just one test callback description
funcTable = new FunctionTag[1];
funcTable[0].Name = "VExampleF1";
funcTable[0].Description.Function = VExampleF1;
funcTable[0].Description.ArrayQty = 0;
funcTable[0].Description.FloatQty = 0;
funcTable[0].Description.StringQty = 0;
funcTable[0].Description.DefaultQty = 0;
funcTable[0].Description.DefaultValues = null;
// saving allocated memory for further cleanup
allocatedMemory = new List<IntPtr>();
int intPtrSize = Marshal.SizeOf(typeof(IntPtr));
IntPtr nativeArray = Marshal.AllocHGlobal(intPtrSize * funcTable.Length);
for (int i = 0; i < funcTable.Length; i++)
{
IntPtr nativeFD = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FunctionTag)));
allocatedMemory.Add(nativeFD);
Marshal.StructureToPtr(funcTable[i], nativeFD, false);
Marshal.WriteIntPtr(nativeArray, i * intPtrSize, nativeFD);
}
Marshal.WriteIntPtr(functionTablePtr, nativeArray);
return funcTable.Length;
}
Such code doesn't work, and the question is how to send a pointer to array of managed structs for use by unmanaged code? In which direction should I go?
We've been doing quite a lot of this sort of thing over the last couple of years.
We use a mixed-mode 'bridge' assembly to manage mapping function calls and marshalling data between managed and unmanaged environments. We often use a 'mixed-mode' class to wrap a managed class, providing a native interface for calling it's functionality.
Let's consider your problem. You could write GetFunctionTable in managed c++. It can call some managed c# code to get the function information in a managed struct, and then 'marshall' it into the native struct.
In (c#) a managed version of GetFunctionTable function:
delegate void FunctionDelegate();
public struct FuncDescStructManager
{
public string Name;
public FunctionDelegate Function;
//...
}
public static List<FuncDescStructManager> GetFunctionTableManaged()
{
List<FuncDescStructManager> list = new List<FuncDescStructManager>();
list.Add(new FuncDescStructManaged () {"ExampleF1", VExampleF1});
return list;
}
In the mixed-mode bridge assembly you can implement the native function GetFunctionTable,
calling the managed function and marshalling the data:
int GetFunctionTable(FuncDescStruct **ppFunctionTable)
{
// Call the managed function
List<FuncDescStructManaged>^ managedList = GetFunctionTableManaged();
nativeArray = malloc(managedList.Length * sizeof(FuncDescStruct));
int i=0;
foreach (FuncDescStructManaged managedFunc in managedList)
{
// Marshall the managed string to native string could look like this:
stringPtr = Marshal::StringToHGlobalUni(managedFunc.Name);
nativeArray[i].Name = ((wchar_t*)stringPtr.ToPointer());
Marshal::FreeHGlobal(stringPtr);
// Marshall a delegate into a native function pointer using a
// wrapper class:
WrapDelegateAsPtr funcPtr = new WrapDelegateAsPtr(managedFunc.Function);
// funcPtr will need to be deleted by caller
nativeArray[i].Function = funcPtr.NativeFunction;
i++;
}
return i;
}
// Mixed mode wrapper class
// Member is a reference to a managed delegate.
// Because we need to reference this from native code, the wrapped
// delegate will be stored as a void*.
class WrapDelegateAsFPtr
{
public:
WrapDelegateAsNativeFunctionPointer(FunctionDelegate _delegate)
{
delegate = _delegate;
// Tell the garbage handler not to remove the delegate object yet
GCHandle gch = GCHandle::Alloc(svgContents);
managedDelegatePtr = GCHandle::ToIntPtr(gch).ToPointer();
}
~WrapDelegateAsNativeFunctionPointer
{
// Tell the garbage collector we are finished with the managed object
IntPtr temp(managedDelegatePtr;);
GCHandle gch = static_cast<GCHandle>(temp);
gch.Free();
}
void NativeFunction()
{
}
private:
void* managedDelegatePtr;
}
Hope this helps - Any questions just ask!
THis is a rather belatedd answer, but I have come up against exactly the same problem. I have implmented a DATA PLUGIN using Kostya's framework, and got it to work perfectly, then trying to implmement an AFL plugin, I ran into the problem as per above. I have managed to get itworking without using a C++ rapper class.
The problem is with the function pointer/reference/address that is passed from withing the FUnctionTable. AS these functions have been dclared as STATIC for EXPORT purposes, they are incompatible with the C++ delegate implmentation in the GETFUNCTIONTABLE method. IF you ddo the following , it should work:-
Add the 2 signatures:-
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool SetDllDirectory(string lpPathName);
the function reference definition in the GetFunctionTable structure must be changed to:
IntPtr Function;
Add the following statements to get the exported function address:-
IntPtr dllHandle = LoadLibrary(fullPath);
IntPtr fptr = GetProcAddress(dllHandle, "VfmExample1");
and lastly initialize the function variable in the GetFunctionTable structure , e.g.
functable[0].Description.function = fptr;
and that should do it
You have to use the fixing construct to fix the buffer in place, because the C# GC reserves the right to move it around if you don't, invalidating the pointer. I don't know how to fix a buffer indefinitely. You're going to have to worry about how that memory is managed, too.