I am working with a C++ DLL that I access with P/Invokes from C# code. The problem is I cannot redirect the std error that happens in the unmanaged side to a file. This works well if the build is in DEBUG but when the build is in RELEASE the STD logfile-unmanaged does not contain the error but contains and does not close the application, it keeps running like there was no error:
Before Error
After Error
In C++ the STD error is redirected to a file like this:
extern "C" __declspec(dllexport) void RedirectStd()
{
int fileNO = _fileno(stderr);
_close(fileNO);
int file = _open("logfile-unmanaged", O_CREAT | O_RDWR, 0644);
_dup2(file, fileNO);
}
A runtime error in C++ is generated like this:
extern "C" __declspec(dllexport) void DoException()
{
fprintf(stderr, "Before Error\n");
int a = 0;
int b = 10 / a;
fprintf(stderr, "After Error\n");
}
In C# I'm calling both of those methods:
[DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
internal static extern void RedirectStd();
[DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
internal static extern void DoException();
My Main function:
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
static void Main(string[] args) {
Console.WriteLine("===== REDIRECT =====");
Console.WriteLine("Native/Unmanaged exception redirected to logfile-unmanaged.");
RedirectStd();
Console.WriteLine("Native/Unmanaged std error redirected.");
Console.WriteLine("");
Console.WriteLine("===== EXCEPTION =====");
DoException();
Console.WriteLine("Waiting for program to crash");
do {
} while (true);
}
EDIT:
C# console application:
program.cs
using System;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Security;
namespace ConsoleWithErrors {
class Program {
[DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
internal static extern void RedirectStd();
[DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
internal static extern void DoException();
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
static void Main(string[] args) {
RedirectStd();
DoException();
Console.WriteLine("Waiting for program to crash");
do {
} while (true);
}
}
}
The console application stays opened like there was no error on the unmanaged side.
This works well if the build is in DEBUG but when the build is in RELEASE the STD logfile-unmanaged does not contain the error
It's probably optimized out, since you don't use the result of the division by zero. Try something like this instead:
extern "C" __declspec(dllexport) int DoException()
{
fprintf(stderr, "Before Error\n");
int a = 0;
int b = 10 / a;
fprintf(stderr, "After Error\n");
return b;
}
This will break the result out of internal linkage and force the compiler to emit the code.
You seem to be new to C# P/Invoke however, so here's a few tips:
the SetLastError attribute instructs the marshaller that the function will use the Win32 API SetLastError to set its error state, and your functions absolutely do not do so. You should remove it, because lying to the marshaller is never a recipe for success.
the SecurityCritical attribute has to do with process elevation between trust levels. Again, your application has nothing to do with that and should be removed.
the HandleProcessCorruptedStateExceptions attribute is deprecated since .Net Core (and including .Net 5). So if you're relying on it, you're nearing the end-of-life of your program, and it seems you barely even started writing it. The good news is that division by 0 isn't one of the exceptions that require this attribute to be processed, so again you should remove it!
Related
Problem Trying to use a function in a c++ dll with the following prototype...
int connectDfuBootloader(char * usbIndex)
...from C#, using any of these P/Invoke signatures, causes an AccessViolationException:
IntPtr...
[DllImport(#"CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int connectDfuBootloader(IntPtr usbIndex);
public static int connectDfuBootloader(string usbIndex)
{
IntPtr value = Marshal.StringToHGlobalAuto(usbIndex);
return connectDfuBootloader(value);
}
String...
[DllImport(#"CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int connectDfuBootloader(string usbIndex);
StringBuilder...
[DllImport(#"CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int connectDfuBootloader(StringBuilder usbIndex);
Variations on the theme of MarshalAs, tried LPStr, LPTStr etc...
[DllImport(#"CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int connectDfuBootloader([MarshalAs(UnmanagedType.LPStr)]string usbIndex);
Variations on the theme of specifying the character set, ANSI, Unicode, etc
[DllImport(#"CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl, CharSet =CharSet.Ansi)]
public static extern int connectDfuBootloader(string usbIndex);
Can anyone offer any suggestions?
Other things I've tried
Various StackOverflow posts, such as
AccessViolationException when accessing unmanaged C++ DLL with C#
Also, I tried making my own small C++ dll for test purposes, which contained a function with the same signature as the problem one above:
int functionAlpha(char * a)
{
if (a[0] == 'a')
return 10;
else
return 20;
}
This function I was able to access from C# without issue, using any of the methods above, e.g.
[DllImport(#"test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int functionAlpha(string a);
Which makes me think there is something special about the 'int connectDfuBootloader(char * usbIndex)' function. But they have the same signature, why would one work and the other not? I do not have the source code for the dll, so can't look in there to see if there are any other differences.
Update - Tried C++/CLI wrapper as suggested by #Taekahn below, same thing!
So, I have this in a header in the C++/CLI wrapper project:
#include "include/CubeProgrammer_API.h"
using namespace System;
namespace CLI
{
public ref class CubeWrapper
{
public:
int wrappedConnectDfuBootloader(String^ a);
};
}
Then in a cpp file in the wrapper project, I have:
namespace CLI
{
static char* string_to_ncchar_array(String^ string)
{
char* str = (char*)(Marshal::StringToHGlobalAnsi(string)).ToPointer();
return str;
}
int CubeWrapper::wrappedConnectDfuBootloader(String^ a)
{
a = "USB0";
// Calls the function from the linked c++ dll I'm trying to wrap
return connectDfuBootloader(string_to_ncchar_array(a));
}
}
And it gives the same result, System.AccessViolationException! What could be going on here?
Also tried using the dll from c++
Tried the same sequence of operations with the dll from a c++ application, and it worked fine.
Turns out that dxiv was on the right track; looked through some of the c++ example code supplied with the API and saw that several callbacks had to be setup first. Once I sorted those, problem fixed.
I have a native, unmanaged C++ DLL (symulator.dll) which I have to load in and call from a managed C# application.
The DLL utilizes C++ classes and dynamic memory allocation (via the new operator).
It exports a function called Init and its definition is as follows:
extern "C" __declspec( dllexport ) int Init( void )
{
sym = new CSymulator();
sym->Init();
return 0;
}
The CSymulator class contained within the DLL has a rather simple constructor:
CSymulator::CSymulator( void )
{
memset( mem, 0, sizeof( mem ) );
memset( &rmr, 0, sizeof( rmr ) );
md = 0;
ma = 0;
tacts = 0;
}
The CSymulator::Init() method, called by the Init() function exported by the DLL, is defined as follows:
int CSymulator::Init( void )
{
int *a = new int;
*a = 1;
FILE *f = fopen( "tmp.log", "wb" );
fprintf( f, "%i", *a );
fclose( f );
delete a;
return 0;
}
I am loading the native C++ DLL into the managed C# application using this code:
public partial class Form1 : Form
{
public IntPtr SimHandle;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string libname);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
delegate int SimInit();
SimInit DLL_Init;
public void InicjujDLL()
{
IntPtr adres;
adres = GetProcAddress(SimHandle, "Init");
DLL_Init = (SimInit)Marshal.GetDelegateForFunctionPointer(adres, typeof(SimInit));
int rc = DLL_Init();
}
private void WczytajDLL()
{
String fileName = "D:\\prg\\kompilator\\Debug DLL\\symulator.dll";
SimHandle = LoadLibrary(fileName);
if (SimHandle == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
throw new Exception(string.Format("Blad przy wczytywaniu biblioteki ({0})", errorCode));
}
else
{
InicjujDLL();
}
}
private void Form1_Load(object sender, EventArgs e)
{
WczytajDLL();
}
}
This code should produce a file named tmp.log with the content of 1 in it. But for some reason, tmp.log contains garbage data (a random 32-bit integer value instead of 1; for example, 2550276).
It's not the only function that produces garbage output. Any DLL function that tries to allocate memory dynamically is unable to use it after doing so.
It's as if the native C++ DLL is somehow getting its memory purged by the C# garbage collector.
How to prevent this behavior?
Wait a sec: Look at the reference below:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int MultiplyByTen(int numberToMultiply);
I didn't notice that your delegate doesn't have the same attribute.
For reference, I don't see anything unusual in how you are doing the LoadLibrary:
Dynamically Loading a Native Library
I have done this myself using the exact reference without a problem. I would suggest removing ALL code temporarily that executes inside the DLL and just do a simple pass-through value. Right now Init() is always returning 0. Try something else since you have zeroed out memory before, getting a zero back may just be a side-effect of the mem-zero op. Return 1974 or something.
Ensure you have allow unsafe code enabled and use the memory viewer to look at the stack (you are getting a pointer back so you have a starting point). If you do this beside the IL, you might spot where your memory is getting trashed.
HTH
I have an one question when I use C# DllImport C++ dll, I use the visual studio 2010 & checked the "Enable unmanaged code debugging", when it's running, always show the message "Buffer overrun detected! ... A buffer overrun has been detected which has corrupted the program's internal state. The program cannot safely continue execution and must now be terminated."
This dll is Third-party vendors to provide, and they says it's no error to run this dll, how can i fixed it?
My M++ dll function is,
int avc_to_avi_convert(char* chPath_avc, char* chPath_avi, int pPrivate, PROGRESS_CALLBACK progress_callback);
void avc_to_avi_close(int* pFd_avi);
typedef void (*PROGRESS_CALLBACK)(int iPercent, int pPrivate);
And i am use it in my C# dllimport like this :
public delegate void PROGRESS_CALLBACK(int _iPercent, int _pPrivate);
[DllImportAttribute(#"..\xxx.dll", EntryPoint = "avc_to_avi_convert", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern unsafe int avc_to_avi_convert([MarshalAs(UnmanagedType.LPStr)] StringBuilder _inputAVC, [MarshalAs(UnmanagedType.LPStr)] StringBuilder _ouputAVI, int pPrivate, PROGRESS_CALLBACK _pg);
[DllImportAttribute(#"..\xxx.dll", EntryPoint = "avc_to_avi_close", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern unsafe void avc_to_avi_close(int pFd_avi);
private void button1_Click(object sender, EventArgs e)
{
int i = 0;
StringBuilder inputFile = new StringBuilder(Application.StartupPath + #"\avc\abc.avc");//(#"C:\avc\abc.avc");
StringBuilder outputFile = new StringBuilder(Application.StartupPath + #"\avi\abc.avi");//(#"C:\avi\abc.avi");
if (avc_to_avi_convert(
inputFile,
outputFile,
1,
progress_func) > 0) {
}
}
public void progress_func(int iProgress, int pPrivate)
{
if (iProgress == 100)
{
//success
}
else if (iProgress == -1)
{
//convert error
}
else if (iProgress == -2)
{
//Not enough disk space
}
else
{
//update progress
}
}
I changed my code to,
[DllImportAttribute(#"..\xxx.dll", EntryPoint = "avc_to_avi_convert", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public static extern unsafe int avc_to_avi_convert([MarshalAs(UnmanagedType.BStr)] String _inputAVC, [MarshalAs(UnmanagedType.BStr)] String _ouputAVI, int pPrivate, PROGRESS_CALLBACK _pg);
[DllImportAttribute(#"..\xxx.dll", EntryPoint = "avc_to_avi_close", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public static extern void avc_to_avi_close(ref int avi);
And ran it, and I still get the same error.
1.) Are you sure about the calling convention? Try CallingConvention.StdCall. Read this:
http://blogs.msdn.com/b/adam_nathan/archive/2003/05/21/56690.aspx
2.) Try a different values for the CharSet attribute and use String instead of StringBuilder for the path arguments. This is a good reference:
http://msdn.microsoft.com/en-us/library/s9ts558h.aspx
3.) Also, the avc_to_avi_close method should look like this:
[DllImportAttribute("xxx.dll", EntryPoint="avc_to_avi_close")]
public static extern void avc_to_avi_close(ref int avi);
4.) Other things you my want to try:
Use unusual combinations of CharSet and MarshalAs. For example you know from the link above that Unicode should be used with LPWStr and Ansi with LPStr but you can of course try other combinations.
Your extern methods need not to be marked unsafe.
If you have exhausted all other options, why don't you try to write a C++ program that is analogous to your C# code:
If it crashes you proved that the error is in the library.
If it doesn't crash you know the error is in your use of PInovke, and you can go through all the 12+ combinations from my answer one by one.
You are passing a delegate to p/invoke and then letting the garbage collector free it. This causes the crash, because when the delegate object is finalized, the trampoline set up to allow calls from native code is deallocated. Then the native code invokes a dangling function pointer.
The garbage collector is totally unaware of any objects being used from inside native code. You MUST keep a reference to the delegate alive for the duration of the call. Try adding a delegate variable (don't use implicit conversion which creates a temporary) and then use GCHandle to keep the delegate alive for as long as the native code is using it.
I was given a block of c++ code that looks like it was from a c++ app that makes use of
Shared Memory for sending messages to other programs.
The c++ code has no #include or anything yet. I was given the code to use in my C# application and I am pretty stuck. I somewhat understand what the code does, but I don't know it well enough to translate it to C# as I am pretty new to coding.
My question is, what is the easiest way to be able to use the functionality of the code in my project? The end result is to send messages to another program, that will in turn do something that I'm not worried about.
I have tried creating different c++ projects and file types in my solution to link them using a reference later on, but I can never get it to compile properly.
Please let me know if you have some advice or a good place to look. I can always provide more information.
Code (I had to remove comments, sorry):
UINT WM_HELO_ZOOM_XYZ = RegisterWindowMessage("WM_HELO_ZOOM_XYZ");
int HELO_Broadcast_Zoom_Message(
double dbX,
double dbY,
double dbZ,
UINT uMessage=WM_HELO_ZOOM_XYZ) {
#ifndef HELO_
typedef struct {
UINT uMajVersion;
UINT uMinVersion;
DWORD dwReserved;
double dbX;
double dbY;
double dbZ;
} HELOCoordsStruct;
#endif
char *szSharedMemory = "HELO-_Coords";
char szErr[_MAX_PATH*3];
HANDLE hMem = OpenFileMapping(FILE_MAP_WRITE, FALSE, szSharedMemory);
if (NULL == hMem) {
return(0);
}
void *pvHead = MapViewOfFile(hMem, FILE_MAP_WRITE, 0,0,0);
if (NULL == pvHead) {
CloseHandle(hMem);
sprintf(szErr, "Unable to view", szSharedMemory);
AfxMessageBox(szErr, MB_OK|MB_ICONSTOP);
return(0);
}
HELOCoordsStruct *pHELOCoords = (HELOCoordsStruct *)pvHead;
BOOL bVersionOk=FALSE;
if (1 == pHELOCoords->uMajorVersion) {
if (WM_HELO_ZOOM_XYZ==uMessage) {
pHELOCoords->dbX = dbX;
pHELOCoords->dbY = dbY;
pHELOCoords->dbZ = dbZ;
}
bVersionOk=TRUE;
}
else {
sprintf(szErr, "Unrecognized HELO- shared memory version: %d.%d", pHELOCoords->uMajVersion, pHELOCoords->uMinVersion);
AfxMessageBox(szErr, MB_OK);
}
if (NULL != hMem) CloseHandle(hMem);
UnmapViewOfFile(pvHead);
if (bVersionOk) {
PostMessage(HWND_BROADCAST,uMessage,0,0);
return(1);
}
else return(0);
}
EDIT: The feedback has been completely unreal. I must say that the community sure spoils folks around here.
Thanks,
Kevin
I think you have three options:
Create a Managed C++ project of type Class Library, put the code in it, make a reference from your main app to this project.
Create an unmanaged C++ DLL project, put the code in a function (or functions), export the function(s) (using .def file), and build the project. Use the functions from that dll using [DllImport] attribute. (See here and here)
Convert the code to C#. This will require some knowledge of unmanaged code, Win32 and P/Invoke (See here and here). And as I see your code, it is takes a little time!
Here is your converted code (option 3):
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace UnmanagedBlock
{
public class ConvertedClass
{
public uint WM_HELO_ZOOM_XYZ = RegisterWindowMessageA("WM_HELO_ZOOM_XYZ"); // Your code uses the ANSI string
int HELO_Broadcast_Zoom_Message(
double dbX, double dbY, double dbZ, uint uMessage) // Porting the default value for 'uMessage' is not possible
{
string szSharedMemory = "HELO-_Coords";
IntPtr hMem = OpenFileMapping(FileMapAccessRights.Write, FALSE, szSharedMemory);
if (IntPtr.Zero == hMem)
return 0;
IntPtr pvHead = MapViewOfFile(hMem, FileMapAccessRights.Write, 0, 0, UIntPtr.Zero);
if (IntPtr.Zero == pvHead)
{
CloseHandle(hMem);
MessageBox.Show(
"Unable to view " + szSharedMemory, // Your code does not concat these two strings.
"Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);
return 0;
}
HELOCoordsStruct pHELOCoords = new HELOCoordsStruct();
Marshal.PtrToStructure(pvHead, pHELOCoords);
int bVersionOk = FALSE;
if (1 == pHELOCoords.uMajVersion) // I think it had a typo (it was uMajorVersion)
{
if (WM_HELO_ZOOM_XYZ == uMessage)
{
pHELOCoords.dbX = dbX;
pHELOCoords.dbY = dbY;
pHELOCoords.dbZ = dbZ;
}
Marshal.StructureToPtr(pHELOCoords, pvHead, false);
bVersionOk = TRUE;
}
else
{
MessageBox.Show(
"Unrecognized HELO- shared memory version: " +
pHELOCoords.uMajVersion.ToString() + "." + pHELOCoords.uMinVersion.ToString());
}
if (IntPtr.Zero != hMem)
CloseHandle(hMem);
UnmapViewOfFile(pvHead);
if (bVersionOk == TRUE)
{
PostMessage(HWND_BROADCAST, uMessage, 0, 0);
return 1;
}
else
return 0;
}
[StructLayout(LayoutKind.Sequential)]
private class HELOCoordsStruct
{
public uint uMajVersion;
public uint uMinVersion;
public uint dwReserved;
public double dbX;
public double dbY;
public double dbZ;
}
[DllImport("user32", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public static extern uint RegisterWindowMessageW([In]string lpString);
[DllImport("user32", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern uint RegisterWindowMessageA([In]string lpString);
[DllImport("kernel32", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
public static extern IntPtr OpenFileMapping(FileMapAccessRights dwDesiredAccess, int bInheritHandle, [In]String lpName);
[DllImport("kernel32", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
public static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, FileMapAccessRights dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, UIntPtr dwNumberOfBytesToMap);
[DllImport("kernel32", CallingConvention = CallingConvention.StdCall)]
public static extern int UnmapViewOfFile(IntPtr lpBaseAddress);
[DllImport("kernel32", CallingConvention = CallingConvention.StdCall)]
public static extern int CloseHandle(IntPtr hObject);
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
public const int FALSE = 0, TRUE = 1;
public enum FileMapAccessRights : uint
{
Write = 0x2,
Read = 0x4,
Execute = 0x20,
}
public const IntPtr HWND_BROADCAST = (IntPtr)0xffff;
}
}
I've done an exact conversion and I think that it should work fine, however I have not tested it.
Let me know if it works.
You can dump the C++ code into a Visual C++ project and build that. When you build it, go into the project settings and select the option that generates tlb files (It's a proxy class for c++/.net interop, I can't remember the name of the option).
Once you have this, you can add a reference to the tlb interop assembly from a C# project.
Also, look here for a Microsoft example http://msdn.microsoft.com/en-us/library/fx82zhxa.aspx
Chances are that if you post the code here, some nice person will port the code from C++ to C# for you. However, for future reference when dealing with using native C++ code from within a .NET application, you can use the InteropServices of the .NET framework to reference native functions in native dlls.
To do this requires a few steps on both the C++ and C# side of things. Firstly you need to build your entry point as an exported function in C++.
For example, say I wanted to write a trivial C++ function to add 2 numbers together and then call it from a C# app, I would have to do the following:
Step 1: Writing the C++ function.
In order for external sources to locate your functions, you need to let the compiler know that the function is to be 'exported'. A point to note is that if you're calling other functions from within your exported function, you do not need to mark them all as exported.
So let's write the "add" function in C++:
#define DLLEXPORT extern "C" declspec(dllexport)
DLLEXPORT int __cdecl add(int x, int y)
{
return (x + y);
}
The first line defines a macro we'll use to mark exported methods. The extern "C" part tells the compiler to avoid mangling the exported name of the function (so it will always be 'add', and not something like #YZadd_), next comes the function definition marked as a DLLEXPORT. Before I continue there's one more point on the 'name mangling' in exported functions, and that is for functions declared __stdcall or any of its variations (WINAPI..etc). Functions that are marked for exporting and declared with extern "C" with the calling convention __stdcall will always be appended with #X where X is the number of bytes of the function paramaters (so for the above example if add was declared __stdcall then the exported function name would be add#8. Just keep this in mind if C# ever has trouble locating your functions.
Now, the C++ side of things is done, compile that as a DLL and move over to C#.
In C# it's rather straightforward to import external functions. First you'll need to reference the InteropServices namespace:
using System.Runtime.InteropServices;
And then you will need to make a [DllImport] declaration:
[DllImport(#"path to your C++ dll here")]
public static extern int add(int x, int y) //make sure the function definition matches.
Provided the function names match, that should be all that's required to import the function. Now you can call add just as you would any normal function in C#
int x = 5;
int y = 10;
int z = add(x, y); //z should be 10
That about concludes how to simply export C++ functions and call them from a C# application.
If you can't get the C++ code to work as-is then there's no point trying to graft it into your C# app.
Figure out the C++ code first (read MSDN documentation for the APIs used, ask the person that gave you the code, post specific questions). Once you understand it better and can make it work then you'll have a better chance of figuring out the best way to do what's needed in C#.
I have a C++ file with some exported functions which I am calling in C#. One of the functions is this:
char segexpc[MAX_SEG_LEN];
extern "C" QUERYSEGMENTATION_API char* fnsegc2Exported()
{
return segexpc2;
}
Somewhere in the program, I am also doing this thing:
if(cr1==1)
{
strcpy(segexpc, seg);
}
In my C# program, I am invoking the above by the followign way:
[DllImport("QuerySegmentation.dll", EntryPoint = "fnsegcExported", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern StringBuilder fnsegcExported();
this.stringbuildervar = fnsegcExported();
Previously, I was not getting any error, but now suddenly I have started getting this error, when I debug in visual studio.
Windows has triggered a breakpoint in SampleAppGUI.exe.
This may be due to a corruption of the heap, which indicates a bug in SampleAppGUI.exe or any of the DLLs it has loaded.
This may also be due to the user pressing F12 while SampleAppGUI.exe has focus.
This error appears only at the end just before it has to display the window. I have not pressed any F12 key key and neither is any breakpoint set here, but I am not sure why the error is occuring and breaking at this point. this.stringbuildervar = fnsegcExported();
When I press on continue, the window appears with the correct output.
What would happen if you changed your external declaration from
[DllImport("QuerySegmentation.dll", EntryPoint = "fnsegcExported", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern StringBuilder fnsegcExported();
to
[DllImport("QuerySegmentation.dll", EntryPoint = "fnsegcExported", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern string fnsegcExported();
And then called it in the following way:
this.stringbuildervar = new StringBuilder(fnsegcExported());
string seems the more appropriate type. Or better yet use the Marshal class to marshal your unmanaged char* return into a managed string.
[DllImport("QuerySegmentation.dll", EntryPoint = "fnsegcExported", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern IntPtr fnsegcExported();
string managedStr = Marshal.PtrToStringAnsi(fnsegcExported);
this.stringbuildervar = new StringBuilder(managedStr);
The reason why you see the error on this line is that it is the last stack frame for which you have debug information.
Fortunately, in your case, there is very little code on the C++ side. Make sure that segexpc contains a zero terminated string.
I suspect that because the default capacity of the string builder is 16, you may not be able to return longer strings in this fashion. Maybe you want to return just strings.
I also wonder whether your C++ string has to be non-Unicode. That will hurt performance upon every conversion.