In my code I have c DLL that accepts array of strings:
void Helper::ProcessEvent(PEVENT_RECORD pEvent,wchar_t** OutPutFormattedData)
I'm invoking it with this code:
[DllImport("Helper.dll", EntryPoint = "ProcessEvent")]
internal static extern uint ProcessEvent(
[In, Out]
ref EVENT_RECORD pEvent,
[In, Out]
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] ref string[] pResult);
In c++ code here is the main code I'm using to fill the array:
for(int i=0;i<list.Size;i++)
{
EventWrapperClass *EWC = new EventWrapperClass();
EWC = list.Events[i];
OutPutFormattedData[i] = new wchar_t [wcslen(HelperFormatMessage(L"%s: %s\n",EWC->GetProperyName(),EWC->GetProperyValue()))+1];
wcscpy(OutPutFormattedData[i] ,HelperFormatMessage(L"%s: %s\n",EWC->GetProperyName(),EWC->GetProperyValue()));
}
And the invoke code:
string[] strArr= new string[1];
NativeHelper.ProcessEvent(ref eventRecord, ref strArr);
I have two questions:
Why when I check the value of the passed array in c# after calling this function I see it's empty (data is exist in c++ code, I debugged it) ?
If I'm allocating memory in c++ dll, where I need to free it? in c++ or c#?
Many thanks!
Edit:
signture of c++:
static __declspec(dllexport) void ProcessEvent(PEVENT_RECORD pEvent, wchar_t** OutPutFormattedData);
If you allocate a (managed or unmanaged) buffer (or array or chars) in C# and then fill in C++:
1. Deallocate in C#.
2. Buffer should be [in] and not have ref or out in C# signature.
If you allocate a buffer (or array of chars) in C++:
1. Pass buffer as [out] out IntPtr.
2. Add deallocate method to C++ signature.
3. Copy buffer into C# buffer or new string
4. Call C++ deallocater from C# with IntPtr.
You can use System.Runtime.InteropServices.Marshal to allocate/deallocate unmanged memory within C#. Do not use to deallocate memory allocated outside .NET!
System.Runtime.InteropServices.Marshal can also be used to copy memory from external buffer (IntPtr) to .NET buffer or string.
Mark imported method as private and wrap with a public (or protected or internal) method that uses the .NET parameters (e.g. that uses System.Runtime.InteropServices.Marshal.Copy and calls external deallocate).
C++:
int ProcessEvent(PEVENT_RECORD eventData, wchar_t*& message)
{
// example code
message = new wchar_t[100];
if (message == 0)
return 1;
}
void DeallocateString(wchar_t* array)
{
delete[] arrayPtr;
}
wchar_t* ErrorCodeToMessage(int errorCode)
{
switch (errorCode)
{
case 0: return 0; // return NULL pointer
case 1: return L"No!!!";
default: return L"WTF!?";
}
}
C#:
[DllImport("Helper.dll", EntryPoint = "ProcessEvent")]
private static extern uint ProcessEventExternal(
[In, Out] ref EventData eventData,
[In, Out, MarshalAs(UnmanagedType.SysInt))] ref IntPtr resultMessages);
[DllImport("Helper.dll", EntryPoint = "DeallocateString")]
private static extern voidDeallocateStringExternal(
[In, MarshalAs(UnmanagedType.SysInt)] IntPtr arrayPtr);
[DllImport("Helper.dll", EntryPoint = "ErrorCodeToMessage")]
private static extern
[return: MarshalAs(UnmanagedType.SysInt)] IntPtr
ErrorCodeToMessageExternal(int errorCode);
public string ProcessEvent(ref EventData eventData)
{
IntPtr resultPtr = IntPtr.Zero;
uint errorCode = ProcessEventExternal(eventData, ref resultPtr);
if (errorCode != null)
{
var errorPtr = ErrorCodeToMessageExternal(errorCode);
// returns constant string - no need to deallocate
var errorMessage = Marshal.PtrToStringUni(errorPtr);
throw new ApplicationException(errorMessage);
}
var result = Marshal.PtrToStringUni(resultPtr);
ExternalDeallocate(resultPtr);
return result;
}
I don't know about question 1, but about question 2:
for(int i=0;i<list.Size;i++)
{
EventWrapperClass *EWC = new EventWrapperClass();
EWC = list.Events[i];
...
}
In the first line, within the loop, you create a new object. In the second line, you assign a different object to your pointer, which is the one stored in list.Events[i].
At this point, nothing points to your first object any longer, and you have a memory leak for every iteration of the loop!
Change the two lines to
EventWrapperClass *EWC = list.Events[i];
as there is no need to create a new object.
Then, if you don't need the event any longer, delete it within the loop and be sure to clear the list afterwards. You may only do this if you are 100% sure the event is not needed any longer. This can easily produce dangling pointers!
Related
In summary:
I`m trying to use a C++ dll with cdecl calling convention all ran fine unless i get to this method signature:
int SaveToBuffer( char **buf, int *buf_size );
from what i have read i should use it like this:
[DllImport("entry.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "SaveToBuffer")]
private static int SaveToBuffer( ref sbyte[] buf, ref int buf_size );
This does not work if this function is called from C# program crashes.
I suppose this is related to Cdecl calling model and should use Marshal.AllocHGlobal(value),
I can`t imagine how should it be done correct.
I also tryed this:
[DllImport("entry.dll",
CallingConvention = CallingConvention.Cdecl,
EntryPoint = "SaveToBuffer")]
private static int SaveToBuffer( IntPtr buf, ref int buf_size );
And then alocate enough memory
IntPtr data=Marshal.AllocHGlobal(128000);
int bufSize=128000;
var sCode=SaveToBuffer(data,bufSize ); /* value of scode idicate succses*/
Calling this way i get return value from SaveToBuffer indicating function succseeded but: bufSize returns to 0 and how should i read my data from IntPtr.
I`m completly stuck on this.
This is not an issue with the calling convention. The problem is in the buffer handling.
There's really only one sensible way to interpret the C++ argument types and the apparent intent to return an array of bytes. That is that the buffer is allocated and populated by the callee, and its address returned in buf. The buffer length is returned in buf_size.
With these semantics the function arguments cannot be marshalled automatically and you'll have to do it manually:
[DllImport("entry.dll", CallingConvention = CallingConvention.Cdecl)]
private static int SaveToBuffer(out IntPtr buf, out int buf_size);
Call like this
IntPtr buf;
int buf_size;
int retval SaveToBuffer(out buf, out buf_size);
// check retval
Then copy to byte array like this:
byte[] buffer = new byte[buf_size];
Marshal.Copy(buf, buffer, 0, buf_size);
The DLL will also need to export a function to deallocate the unmanaged buffer.
I tried the solution proposed in Using SetFilePointer in C# has unblanced the stack to move file's pointer and it worked. The only problem was that when reading a device rather than a file, setfilepointer places the pointer at the beginning of the sector containing the address required, instead of placing it at the required address itself. Don't know why.
But my question is another. According to the docs i've read if you don't need to use moveDistanceHighBits because only the low order bytes are needed to address your wanted offset, you have to set moveDistanteHighBits to null. But i don't know how to do this.
Could someone help please?
Don't special-case this just because the winapi function does. Unlike C compilers that were in use 25 years ago, C# supports 64-bit integers well. And it is too risky. You want to write a wrapper method anyway so you can properly throw an exception when the winapi function fails. Never skip that. Do it like this:
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
public class NativeMethods {
public static void SetFilePointer(SafeFileHandle hFile, long dist, SeekOrigin method) {
int lodist = (int)dist;
int hidist = (int)(dist >> 32);
int retval = SetFilePointer(hFile, lodist, ref hidist, method);
if (retval == -1) throw new System.ComponentModel.Win32Exception();
}
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern int SetFilePointer(SafeFileHandle hFile,
int distlo, ref int disthi, SeekOrigin method);
}
}
Do note that you don't need pinvoke it at all. You want to use one of the FileStream constructors that takes an IntPtr or SafeHandle. This one for example. Now you can simply use FileStream.Seek().
You can change the PInvoke signature to use an IntPtr instead and then you can pass IntPtr.Zero when you want to pass NULL. The new signature would look like this:
[DllImport("kernel32.dll", EntryPoint="SetFilePointer", SetLastError=true)]
static extern uint SetFilePointer(
[In] Microsoft.Win32.SafeHandles.SafeFileHandle hFile,
[In] int lDistanceToMove,
[In] IntPtr lpDistanceToMoveHigh,
[In] EMoveMethod dwMoveMethod);
This works because in both definitions you're passing a pointer but using an IntPtr lets you control the actual pointer value. When you use ref, you have to have an exisiting local variable and the compiler will take its address.
If you need to pass/receive a value back (other than NULL) from the SetFilePointer call, you can do it this way:
int nValue = ...; // Value to pass for lpDistanceToMoveHigh
IntPtr pInt = IntPtr.Zero;
try
{
pInt = Marshal.AllocHGlobal ( sizeof ( int ) );
Marshal.WriteInt32 ( pInt, nValue );
// Call SetFilePointer(); upon successful return,
// you can read the value returned for lpDistanceToMoveHigh
nValue = Marshal.ReadInt32 ( pInt );
// ...
}
finally
{
if ( pInt != IntPtr.Zero )
Marshal.FreeHGlobal ( pInt );
}
I'm working in c# and I need to use this function from a c++ dll:
extern "C" char IMPEXP __stdcall service_GetParameter ( const char* parameter, const int value_lenght, char** value );
I have used it in c++ code as follow:
char *val = new char[256];
service_GetParameter("firmware_version", 255, &val);
AnsiString FirmwareVersion = val;
delete[] val;
How can I import this function and use it in c#?
Thanks in advance
If this function allocates memory and makes the caller responsible for freeing it, I'm afraid you'll have to manage this manually: Declare the parameter as a ref IntPtr and use the methods of the Marshal class to get a String with a copy of the pointed data.
Then call the appropriate function for freeing the memory (as Dirk said, we can't say more about this without more information on the function).
If it really must be allocated before calling, it should be something looking like this:
[DllImport("yourfile.dll", CharSet = CharSet.Ansi)]
public static extern sbyte service_GetParameter ( String parameter, Int32 length, ref IntPtr val);
public static string ServiceGetParameter(string parameter, int maxLength)
{
string ret = null;
IntPtr buf = Marshal.AllocCoTaskMem(maxLength+1);
try
{
Marshal.WriteByte(buf, maxLength, 0); //Ensure there will be a null byte after call
IntPtr buf2 = buf;
service_GetParameter(parameter, maxLength, ref buf2);
System.Diagnostics.Debug.Assert(buf == buf2, "The C++ function modified the pointer, it wasn't supposed to do that!");
ret = Marshal.PtrToStringAnsi(buf);
}
finally { Marshal.FreeCoTaskMem(buf); }
return ret;
}
I'd start with something like this:
[DllImport("yourfile.dll", CharSet = CharSet.Ansi]
public static extern Int32 service_GetParameter([MarshalAs(UnmanagedType.LPStr)] String szParameter, Int32 value_length, [Out] StringBuilder sbValue);
i have a c++ dll ,that has some extern function.
and its look like this
//C++ Code
void GetData(byte * pData)
{
byte a[] = {3,2,1};
pData = a;
}
and i have use this code in C# side to get data :
//C# Code
[DllImport(UnmanagedDLLAddress)]
public static extern void GetData(ref IntPtr pData);
//and use it like
IntPtr pointer = IntPtr.Zero;
GetData(ref pointer);
byte[] data = new byte[3] // <===== think we know size
Marshal.Copy(pointer,data ,0,3);
but always "pointer" is zero so Marshal.Copy throw null exception
where i did mistake ?
ty
First, your C++ code puts the array to the stack. You need to allocate it some other way, for documentation start from here: http://msdn.microsoft.com/en-us/library/aa366533%28VS.85%29.aspx
Second, pData is a "normal" value argument, effectively a local variable. You assign to it, then it gets forgotten when function returns. You need it to be reference to pointer or pointer to pointer if you want it to be "out parameter" returning a value back. If you want to actually copy the array contents to the buffer pointed to by pData, then you need to use memcpy function from #include <cstring>.
Actually, like hyde told, use pData as "out" parameter.
Because it's obviously a reference type and no value type, the called method shall take care about memory allocation. I used "ref" only for value types, like integer - e.g. getting the length of the array from the unmanaged method.
This way works fine for me, furthermore, i use the "cdecl" calling convention.
IntPtr aNewIntArray;
uint aNewIntArrayCount = 0;
NativeMethods.getEntityFieldIntArray(out aNewIntArray, ref aNewIntArrayCount);
int[] aNewIntArrayResult = new int[aNewIntArrayCount];
Marshal.Copy(aNewIntArray, aNewIntArrayResult, 0, (int)aNewIntArrayCount);
method declaration:
[DllImport(SettingsManager.PathToDLL, EntryPoint = "getEntityFieldIntArray", CallingConvention = CallingConvention.Cdecl)]
public static extern ErrorCode getEntityFieldIntArray(out IntPtr aNewIntArray, ref UInt32 aNewIntArrayCount);
My C declarations are as follows:
int myData(uint myHandle, tchar *dataName, long *Time, uint *maxData, DATASTRUCT **data);
typedef struct {
byte Rel;
__int64 Time;
char Validated;
unsigned char Data[1];
} DATASTRUCT;
My C# declarations are as follows:
[DllImport("myData.dll", EntryPoint = "myData")]
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref DATASTRUCT[] data);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct DATASTRUCT
{
public sbyte Rel;
public long Time;
public byte Validated;
public double Data;
}
I then call the managed function as follows:
string dataToShow = "description";
long Time;
uint maxData; // How many structs will be returned, i.e., how much data is available?
uint myHandle = 1;
DATASTRUCT[] dataInformation = new DATASTRUCT[3]; // Doesn't it matter what I specify as the array size?
myData(myHandle, dataToShow, out Time, out maxData, ref dataInformation);
Upon execution the above function will return successfully with only one structure even though there are 3 to return. Why is this so?
Additional information; I have tried passing the pointer to a pointer of an array of structs the following ways:
ref DATASTRUCT[] data; // It works, but it only returns one struct
[Out, MarshalAs(UnmanagedType.LPArray)] DATASTRUCT[] data; // returns the number of defined structs with garbage
As I understand it I might need to do some manual marshalling using IntPtr, I do not know how to implement this however, so any advice would be appreciated.
Okay, it seems as though your native library does the allocation, so really all you need to do is provide a pointer through which you can access the allocated data.
Change your API definition to (note, I changed the maxData param to uint, long is 64 bits in .NET and 32 bits in native.
[DllImportAttribute("myData.dll", EntryPoint = "myData")]
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out uint Time, out uint maxData, out IntPtr pData);
Off the top of my head I can't quite remember if you need the out keyword for the final parameter, but I think so.
Then, call myData:
uint nAllocs = 0, time = 0;
IntPtr pAllocs = IntPtr.Zero;
myData(1, "description", out time, out nAllocs, out pAllocs);
Now, pAllocs should point to unmanaged memory, to marshal these into managed memory isn't too difficult:
[StructLayoutAttribute(LayoutKind.Sequential, Pack = 1)]
public struct DATASTRUCT
{
public byte Rel;
public long Time;
public byte Validated;
public IntPtr Data; //pointer to unmanaged string.
}
int szStruct = Marshal.SizeOf(typeof(DATASTRUCT));
DATASTRUCT[] localStructs = new DATASTRUCT[nAllocs];
for(uint i = 0; i < nallocs; i++)
localStructs[i] = (DATASTRUCT)Marshal.PtrToStructure(new IntPtr(pAllocs.ToInt32() + (szStruct * i)), typeof(DATASTRUCT));
And now you should have an array of local structs.
A point to note
You may need to set your project to compile as x86, to standardize the size of an IntPtr to 4 bytes (DWORD) instead of AnyCPU's default 8.
A pointer to a pointer could be represented in your dllimport declaration as ref IntPtr data, so your declaration would become:
[DllImportAttribute("myData.dll", EntryPoint = "myData")]
public static extern int myData(uint myHandle, [MarshalAs(UnmanagedType.LPTStr)] string dataName, out long Time, out uint maxData, ref IntPtr data);
(As an aside, I think a long in C is just equivalent to an int in C#. Long in C# is an Int64, which would be a long long in C)
Marshalling your DATASTRUCT[] to an IntPtr can be done using the GCHandle class
DATASTRUCT [] dataInformation = new DATASTRUCT[3];
GCHandle gch = GCHandle.Alloc(dataInformation , GCHandleType.Pinned);
IntPtr ptr = gch.AddrOfPinnedObject();
myData(myHandle, dataToShow, out Time, out maxData, ref ptr);
//It's absolutely essential you do this next bit so the object can be garbage collected again,
//but it should only be done once the unmanaged code is definitely done with the reference.
gch.Free();
Using the Marshal class and the StructureToPtr or Copy methods of it would also be an option but for the purposes of proving the concept at least the GCHandle should do the trick, it's just not ideal for scenarios where the unmanaged code does long running operations because you've pinned this object in place and the GC can't move it until you free it.