C++ API and PInvoke in C# - c#

I got the "System.AccessViolationException" when I am trying to call the method from C++ API. In resultXML_out I got properly formated XML with data returned as exepted but the exception is raised (exactly on this method) and cannot handle this even with try catch block. I assume that I should declare somehow the memory for resultXML_out but I don't know how to do this.
Here is C++ API method declaration:
SW_ErrCode SW_GetMyUserInfo (SW_LoginID lh, SW_XML *resultXML_out)
Declaration of SW_XML:
const char * SW_XML
Here is my code:
[StructLayout(LayoutKind.Sequential)]
public struct SW_LoginID
{
public int loginId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SW_XML
{
public string xml;
}
[DllImport("sw_api.dll")]
[HandleProcessCorruptedStateExceptionsAttribute]
public static extern SW_ErrCode SW_GetMyUserInfo(SW_LoginID sh, out SW_XML resultXML_out);
And here is the call of this method:
SW_XML resultXML_out = new SW_XML();
resultXML_out.xml = "";
SW_ErrCode d = SW_GetMyUserInfo(login, out resultXML_out);
In API I found also such a method. But I don't know how to properly use it (or even if it is necessary):
char* SW_AllocateString (unsigned size)
But after I passed e.g. 1000 the program terminates without even an exception... Here is description of this function in API Documentation:
Allocate a string.
Returns a string allocated by the API

Related

How to fix 'Method's type signature is not PInvoke compatible' error in C#

All I need to know is how to return a struct from a PInvoke in C++ that has the following struct. For the moment I can deal with it being blank and I just want to know how to return the struct under the conditions set in the code.
I've tried with with the entire struct that I need to return and isolated each part of struct to know which part is giving me the issue (which will be made apparent in the code provided).
I've tried the same method by wanting to return a few integers within the struct which works fine. (Tried to make this bold using ***, ___)
//.header file
typedef struct { //Defintion of my struct in C++
TCHAR msg[256];
}testTCHAR;
//.cpp file
extern "C" {
__declspec(dllexport) testTCHAR* (_stdcall TestChar(testTCHAR* AR))
{
AR->msg;
return AR;
}
}
In my C# I Call the .dll as:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void testChar(testTCHAR AR);
[DllImport("C:\\Users\\jch\\source\\repos\\FlatPanelSensor\\x64\\Debug\\VADAV_AcqS.dll", EntryPoint = "TestCallBackChar", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern testTCHAR TestCallBackChar([MarshalAs(UnmanagedType.FunctionPtr)] testChar call);
//Struct
public struct testTCHAR
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string rMsg; //I assume the error should be fixed here
but to what exactly I don't know.
}
//Defining the callback
testChar tChar =
(test) =>
{
//As far as I'm aware this part can be left blank
as I followed a tutorial online
};
testTCHAR returned = TestCallBackChar(tChar); //This is where the error
happens
I just need to return the struct, preferably with a value attached to it.
The error I get is 'Method's type signature is not PInvoke compatible.' Which is in the title, but I'm covering all basis.
If you need anymore information about this please ask away and I should be able to provide.
Since the testTCHAR type contains managed references, you cannot use a pointer as a part of signature (which you didn't), but also it does not make sense to pass it by value (this is why the runtime produces the error you see).
You need to change your signatures so that it's apparent that you want to pass a pointer into the native method:
public delegate void testChar(IntPtr data);
public unsafe static extern IntPtr TestCallBackChar([MarshalAs(UnmanagedType.FunctionPtr)] testChar call);
When calling you need to explicitly marshal structures to the managed equivalent (and vice versa):
testChar tChar =
(inputPtr) =>
{
testTCHAR input = (testTCHAR)Marshal.PtrToStructure(inputPtr, typeof(testTCHAR));
};
IntPtr returnedPtr = TestCallBackChar(tChar);
testTCHAR returned = (testTCHAR)Marshal.PtrToStructure(returnedPtr, typeof(testTCHAR));
Additionally, I guess there is a mismatch between C++ and C# signature and method name.

How do I get P/Invoking a pointer to a pointer to a struct working from a function parameter?

I'm trying to P/invoke this in such a way I should be able to access the const char* members of this struct from C# (system, game, song, copyright, etc).
This is the struct as it is defined in the C++ header here: gme.h starting at line 79
struct gme_info_t
{
/* times in milliseconds; -1 if unknown */
int length; /* total length, if file specifies it */
int intro_length; /* length of song up to looping section */
int loop_length; /* length of looping section */
/* Length if available, otherwise intro_length+loop_length*2 if available,
otherwise a default of 150000 (2.5 minutes). */
int play_length;
int i4,i5,i6,i7,i8,i9,i10,i11,i12,i13,i14,i15; /* reserved */
/* empty string ("") if not available */
const char* system;
const char* game;
const char* song;
const char* author;
const char* copyright;
const char* comment;
const char* dumper;
const char *s7,*s8,*s9,*s10,*s11,*s12,*s13,*s14,*s15; /* reserved */
};
In my C# code, I have modeled this struct as follows:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct gme_info_t
{
public int length;
public int introLength;
public int loopLength;
public int playLength;
public int i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15;
public string system;
public string game;
public string song;
public string author;
public string copyright;
public string comment;
public string dumper;
public string s7, s8, s9, s10, s11, s12, s13, s14, s15;
}
Now, the function I'm calling is one that I have P/Invoked which has the C++ prototype as follows:
gme_err_t gme_track_info( Music_Emu const* me, gme_info_t** out, int track )
where gme_err_t is a const char*
(see gme.h, line 74, if you want a direct look at it)
(see gme.cpp, line 252 for its definition)
So, the function without all of its typedefs is as follows:
const char* gme_track_info( Music_Emu const* me, gme_info_t** out, int track )
The way this function works is that when called with a valid Music_Emu and a valid track,
the result is info about the music track which is assigned to the parameter 'out'.
The const char* being returned is basically for when errors occur, so it's not the main focus. The 'out' parameter is.
I have defined the P/Invoke for this function in C# as follows:
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern string gme_track_info(IntPtr emuHandle, out gme_info_t trackInfo, int track);
Here is my code currently for attempting to read the copyright string in that struct.
static void Main()
{
// Initialize the MusicEmu reference first (this works fine).
IntPtr emuRef;
string initEmuRef = NativeMethods.gme_open_file("Adventure Island 4.nsf", out emuRef, 48000);
Console.WriteLine("Error Message (if any): " + initEmuRef);
// Now get the track info.
gme_info_t trackInfo;
NativeMethods.gme_track_info(emuRef, out trackInfo, 0);
Console.WriteLine("Copyright: " + trackInfo.copyright); // I get an empty string. When checked with a different NSF reader it prints "1994 Hudson Soft."
// Keep console window up.
Console.ReadLine();
}
Any help would be appreciated. I've tried for roughly 4 hours to make this work. I've looked all around StackOverflow (and the net in general) for possible solutions, but I haven't found any that were close to this sort of question. Most other problems were about a pointer to a pointer to a struct of arrays and such, which isn't very helpful at all for this case.
If any other information is needed, just ask, I'd be happy to provide it.
Your trackInfo parameter should be an out IntPtr. Then you can marshal it to a C# struct with the Marshal.PtrToStructure method.
IntPtr trackInfoPtr;
NativeMethods.gme_track_info(emuRef, out trackInfoPtr);
gme_info_t trackInfo = (gme_info_t)Marshal.PtrToStructure(trackInfoPtr, typeof(gme_info_t));

Passing String from Native C++ DLL to C# App

I have written a DLL in C++. One of the functions writes to a character array.
C++ Function
EXPORT int xmain(int argc, char argv[], char argv2[])
{
char pTypeName[4096];
...
//Other pTypeName ends up populated with "Portable Network Graphics"
//This code verifies that pTypeName is populated with what I think it is:
char szBuff[64];
sprintf(szBuff, pTypeName, 0);
MessageBoxA(NULL, szBuff, szBuff, MB_OK);
//The caption and title are "Portable Network Graphics"
...
//Here, I attempt to copy the value in pTypeName to parameter 3.
sprintf(argv2, szBuff, 0);
return ret;
}
C# Import
//I believe I have to use CharSet.Ansi because by the C++ code uses char[],
[DllImport("FirstDll.dll", CharSet=CharSet.Ansi)]
public static extern int xmain(int argc, string argv, ref string zzz);
C# Function
private void button2_Click(object sender, EventArgs e)
{
string zzz = "";
int xxx = xmain(2, #"C:\hhh.bmp", ref zzz);
MessageBox.Show(zzz);
//The message box displays
//MessageBox.Show displays "IstuÈst¼ÓstÄstlÄstwÄstiÑstõÖstwÍst\
// aÖst[ÖstÃÏst¯ÄstÐstòÄstŽÐstÅstpÅstOleMainThreadWndClass"
}
I have attempted to pass a parameter from C# by reference and have the C++ DLL populate the parameter. Even though I have verified that the value is correct in the DLL, gibberish gets passed to the C# application.
What can I do to write the correct string value to the C# string?
Use a StringBuilder to pass a character array that native code can fill in (see Fixed-Length String Buffers).
Declare the function:
[DllImport("FirstDll.dll", CharSet=CharSet.Ansi)]
public static extern int xmain(int argc, string argv, StringBuilder argv2);
Use it:
// allocate a StringBuilder with enough space; if it is too small,
// the native code will corrupt memory
StringBuilder sb = new StringBuilder(4096);
xmain(2, #"C:\hhh.bmp", sb);
string argv2 = sb.ToString();
Give some other information to the DLLImport call. Look at the following example of my own:
[DllImport("tcpipNexIbnk.dll", EntryPoint = "SendData", CallingConvention = CallingConvention.Cdecl)]
public static extern int Send([MarshalAs(UnmanagedType.LPWStr)]string message);
Notice two things, the CallingConvention parameter:
CallingConvention = CallingConvention.Cdecl)
Use that as it is.
And then just behind the c# string type, you can play with the different Unmanaged types using the MarshalAS instruction, that will cast your C# string parameter to the native string type you have in your c++ program:
public static extern int Send([MarshalAs(UnmanagedType.LPWStr)]string message);
Hope it helps.

C# dll call not passing char* to C++ MFC regular dll

I have a C++ MFC regular DLL I am calling with the following:
public static class Access3rdPartyDLL
{
public static string FilePath;
[DllImport("3rdparty.dll")]
// I have also tried LPWStr
public static extern long Download([MarshalAs(UnmanagedType.LPStr)]string sDownloadFile,
int iDeviceNum
...);
public static long DownloadToDevice()
{
long result;
string FilePath = "C:\\myfile.txt"
result = Download(FilePath, 1, ...);
// check if success or error
if(result > 0)
...
}
}
I get an error back from the DLL saying "File: 'C:\myfile.txt' not found. But its there...
I have also tried using StringBuilder but this also fails.
Could this be a problem with the DLL or am I doing something wrong?
I found this current code here: SO: equivalent char* in C#
EDIT: I have done this in C++ before and this code works:
extern "C" __declspec(dllimport) HRESULT __stdcall Download(char* sDownloadFile, int ...
which I call with:
HRESULT result = Download(file_for_download, 1, .. // where file_for_download is a char*
The only thing wrong with the P/invoke is that you are using C# long which is 64 bits, but an HRESULT is only 32 bits.
You have matching calling conventions, default marshalling for managed string is char* on the unmanaged side.
Mismatching return value size would not explain why your C# code receives a string message File: 'C:\myfile.txt' not found so your main problem most likely lies in the code that you haven't shown us.
I don't see any reason why the following wouldn't work in this simple scenario:
[DllImport( "3rdparty.dll", CharSet = CharSet.Ansi )]
static extern long Download(string sDownloadFile, int iDeviceNum, ...)
long result = Download("C:\\myfile.txt", 1, ...);

C#: Object with custom marshaller not containing data after PInvoke call

I am having a problem with PInvoking some WinAPI functions that accept WAVEFORMATEX structures as parameters. Since the length of the WAVEFORMATEX structure can vary, I implemented a WaveFormatEX class that is marshalled by a custom marshaller class (which implements ICustmoMarshaller). This is following an example provided by Aaron Lerch in his Blog (Part 1, Part 2), but with a few modifications from my side.
When I call the API function from my code, the methods MarshalManagedToNative and MarshalNativeToManaged of the custom marshaller are called, and at the end of MarshalNativeToManaged, the managed object contains the correct values. But when the execution returns to my calling code, the WaveFormatEx object does not contain the values read during the API call.
So the question is: Why does the data that is correctly marshalled back from native to managed not show up in my WaveFormatEx object after the native API call? What am I doing wrong here?
Edit:
To clarify, the function call succeeds, so does the marshalling of the WaveFormatEx object back to managed code. Just when the execution returns from the marshalling method to the scope from where the method was called, the WaveFormatEx object that was declared in that calling scope does not contain the result data.
Here are the function prototype and the WaveFormatEx class:
[DllImport("avifil32.dll")]
public static extern int AVIStreamReadFormat(
int Stream,
int Position,
[In, Out, MarshalAs(UnmanagedType.CustomMarshaler,
MarshalTypeRef = typeof(WaveFormatExMarshaler))]
WaveFormatEx Format,
ref int Size
);
[StructLayout(LayoutKind.Sequential)]
public class WaveFormatEx
{
public int FormatTag;
public short Channels;
public int SamplesPerSec;
public int AvgBytesPerSec;
public short BlockAlign;
public short BitsPerSample;
public short Size;
public byte[] AdditionalData;
public WaveFormatEx(short AdditionalDataSize)
{
WaveFormat.Size = AdditionalDataSize;
AdditionalData = new byte[AdditionalDataSize];
}
}
The marshalling methods look like this:
public object MarshalNativeToManaged(System.IntPtr NativeData)
{
WaveFormatEx ManagedObject = new WaveFormatEx(0);
ManagedObject = (WaveFormatEx)Marshal.PtrToStructure(
NativeData, typeof(WaveFormatEx));
ManagedObject.AdditionalData = new byte[ManagedObject.Size];
// If there is extra data, marshal it
if (ManagedObject.WaveFormat.Size > 0)
{
NativeData = new IntPtr(
NativeData.ToInt32() +
Marshal.SizeOf(typeof(WaveFormatEx)));
ManagedObject.AdditionalData = new byte[ManagedObject.WaveFormat.Size];
Marshal.Copy(NativeData, ManagedObject.AdditionalData, 0,
ManagedObject.WaveFormat.Size);
}
return ManagedObject;
}
public System.IntPtr MarshalManagedToNative(object Object)
{
WaveFormatEx ManagedObject = (WaveFormatEx)Object;
IntPtr NativeStructure = Marshal.AllocHGlobal(
GetNativeDataSize(ManagedObject) + ManagedObject.WaveFormat.Size);
Marshal.StructureToPtr(ManagedObject, NativeStructure, false);
// Marshal extra data
if (ManagedObject.WaveFormat.Size > 0)
{
IntPtr dataPtr = new IntPtr(NativeStructure.ToInt32()
+ Marshal.SizeOf(typeof(WaveFormatEx)));
Marshal.Copy(ManagedObject.AdditionalData, 0, dataPtr, Math.Min(
ManagedObject.WaveFormat.Size,
ManagedObject.AdditionalData.Length));
}
return NativeStructure;
}
And this is my calling code:
WaveFormatEx test = new WaveFormatEx(100);
int Size = System.Runtime.InteropServices.Marshal.SizeOf(test);
// After this call, test.FormatTag should be set to 1 (PCM audio),
// but it is still 0, as well as all the other members
int Result = Avi.AVIStreamReadFormat(AudioStream, 0, test, ref Size);
There are several mistakes in the code and the declarations that prevents this code from working on a 64-bit operating system. Be sure to set the Platform Target to x86.
Are you sure the native function actually returns data? What is the Result return value? A non-zero value indicates failure.
The proper way to call this function is to call it twice. First with the lpFormat argument set to null (IntPtr.Zero) so it tells you how large a buffer it needs (returned by lpbcFormat). Then you create the buffer and call it again.
Instead of a custom marshaller, I would just create the buffer with Marshal.AllocHGobal after the first call and pass the IntPtr it returns as the lpFormat argument in the second call. Then, iff you get a success return code, use Marshal.PtrToStructure to write the WaveFormatEx. And Marshal.Copy to get the additional data.
Fwiw, using ref causes the P/Invoke marshaller to pass a WaveFormatEx** to the function but it expects a WaveFormatEx*. Which will cause it to overwrite data in the garbage collected heap, destroying its internal format. A kaboom is next when the CLR notices this.
Check out the NAudio project as a good alternative for doing this yourself.

Categories