P/Invoke mapping for complicated C function (const void *text) - c#

I'm trying to create a wrapper for the eSpeak speech synthesis library, and so far I've managed to make simple methods like single character synthesis work. However, the main synthesis method is too complex for me to wrap my head around. Does anyone have a good suggestion for a C# mapping of this method signature? (Especially the first variable!)
ESPEAK_API espeak_ERROR espeak_Synth(const void *text,
size_t size,
unsigned int position,
espeak_POSITION_TYPE position_type,
unsigned int end_position,
unsigned int flags,
unsigned int* unique_identifier,
void* user_data);
espeak_POSITION_TYPE I've mapped to an enum and I think it works. The following is the description of the text variable if that is helpful:
text: The text to be spoken, terminated by a zero character. It may be either 8-bit characters,
wide characters (wchar_t), or UTF8 encoding. Which of these is determined by the "flags"
parameter.

It depends.
assuming espeak_ERROR and espeak_POSITION_TYPE are integers:
[DllImport("...", CharSet=CharSet.Ansi)
static extern int espeak_Synth(string text,
int text_length,
int position,
int position_type,
int end_position,
int flags,
IntPtr unique_identifier,
IntPtr user_data);
this should work, if you pass to flags, that text is 8bit (and not UTF).
[DllImport("...")
static extern int espeak_Synth(IntPtr text,
int text_length,
int position,
int position_type,
int end_position,
int flags,
IntPtr unique_identifier,
IntPtr user_data);
// vv this can be some parameter
byte[] dat = Encoding.UTF8.GetBytes("some utf-8 string" + "\0"); // note the trailing '\0'!
IntPtr pnt = Marshal.AllocHGlobal(dat.Length);
try {
Marshal.Copy(dat, 0, pnt, dat.Length);
espeak_Synth(pnt, dat.Length, .....); // assuming the text_length is in bytes. If in octets, pass the string's length
}
finally
{
Marshal.FreeHGlobal(pnt);
}
if you want to pass UTF-8 string.

Related

Which way of marshaling is correct for this C code?

I have two different implementation of marshaling C code into C#.
First of all the C code is:
int GetVersion(char * pVer, size_t * piSize);
My first attempt in C#(I don't show the DllImport part):
static extern int GetVersion( byte[] ver, ref UIntPtr size );
And the other attempt in C#:
static extern int GetVersion([MarshalAs(UnmanagedType.LPStr)] StringBuilder ver, ref ulong size);
I came through these from different examples in net. But I cannot conclude which one is correct.
Bare bones: we can always do this
static extern int GetVersion(IntPtr ver, ref UIntPtr size );
I would never write this because I don't like what this generates for A->W conversion:
static extern int GetVersion([MarshalAs(UnmanagedType.LPStr)] StringBuilder ver, ref UIntPtr size);
Also, if this is a true counted string, the P/Invoke code doesn't work as it wants a 0 after the end.
I would normally write
static extern int GetVersion( byte[] ver, ref UIntPtr size );
in which you must first create ver at the maximum size. You can then convert ver to string using the stuff in System.Text.Encoding. GetVersion() almost certainly passes a constant back, so you can hardcode the correct encoding (probably ASCII anyway).
There's a pretty good chance you're missing [DllImport(..., CallingConvention=CallingConvention.Cdecl)] causing you some memory corruption, but I can't actually tell.
Mistake: don't use ulong for size_t. size_t is UIntPtr on all supported platforms.
Comment suggests a completely different signature GetDevInfo(int Index, DevInfo * pdevInfo); This is a different thing altogether and there's clearly one best choice:
const int IDLength = 100;
[StructLayout(LayoutKind.Sequential)]
struct DeviceInfo {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = IDLength)]
byte[] ID;
}
static extern /* I don't know what's here because missing from comment */
GetDevInfo(int index, ref strcut DeviceInfo info);
string GetInfo(int index)
{
var info = new DeviceInfo();
info.ID = new byte[IDLength];
GetDevInfo(info.ID);
return System.Text.Encoding.ASCII.GetString(info.ID, 0, strlen(info.ID));
}
int strlen(byte[] bytes)
{
int i;
for (i = 0; i < bytes.Length && bytes[i] != 0; i++)
;
return i;
}
````

DLLImport c++ functions with char* as input or as output parameters

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.

Calling a C++ function that takes a char pointer from C#

I cant figure out how to pass a char * to this C++ function from C#.
extern "C" __declspec(dllexport)
unsigned int extractSegment(char * startPoint, unsigned int sizeToExtract)
{
//do stuff
shared_ptr<std::vector<char>> content(new std::vector<char>(startPoint,startPoint+sizeToExtract));
//do more stuff
return content->size();
}
This function is used to read a segment from a file and do some binary operations on it (why i use the vector of chars). The startPoint is the start of the segment i want to read. I cannot change this function.
In C# I tried reading the file into a byte[] array and defining the DllImport to use StringBuilder where the export had char *. I tried to call it as such:
byte[] arr = File.ReadAllBytes(filename);
StringBuilder sb = new StringBuilder(System.Text.Encoding.Unicode.GetString(arr, startPoint,arr.Length - startPoiunt));
extractSegment(sb,200);
This resulted in SEHException.
A char * can have several different meanings. In your case it appears to be a preallocated and filled array of bytes that is used as an input parameter for the extractSegment function.
The equivalent C# method would then take a byte[] parameter, i.e.
[DllImport(...)]
public static extern int extractSegment(byte[] startPoint, uint sizeToExtract);
I use byte[] because you mention binary operations, however if it is actually a string then you can also marshal it as such, setting the correct encoding in the DllImport attribute.
And just for further information, other possible options for char * that I can think of right now would be ref byte, out byte, ref char, out char or string. StringBuilder on the other hand is used when the calling function allocates and returns a string, i.e. char **.

Import C dll in C# , how to convert short** type

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.

How to marshal unmanaged buffer of packed structs in c#

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.

Categories

Resources