I am (successfully) calling the Windows FilterSendMessage function in c# using the following pinvoke signature:
[DllImport("fltlib.dll")]
public static extern IntPtr FilterSendMessage(
IntPtr hPort,
IntPtr inBuffer,
UInt32 inBufferSize,
IntPtr outBuffer,
UInt32 outBufferSize,
out UInt32 bytesReturned);
The outBuffer parameter is populated with an arbitrary number of structs (packed one after the other), defined in C as:
typedef struct _BAH_RECORD {
int evt
int len;
WCHAR name[1];
} BAH_RECORD, *PBAH_RECORD;
The name field is assigned a variable length, null-terminated unicode string. The len field describes the total size of the struct in bytes (including the name string). I am confident there's nothing wrong with how the structs are being handled in the unmanaged side of things.
My problem arises when I try and marshal the outBuffer to an instance of the BAH_RECORD struct, defined in c# as:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct BAH_RECORD
{
public UInt32 evt;
public UInt32 len;
public string name;
}
IntPtr outBuffer = Marshal.AllocHGlobal(OUT_BUFFER_SIZE);
hResult = Win32.FilterSendMessage(hPortHandle, inBuffer, IN_BUFFER_SIZE, outBuffer, OUT_BUFFER_SIZE, out bytesReturned);
BAH_RECORD bah = (BAH_RECORD)Marshal.PtrToStructure(outBuffer, typeof(BAH_RECORD));
<snip>
If I try and print/view/display bah.name, I get garbage...
To confirm that outBuffer does actually contain valid data, I did some crude pointer hackery in c# to step though it, calling Marshal.ReadInt32 twice (to cover the first 2 struct fields), and then Marshal.ReadByte a few times to populate a byte[] which I then use as an argument to Encoding.Unicode.GetString()...the string comes out fine, so it's definitely in there, I just can't seem to get the marshaller to handle it correctly (if it even can?)
Any help appreciated
Steve
The problem is that the 'name' string in your C# BAH_RECORD struct is marshaled as a pointer to a string (WCHAR*) but on the C side it is an inline WCHAR buffer. So when you marshal your struct the runtime reads the first four bytes of the buffer as a pointer and then attempts to read the string that it points to.
Unfortunately, there is no way for the runtime to automatically marshal variable sized buffers inside structs so you will need to use manual marshaling (or as you say "pointer hackery"). But when you advance the pointer to point at the buffer you don't need to read in the bytes individually and then convert them to a string - just call Marshal.PtrToStringUni.
Related
I am looking to use the GetEffectiveRightsFromAclW Win32 function in .NET Framework. One of the parameters is of type PTRUSTEE_W. Insofar as what I've come across, this is a rather complex C type:
typedef struct _TRUSTEE_W {
struct _TRUSTEE_W *pMultipleTrustee;
MULTIPLE_TRUSTEE_OPERATION MultipleTrusteeOperation;
TRUSTEE_FORM TrusteeForm;
TRUSTEE_TYPE TrusteeType;
union {
LPWSTR ptstrName;
SID *pSid;
OBJECTS_AND_SID *pObjectsAndSid;
OBJECTS_AND_NAME_W *pObjectsAndName;
};
LPWCH ptstrName;
} TRUSTEE_W, *PTRUSTEE_W, TRUSTEEW, *PTRUSTEEW;
I'm stuck on 3 main fronts:
My understanding of a C union is that any of the parameters defined within a union may be used to populate the field at that position in the struct. Am I understanding that correctly?
The parameter name ptstrName appears twice, once within the union and once at the end. Each instance has a different type (LPSTR and LPCH). What does this mean, exactly?
I have absolutely no idea what the last line of this typedef (after the final closing brace) means
TRUSTEE_W, *PTRUSTEE_W, TRUSTEEW, *PTRUSTEEW;
I did make an attempt, which did not work (this is assuming x64 architecture, so I'm incrementing the offset by 8 instead of 4; let me know if that's wrong):
struct Trustee {
[FieldOffset(0)] IntPtr pMultipleTrustee;
[FieldOffset(8)] int MultipleTrusteeOperation;
[FieldOffset(16)] int TrusteeForm;
[FieldOffset(24)] int TrusteeType;
[FieldOffset(32)] string ptstrName;
[FieldOffset(32)] IntPtr pSid;
[FieldOffset(32)] IntPtr pObjectsAndSid;
[FieldOffset(32)] IntPtr pObjectsAndName;
};
I was able to find what I needed on pinvoke.net
GetEffectiveRightsFromAcl method signature
TRUSTEE structure
I'm not marking this as the authoritative answer since I don't have the function actually working yet. Return code is: (0x80004005): The parameter is incorrect
I have two c++ functions that I want to DllImport:
bool SendString(const char* pSendStr, long strSize);
bool ReadString(char* pReadStr, long& readStrSize);
There are a lot of articles that write how to DllImport strings.
Alas quite often I see different replies to the same question.
For instance some say if a c++ function returns a a char* and an int* strLen, then some people say I should use a StringBuilder in my dllImport statement and others say return byte[], some have a marshall statement in the dllImport, some don't. Some answers seem needed because of old C# / .net versions.
So the question is: If the dll call from c++ is fairly straightforward, without strange calling conventions, or other strange items, what should the corresponding DllImport functions be if you have output char* and size or input char * and size?
c++ .h
bool SendString(const char* pSendStr, long strSize);
bool ReadString(char* pReadStr, long& readStrSize);
What are the corresponding DllImports? replace the instr and outstr with string? stringbuilder? char[]? byte[]? Is any marshal statement needed?
bool SendString(const char* pSendStr, long strSize);
This function is the easy one. The text is sent from the caller to the callee. The p/invoke is declared like this:
[DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
static extern bool SendString(string SendStr, int Len);
Note that I'm assuming the cdecl calling convention since that is the default for C++ code. And also do note that long in C++ on Windows is 32 bits wide. So it matches int in C#.
When you call the function you need to pass the string and its length. However, the normal convention is for null-terminated strings to be used so the length parameter is not needed. I'd declare the unmanaged function like this:
bool SendString(const char* pSendStr);
And the p/invoke like this:
[DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
static extern bool SendString(string SendStr);
The other function is a little more complex. You've declared it like this:
bool ReadString(char* pReadStr, long& readStrSize);
Here the caller allocates the buffer which is populated by the callee. You can use StringBuilder for the text and let the marshaler do the work for you. The p/invoke is:
[DllImport(dllname, CallingConvention = CallingConvention.Cdecl)]
static extern bool ReadString(StringBuilder ReadStr, ref int Len);
The convention is that you supply the length of the provided buffer. In turn the function will let you know how many characters it wrote. You'd call the function like this:
int len = 256;
StringBuilder ReadStr = new StringBuilder(len);
bool succeeded = ReadString(ReadStr, ref len);
if (succeeded)
{
string str = ReadStr.ToString();
}
As leppie wrote, what you usually want is:
[DllImport(my.dll)]
static extern bool SendString(string sendString, int stringSize);
[DllImport(my.dll)]
static extern bool ReadString(StringBuilder readString, ref int readStringSize);
This would do automatic conversion to Unicode (and back) for you.
If you want precise access to your char*, you would use byte[]. This way no conversion is done and you have more control on what is going on. Usually you won't need that. One use case might by when your char* can include \0 chars.
I want to import a dll in C# which is coded in C language. The following is the formate of the function I want to call.
/*
*ReadAnswer
*#param objectID The answer object ID
*#param answerBuf The answer buffer.This buffer is automatically allocated by
the function.
It is automatically recycled with each call. A call to this
function with an empty answer or a new request will
automatically free the allocated buffer.
*#param answerBufferSize The answer buffer size.This function return the size of the
allocated buffer in this parameter.
*#return 0 if error occurs
1 if success
*/
int ReadAnswer(unsigned short *objectID,
unsigned short **answerBuf, unsighed short *answerBufferSize )
Please help me with it. I'm stucked by this function. Thank you in advance.
The C side-of-things is usually not enough to be sure, but after reading the comment, it should be something like this:
[DllImport("my.dll")]
private extern static int ReadAnswer(ref ushort objectID, out IntPtr answerBuf, out ushort answerBufferSize);
The function should be declared like this:
[DllImport(dllname)]
private extern static int ReadAnswer(
out ushort objectID,
out IntPtr answerBuf,
out ushort answerBufferSize
);
Call it like this:
ushort objectID, answerBufSize;
IntPtr answerBufPtr;
int retval = ReadAnswer(out objectID, out answerBufPtr,
out answerBufSize);
if (retval == 0)
// handle error
ushort[] answerBuf = new ushort[answerBufSize/2];
Marshal.Copy(answerBufPtr, (Int16[])answerBuf, 0, answerBuf.Length);
My assumption is that answerBufSize is the size in bytes.
First of all, there is no unsigned keyword in C#, use ushort.
Second, in order to declare pointers you need to write * after the type, example: ushort*.
And finally to import dlls which are written in C, use this:
[System.Runtime.InteropServices.DllImport(requiredDll)]
extern static int ReadAnswer(ushort* objectID, ushort** answerBuf, out ushort* answerBufferSize);
Also, since the function put the buffer size in answerBufferSize, this parameter requires out.
I've seen the attempted to read or write protected memory error before.
Typically the error shows up when I don't set up the c# struct correctly. I do have other calls working properly but this one is not co-operating.
I'm almost certain that it could be both my function call and the struct that is causing the problem.
C Syntax
int CardTransaction(pTRequest req, char *ProductCodes)
Request structure (I condensed it b/c there were repetitive data types)
typedef struct _cardRequest
{
unsigned short RedemptionNum
long TotalAmount;
unsigned char filler1[257];
char CardNumber[80];
unsigned char cardType;
} TRequest, *pTRequest;
C# function call
[DllImport("card.dll"), CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern int CardTransaction(ref CardRequest cardRequest, [MarshalAs(UnManagedType.LPStr)] StringBuilder productCodes);
ProductCodes is null so I just instantiated a stringbuilder object with nothing in it and passed it through. This is one place I'm thinking could be a problem.
C# structure
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct CardRequest
{
public uint16 RedemptionNum
public int TotalAmount;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
public string filler1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string CardNumber;
public byte cardType;
}
The obvious problem is that the C code uses an aligned struct, but for some reason you have elected to pack the C# struct. Remove the Pack = 1 from the C# code to make the two structures match.
Beyond that the filler array looks more like a byte array than a string. I'd declare it like this:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 257)]
public byte[] filler1;
If you want to pass null to the productCodes parameter, then I expect you can do just that. I cannot recall every doing that myself, but generally when you pass null to a p/invoke, then the marshaller will pass NULL to the native code.
I have a C# project that imports a C dll, the dll has this function:
int primary_read_serial(int handle, int *return_code, int *serial, int length);
I want to get access to the serial parameter. I've actually got it to return one character of the serial parameter, but I'm not really sure what I'm doing and would like to understand what is going, and of course get it working.
So, I'm very sure that the dll is being accessed, other functions without pointers work fine. How do I handle pointers? Do I have to marshal it? Maybe I have to have a fixed place to put the data it?
An explanation would be great.
Thanks!
Richard
You will have to use an IntPtr and Marshal that IntPtr into whatever C# structure you want to put it in. In your case you will have to marshal it to an int[].
This is done in several steps:
Allocate an unmanaged handle
Call the function with the unamanged array
Convert the array to managed byte array
Convert byte array to int array
Release unmanaged handle
That code should give you an idea:
// The import declaration
[DllImport("Library.dll")]
public static extern int primary_read_serial(int, ref int, IntPtr, int) ;
// Allocate unmanaged buffer
IntPtr serial_ptr = Marshal.AllocHGlobal(length * sizeof(int));
try
{
// Call unmanaged function
int return_code;
int result = primary_read_serial(handle, ref return_code, serial_ptr, length);
// Safely marshal unmanaged buffer to byte[]
byte[] bytes = new byte[length * sizeof(int)];
Marshal.Copy(serial_ptr, bytes, 0, length);
// Converter to int[]
int[] ints = new int[length];
for (int i = 0; i < length; i++)
{
ints[i] = BitConverter.ToInt32(bytes, i * sizeof(int));
}
}
finally
{
Marshal.FreeHGlobal(serial_ptr);
}
Don't forget the try-finally, or you will risk leaking the unmanaged handle.
If I understand what you're trying to do, this should work for you.
unsafe
{
int length = 1024; // Length of data to read.
fixed (byte* data = new byte[length]) // Pins array to stack so a pointer can be retrieved.
{
int returnCode;
int retValue = primary_read_serial(0, &returnCode, data, length);
// At this point, `data` should contain all the read data.
}
}
JaredPar gives you the easy way to do it however, which is just to change your external function declaration so that C# does the marshalling for you in the background.
Hopefully this gives you an idea of what's happening at a slightly lower level anyway.
When writing your P/invoke declaration of that function, try using keyword ref for the pointer parameters like this:
[DllImport("YouDll.dll", EntryPoint = "primary_read_serial")]
public static extern int primary_read_serial(int, ref int, ref int, int) ;
I'm not sure if you need to specify the parameters' name in C#. And remember, when calling that method, you will also have to use ref in the arguments you're passing by reference.