Equivalent of C++ "this" pointer in C# - c#

I'm trying to translate a successfully working C++ program to C# and I'm currently stuck at initializing the audio output device. I would like to know the equivalent of 'this' pointer in C#.
Below is the working C++ code:
DWORD WINAPI SCFStartOutSound(SCFHANDLE sohOwnerHandle, SCFHANDLE *pscfHandle, SCFEventOutSoundCallback lpfnEventCallback, BSTR FileName);
CCComBSTR FileName = "Out_DxSpeaker.dll";
HANDLE OutSoundHandle = NULL;
SCFStartOutSound(this, &OutSoundHandle, OnSCFEventOutSoundCallback, FileName);
Class:
CTestAppDlg::CTestAppDlg(CWnd* pParent /*=NULL*/)
: CDialog(CTestAppDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
Typedef:
typedef HANDLE SCFHANDLE;
Result: The audio output device is properly initialized
Initialized output device
Now here is the my C# translated code:
[DllImport(DllFileName.dll)]
unsafe public static extern uint SCFStartOutSound(IntPtr OwnerHandle,IntPtr* soundoutHandle, SCFEventOutSoundCallback EventCallback, [MarshalAs(UnmanagedType.BStr)] string fileName);
unsafe
{
IntPtr soundOutPtr;
uint res = SCFStartOutSound(this.Handle, &soundOutPtr, onSoundOutEventCallback,"Out_DxSpeaker.dll");
MessageBox.Show(res.ToString());
}
This code results to a System.AccessViolationException.
I know that 'this' in C++ is a pointer that holds the memory address of the current object while 'this' in C# refers to the current instance of the class .

Related

Passing a return char* from C++ to C# with DllImport

I am a newbie on both C# WPF and C++.
Recently, I got an external .dll, which returns a char*, and I want to receive the return value in C# by using DllImport. Then, using str.Split(';') to separate the characters. To the purpose, I created a button to show the first character in the string I split on a label when I click the button.
Therefore, I use an IntPtr to receive a char*, which is from C++ .dll, and call Marshal.PtrToStringAnsi() to turn it into a string. However, when I execute the code, it sometimes works but sometimes crushes. Then error code always shows
Unhandled exception at 0x00007FFC06839269 (ntdll.dll) in UITest.exe: 0xC0000374: heap corruption (parameters: 0x00007FFC068A27F0).
I thought my code is reasonable and I couldn't find root causes. Can anyone help me? Thanks!
The below shows the content in .dll and also the code I used in C#.
C++ code in Dlltest.h:
#define DLL_EXPORT extern "C" __declspec(dllexport)
char* getRbtData = nullptr;
DLL_EXPORT char* func_getRbtData();
C++ code in Dlltest.cpp:
char* func_getRbtData()
{
getRbtData = new char(128);
memset(getRbtData, 0, strlen(getRbtData));
char* _getRbtData = "1.0;23.0;55.0;91.0;594.0;";
memcpy(getRbtData, _getRbtData, strlen(_getRbtData));
return getRbtData;
};
C# code in UITest.xaml.cs:
[DllImport("DllTest.dll",EntryPoint = "func_getRbtData", CharSet = CharSet.Ansi)]
public static extern IntPtr func_getRbtData();
string[] words;
private void btn_test_Click(object sender, RoutedEventArgs e)
{
IntPtr intptr = func_getRbtData();
string str = Marshal.PtrToStringAnsi(intptr);
words = str.Split(';');
lb_content.Content = words[1];
}
There are several problems with your code.
On the C++ side, your DLL function is implemented all wrong:
getRbtData = new char(128);
You are allocating a single char whose value is 128, not an array of 128 chars. You need to use new char[128] instead for that.
memset(getRbtData, 0, strlen(getRbtData));
getRbtData is not a pointer to a null-terminated string, so strlen(getRbtData) is undefined behavior. It reads into surrounding memory while calculating the length until it finds a random 0x00 byte in memory.
And then the subsequent memset() into getRbtData will overwrite that surrounding memory. If it doesn't just crash outright.
char* _getRbtData = "1.0;23.0;55.0;91.0;594.0;";
Prior to C++11, this assignment is OK but discouraged. In C++11 and later, this assignment is actually illegal and won't compile.
String literals are read-only data, so you need to use const char instead of char in your pointer type. You should do that even in older compilers.
memcpy(getRbtData, _getRbtData, strlen(_getRbtData));
strlen(_getRbtData) is OK since _getRbtData is a pointer to a null-terminated string.
However, since getRbtData is not allocated with enough memory to receive all of the copied chars, memcpy() into getRbtData is also undefined behavior and will trash memory, if not crash outright.
return getRbtData;
This is OK to pass the pointer to C#.
However, since the memory is being allocated with new (better, new[]), it needs to be freed with delete (delete[]), which you are not doing. So you are leaking the memory.
Marshal.PtrToStringAnsi() on the C# side will not (and cannot) free new'ed memory for you. So your C# code will need to pass the pointer back to the DLL so it can delete the memory properly.
Otherwise, you will need to allocate the memory using the Win32 API LocalAlloc() or CoTaskMemAlloc() function so that the Marshal class can be used on the C# side to free the memory directly without passing it back to the DLL at all.
On the C# side, you are using the wrong calling convention on your DllImport statement. The default is StdCall for compatibility with most Win32 API functions. But your DLL function is not specifying any calling convention at all. Most C/C++ compilers will default to __cdecl unless configured differently.
With that said, try this instead:
Dlltest.h
#define DLL_EXPORT extern "C" __declspec(dllexport)
DLL_EXPORT char* func_getRbtData();
DLL_EXPORT void func_freeRbtData(char*);
Dlltest.cpp
char* func_getRbtData()
{
const char* _getRbtData = "1.0;23.0;55.0;91.0;594.0;";
int len = strlen(_getRbtData);
char *getRbtData = new char[len+1];
// alternatively:
/*
char *getRbtData = (char*) LocalAlloc(LMEM_FIXED, len+1);
if (!getRbtData) return NULL;
*/
memcpy(getRbtData, _getRbtData, len+1);
return getRbtData;
}
void func_freeRbtData(char *p)
{
delete[] p;
// alternatively:
// LocalFree((HLOCAL)p);
}
UITest.xaml.cs
[DllImport("DllTest.dll", EntryPoint = "func_getRbtData", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr func_getRbtData();
[DllImport("DllTest.dll", EntryPoint = "func_freeRbtData", CallingConvention = CallingConvention.Cdecl)]
public static extern void func_freeRbtData(IntPtr p);
string[] words;
private void btn_test_Click(object sender, RoutedEventArgs e)
{
IntPtr intptr = func_getRbtData();
string str = Marshal.PtrToStringAnsi(intptr);
func_freeRbtData(intptr);
// alternatively:
// Marshal.FreeHGlobal(intptr);
words = str.Split(';');
lb_content.Content = words[1];
}
new char(128) returns a pointer to one character, with initial value 128.
I could tell you how to allocate 128 characters, but the chief problem with that is that you can't clean it up so that's not a useful answer. Check the existing questions about returning a string to C#.

Initializing a C# IntPtr to accept data from unmanaged C++ DLL?

I have an exported function in unmanaged C++ code that expects a pointer to a BStr, where it will write some text data (258 bytes, max)
extern "C" __declspec(dllexport)
int CppFunc(BSTR *data)
{ ... }
I want that data as a string.
This works
[DllImport( ... CallingConvention = CallingConvention.Cdecl)]
public static extern int CppFunc([MarshalAs(UnmanagedType.BStr)] ref string data);
but it creates a memory leak.
I assume what I should be doing is creating and passing an IntPtr, then Marshal out the Bstr as a string, then free the IntPtr:
IntPtr p = Marshal.AllocHGlobal(512);
CppFunction(p);
string data = Marshal.PtrToStringBSTR(p);
Marshal.FreeHGlobal(p) ;
The problem is, with that code, I get a System.AccessViolationException on the call into Marshal.PtrToStringBSTR(p).
What am I doing wrong?!
The first line of the Remarks for Marshal.PtrToStringBSTR is
Call this method only on strings that were allocated with the unmanaged SysAllocString and SysAllocStringLen functions.
Which is probably where your crash came from.
Add to this your C++ function expects BSTR* (effectively a pointer to a pointer to the first character of data in the string), but you pass it a pointer to data.
Remember that a BSTR has a special structure: it starts with 4 bytes of length, then data, then a null. The pointer points to the first character of data. So Marshal.PtrToStringBSTR is looking backwards from the pointer to find the length of the string - but that isn't memory which was allocated by Marshal.AllocHGlobal.
It could be that your C++ function does something like *data = ....AllocSysString(); - that is, it never reads the string it's given, but instead assigns the pointer to a string which it allocates.
In that case, you probably want something like:
[DllImport( ... CallingConvention = CallingConvention.Cdecl)]
public static extern int CppFunc(out IntPtr data);
...
CppFunc(out IntPtr p);
string data = Marshal.PtrToStringBSTR(p);
Marshal.FreeBSTR(p) ;
Here, we pass it a pointer to a pointer. The C++ function re-assigns the pointer to point to the first character of data in a BSTR, and we use that to deserialize the BSTR, and then free it (using a method which knows how to free BSTRs).
If this isn't the case, it's unclear why your C++ function takes a BSTR* (as opposed to a BSTR), and what it does with it. I think we need to see that before much else can be said.
If your C++ function took a BSTR instead (remember that BSTR is itself a pointer), then what you should be doing is using a StringBuilder (with a particular initial capacity) - the marshalling layer turns that into a pointer the C++ code can write to, and then you can turn the StringBuilder into a string.
[DllImport( ... CallingConvention = CallingConvention.Cdecl)]
public static extern int CppFunc([MarshalAs(UnmanagedType.BStr)] StringBuilder data);
...
var data = new StringBuilder(512);
CppFunction(data);
string result = data.ToString();

DLLimport using and Error

I have a c++ native dll file and I should call some functions from Dll by c#
the c++ function that I should call is
extern NCSError NCS_CALL NCSOpenFileViewA(const char *szUrlPath, NCSFileView **ppNCSFileView,NCSReadStatus (*pRefreshCallback)(NCSFileView *pNCSFileView));
NCSFileView object is
typedef struct NCSFileViewStruct NCSFileView;
and working example c++ code is
NCS::CApplication App
NCSCompressClient *pClient;
char *szInputFilename = "C:\\testdata\\RGB_8bit.ecw";
NCSFileView *pNCSFileView;
NCSFileInfo *pNCSFileInfo;
NCSFileMetaData *pNCSFileMeta;
NCSError eError;
NCSInit();
eError = NCSOpenFileViewA(szInputFilename, &pNCSFileView, NULL);
if (eError != NCS_SUCCESS) {
ReportError("Could not open view for file:%s, Error = %s",
szInputFilename, NCSGetErrorText(eError));
exit(1);
}`
I made a c# code like this
[DllImport("C:\\NCSEcw.dll",CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr NCSOpenFileViewA ([MarshalAs(UnmanagedType.LPStr)] string path,
IntPtr a,
IntPtr c);
but i get en error like this
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
so, can you help me to write wrapper?

Marshaling a pointer to a pointer

Problem:
I have a test application that is looking for a scanner attached to a PC. The European manufacturer supplies an SDK written in C++ and provides some header files and other information. Their C++ API populates a structure as shown below. My concern is de-referencing (Marshaling) the returned char** inside of that structure into a set of strings.
//
//The struct, as per the manufacturer's C++ header file:
//
typedef struct {
int count;
char** serialNumbers;
} ScannerList;
//
// My interpretation of the struct
//
struct ScannerList
{
public int count;
public IntPtr serialNumbers;
}
//
// C++ API Method Signature:
//
bool SCN_GetScannerList( ScannerList** scannerList );
//
// My DLLImport:
//
[DllImport(#"C:\ScannerSDK\Scanner.dll", CharSet = CharSet.Unicode)]
public static extern bool SCN_GetScannerList(out IntPtr scannerList);
Inside my C# wrapper class, I have several classes that wrap each specific set of Scanner API calls. For this method, I'm using the ScannerNative class that the DllImport resides in, along with the struct. This is the method charged with summoning the list:
private object GetScanlistFromDLL()
{
// our managed code return object - class instead of struct
ScannerNative.ScannerListSafe safeList = new ScannerNative.ScannerListSafe();
// IntPtr to give to the API
IntPtr ptr = new IntPtr();
// unmanaged code struct
ScannerNative.ScannerList scanList = new ScannerNative.ScannerList();
// Call the API with our IntPtr
bool success = ScannerNative.GetScannerList(out ptr);
if(success)
{
// Map the pointer to the type of structure from the API call
scanList = (ScannerNative.ScannerList)Marshal.PtrToStructure(ptr, typeof(ScannerNative.ScannerList));
// copy the unsafe struct members to our safe class
safeList.count = scanList.count;
//Problem - the marshaling returns a string full of garbage
//likely because char** is an array of char* ...
//Not sure how to Marshal a pointer to a pointer to a string :-\
safeList.serialNumbers = Marshal.PtrToStringUni(scanList.serialNumbers);
}
else
{
return null;
}
//
// API call to release unmanaged scanner list ..
//
return safeList;
}
Results:
I do get what appears to be a valid structure back from the API call
bool success = ScannerNative.GetScannerList(out ptr);
The subsequent casting in the if(success) block:
if(success)
{
// Map the pointer to the type of structure from the API call
scanList = (ScannerNative.ScannerList)Marshal.PtrToStructure(ptr, typeof(ScannerNative.ScannerList));
This works all the way until we get to our IntPtr stored in the scanList.serialNumbers member, which is holding a reference to the char** from the API struct.
scanList.count shows a correct value of '1' (when the scanner is attached) but I'm not able to de-ref the char** held in the scanList.serialNumbers member.
I've tried attacking this in a couple ways, but nothing is coming out and either I'm using the wrong search engines, or trying to find "c# DllImport Char** Marshal" and variants is asking the wrong question.

Passing list of struct to C++ DLL from C# Application

I am trying to pass struct as a parameter from C# application to the C++ MFC DLL. DLL fills records in the struct object and return back to the C# application. So here I used "out" keyword in C# application to call C++ method. When I execute it, it fails with error "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." I am allocating memory at C++ DLL to store the records and assign it to out parameter. I could send data in struct object and print it in C++ DLL but not able to do modification in struct object at DLL side and return back to C# application.
Could anybody please help on this.
NativeDLLHelper.cs
class NativeDLLHelper
{
const int FIELD_LENGTH = 255;
[StructLayout(LayoutKind.Sequential)]
public struct APPDATA
{
public int ID;
public int Version;
[MarshalAs(UnmanagedType.LPTStr, SizeConst = FIELD_LENGTH)]
public string AppName;
};
[DllImport("Parser.dll", EntryPoint = "?FillLstStructData#DLLWrapper##QAEXAAPAU_APPDATA#1##Z")]
public static extern void FillLstStructData(out APPDATA[] AppDataStruct);
}
Main.cs
NativeDLLHelper.APPDATA[] lstFillAppDataStruct;
NativeDLLHelper.FillLstStructData(out lstFillAppDataStruct);
for (int i = 0; i < lstFillAppDataStruct.Length; i++)
{
Console.WriteLine(" ID[{0:G}]:{1:G}", i, lstFillAppDataStruct[i].ID);
Console.WriteLine(" Version[{0:G}]:{1:G}", i, lstFillAppDataStruct[i].Version);
Console.WriteLine(" AppName[{0:G}]:{1:G}", i, lstFillAppDataStruct[i].AppName);
}
C++ DLL:
static const int FIELD_LENGTH = 128;
typedef struct _APPDATA
{
int ID;
double Version;
TCHAR AppName[FIELD_LENGTH];
}APPDATA;
extern "C" __declspec(dllexport) FillLstStructData(APPDATA* &pAppData)
{
APPDATA *localAppDataStruct = new APPDATA[2];
localAppDataStruct[0].ID = 1;
localAppDataStruct[0].Version = 1.1;
_tcscpy(localAppDataStruct[0].AppName, L"MS Visual Studio 2010");
localAppDataStruct[1].ID = 2;
localAppDataStruct[1].Version = 2.1;
_tcscpy(localAppDataStruct[1].AppName, L"MS Office 2010");
pAppData = localAppDataStruct;
}
Your exported function unmangles to:
void __thiscall DLLWrapper::FillLstStructData(struct DLLWrapper::_APPDATA * &)
That makes it an instance method of a C++ class. You cannot pinvoke such methods, they require the C++ object to be created first and you cannot do this reliably with pinvoke. Only static C++ member functions can be pinvoked. Do note that this function doesn't need to be an instance method at all, it doesn't actually use instance members of the class.
That isn't the only problem, there's also a nasty memory management issue. The function allocates memory with ::operator new and memory needs to be released by the caller. But a C# program cannot do this, it doesn't have access to the ::operator delete that the C++ code uses. This kind of function is very hard to use reliably from a C++ program for that reason, it doesn't get better when you do it from C#. The proper way to do it is to allow the caller to pass the array. In other words, APPDATA[] and an extra argument that says how long the passed array is.
If you can't rewrite this function then you must write a wrapper for this class using the C++/CLI language. Very important that this wrapper uses the exact same compiler and CRT version as the C++ DLL.

Categories

Resources