Windows has triggered a breakpoint due to corruption in heap or dlls loaded - 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.

Related

Using QPDF with C#

I am attempting to translate this qpdf command:
qpdf --qdf --object-streams=disable input.pdf editable.pdf
into the equivalent method calls I would need when using the qpdf dll (available from here: https://sourceforge.net/projects/qpdf/).
I ran the qpdf dll through dumpbin to get the function names, and by looking at the header files that were included for use with a c++ project I can see the parameters for the functions.
For example the function needed to impart the --object-streams option above would (from what I can tell) be this function:
void setObjectStreamMode(qpdf_object_stream_e);
from the c++ header file which becomes:
[DllImport("qpdf21.dll")]
static extern void _ZN10QPDFWriter19setObjectStreamModeE20qpdf_object_stream_e(int stateEnum);
in the C# file.
The problem is when I use the above function I get an
AccessViolationException: Attempted to read or write protected memory
error, which makes me think I need to create a QPDF object somehow, but I have never used object oriented pinvokes, so I'm at a loss of how to make the object accessible in c#.
If anyone is already familiar with using the dll in C#, or even in C++, and could tell me the correct functions to call to replicate the command I would appreciate it!
I've managed to figure it out, the below code replicates the command, turns out I was looking into the wrong header file:
[DllImport("qpdf21.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr qpdf_init();
[DllImport("qpdf21.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern void qpdf_cleanup(ref IntPtr qpdfData);
[DllImport("qpdf21.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern int qpdf_read(IntPtr qpdfdata, [MarshalAs(UnmanagedType.LPStr)] string fileName, IntPtr password);
[DllImport("qpdf21.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern void qpdf_set_object_stream_mode(IntPtr qpdf, int mode);
[DllImport("qpdf21.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern void qpdf_set_qdf_mode(IntPtr qpdf, int value);
[DllImport("qpdf21.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern int qpdf_init_write(IntPtr qpdf, [MarshalAs(UnmanagedType.LPStr)] string fileName);
[DllImport("qpdf21.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
static extern int qpdf_write(IntPtr qpdf);
static void Main(string[] args)
{
//call init
IntPtr qpdfData = qpdf_init();
//call read (which gets and processes the input file)
//0 result == good
int result = qpdf_read(qpdfData, #"scan.pdf", IntPtr.Zero);
//call init_write
result = qpdf_init_write(qpdfData, #"scanEditable.pdf");
//set write options
//disable object stream mode
qpdf_set_qdf_mode(qpdfData, 1);
qpdf_set_object_stream_mode(qpdfData, 0);
//call write
result = qpdf_write(qpdfData);
//call cleanup
qpdf_cleanup(ref qpdfData);
}
Looks like you've figured out a good answer here. You've discovered the C API, which is intended for helping to use QPDF from languages other than C++ through the DLL. The C API is primarily documented in the qpdf-c.h header file. You can find some information in the Using Other Languages section of the manual as well. The C API does not expose the full functionality of qpdf's C++ library. If you find missing pieces, please feel free to create an issue at github. I try to update the C API when I add new interfaces, but I don't do it for every interface, and some of the functionality in the CLI is implemented directly in the tool and doesn't map to a single library function.
If the C API is not rich enough for your use case, it's also possible to write your own C++ class with some functions declared extern "C", export them, and build an additional DLL that you can use in the manner you have found above. You can look at qpdf-c.cc as an example of how this works.

Buffer overrun detected

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.

Return contents of a std::wstring from C++ into C#

I have an unmanaged C++ DLL that I have wrapped with a simple C interface so I can call PInvoke on it from C#. Here is an example method in the C wrapper:
const wchar_t* getMyString()
{
// Assume that someWideString is a std::wstring that will remain
// in memory for the life of the incoming calls.
return someWideString.c_str();
}
Here is my C# DLLImport setup.
[DllImport( "my.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl )]
private static extern string GetMyString();
However the string is not correctly marshalled, often screwing up the first character or sometimes way off showing a bunch of chinese characters instead. I have logged output from the implementation on the C side to confirm that the std::wstring is correctly formed.
I have also tried changing the DLLImport to return an IntPtr and convert with a wrapped method using Marshal.PtrToStringUni and it has the same result.
[DllImport( "my.dll", CallingConvention = CallingConvention.Cdecl )]
private static extern IntPtr GetMyString();
public string GetMyStringMarshal()
{
return Marshal.PtrToStringUni( GetMyString() );
}
Any ideas?
Update with Answer
So as mentioned below, this is not really an issue with my bindings but the lifetime of my wchar_t*. My written assumption was wrong, someWideString was in fact being copied during my calls to the rest of the application. Therefore it existed only on the stack and was being let go before my C# code could finish marshalling it.
The correct solution is to either pass a pointer in to my method as described by shf301, or make sure my wchar_t* reference does not get moved / reallocated / destroyed before my C# interface has time to copy it.
Returning the std::wstring down to my C layer as a "const &std::wstring" means my call to c_str() will return a reference that won't be immediately dealloc'd outside the scope of my C method.
The calling C# code then needs to use Marshal.PtrToStringUni() to copy data from the reference into a managed string.
You are going to have to rewrite your getMyString function for the reasons mentioned in Hans Passant's answer.
You need to have the C# code pass a buffer in to your C++ code. That way the your code (ok, the CLR Marshaller) controls the lifetime of the buffer and you don't get into any undefined behavior.
Below is an implementation:
C++
void getMyString(wchar_t *str, int len)
{
wcscpy_s(str, len, someWideString.c_str());
}
C#
[DllImport( "my.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode )]
private static extern void GetMyString(StringBuffer str, int len);
public string GetMyStringMarshal()
{
StringBuffer buffer = new StringBuffer(255);
GetMyString(buffer, buffer.Capacity);
return buffer.ToString();
}
You need to specify MarshalAs attribute for the return value:
[DllImport( "my.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
[return : MarshalAs(UnmanagedType.LPWStr)]
private static extern string GetMyString();
Make sure the function is indeed cdecl and that the wstring object is not destroyed when the function returns.

DllImport, Char*& and StringBuilder C/C#

I have a problem, I tried to look at almost all the poster solutions, unsuccessful to find a suitable one.
The question is easy, Want to have a return string from unmanaged C code in my managed C#.
The c function is:
extern "C" __declspec(dllexport) int process_batch (char *&result);
and in C# I imported the DLL:
[DllImport("mydll.dll")]
public static extern IntPtr process_batch(StringBuilder result);
I run, but the return value in my StringBuilder is a 7-8 character string of non-sense! (I think the memory address)
I tried adding ref before the StringBuilder, this time I get the correct return value in StringBuilder but I get AccessViolationException :
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
So I need your help to fix this.
Just one more thing, I use malloc in c for allocating the memory to the char* variable.
Thanks,
If you really want to do that, pass the parameter as a ref IntPtr, then call Marshal.PtrToStringAnsi, or similar.
[DllImport("mydll.dll")]
public static extern IntPtr process_batch(ref IntPtr result);
Note that, since you're allocating the string with malloc in C, the .NET program will have to keep track of the returned pointer so that it can pass it back to you to be deallocated. Otherwise you'll have a memory leak.
If you were to pass a ref StringBuilder, you wouldn't be able to deallocate the memory that was allocated by the C program.
Also, as somebody commented in another post, you need to set the calling convention:
[DllImport("mydll.dll", CallingConvention=CallingConvention.Cdecl)]
I've been using the following code successfully:
[DllImport("user32.dll", EntryPoint = "GetClassName", ExactSpelling = false,
CharSet = CharSet.Auto, SetLastError = true)]
private static extern int _GetClassName(IntPtr hwnd, StringBuilder lpClassName,
int nMaxCount);
public static string GetClassName(IntPtr hWnd)
{
StringBuilder title = new StringBuilder(MAXTITLE);
int titleLength = _GetClassName(hWnd, title, title.Capacity + 1);
title.Length = titleLength;
return title.ToString();
}
I'd advise for a more specific declaration of the imported method via the DllImportAttribute. Try the CharSet = CharSet.Auto bit for instance
I know that this is not exactly related to your original problem as this makes use of the Windows API, but maybe it is of help nonetheless.

How to import void * C API into C#?

Given this C API declaration how would it be imported to C#?
int _stdcall z4ctyget(CITY_REC *, void *);
I've been able to get this far:
[DllImport(#"zip4_w32.dll",
CallingConvention = CallingConvention.StdCall,
EntryPoint = "z4ctygetSTD",
ExactSpelling = false)]
private extern static int z4ctygetSTD(ref CITY_REC args, void * ptr);
Naturally in C# the "void *" doesn't compile.
Some Googling indicates that it should be translated as "object." Which seems like it should work. But others indicate that "Void * is called a function pointer in C/C++ terms which in C# terms is a delegate". That doesn't make a whole lot of sense here as what would it delegate to? Some similar calls for other APIs found through Googling use other functions in the respective API. But in this API no other call would make sense.
The documentation for the call shows an example:
z4ctyget(&city, “00000”);
Which seems to show that even a static value could be passed.
It will compile with object in place of the void *. I don't know whether this is right and I haven't had an opportunity to test it (licensing issue).
For the void* parameter you can just use an IntPtr
[DllImport(#"zip4_w32.dll",
CallingConvention = CallingConvention.StdCall,
EntryPoint = "z4ctygetSTD",
ExactSpelling = false)]
private extern static int z4ctygetSTD(ref CITY_REC args, IntPtr ptr);
You can also use void* if you mark your class as unsafe.
It really depends on what the API is looking for in that parameter.
You can add IntPtr or Object* to get past compiler, but you will still need to pass it the correct data when you call it.
As far as I can tell the C declaration of z4ctyget is:
int z4ctyget(CITY_REC *cityrec, char *zipcode);
The second parameter is a 5 character ANSI string representing the zip code at which you want to start your search or "00000" to start at the beginning of the file. So your declaration should be:
[DllImport(#"zip4_w32.dll", CharSet = CharSet.Ansi)]
private extern static int z4ctygetSTD(ref CITY_REC args, string zipcode);

Categories