DllImport StackOverflowException - c#

I'm trying to use C++ DLL(borland c builder) from c#. Function writeParameter works fine, it writes correct data to a file, but then i have an exception "An unhandled exception of type 'System.StackOverflowException' occurred in PresentationFramework.dll"
C++ code:
#include <vcl.h>
#include <windows.h>
#include <fstream.h>
#pragma hdrstop
#pragma argsused
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fwdreason, LPVOID lpvReserved)
{
return 1;
}
//---------------------------------------------------------------------------
#pragma pack (push,1)
typedef struct
{
int a;
}ABC;
#pragma pack (pop)
//---------------------------------------------------------------------------
extern "C" void __declspec(dllexport) __cdecl writeParameter(ABC *abc)
{
ofstream outfile("result.txt");
outfile<< "A=" <<endl;
outfile << abc->a <<endl;
outfile.close();
}
c#:
[StructLayoutAttribute(LayoutKind.Sequential)]
public class ABC
{
public int a;
}
[DllImport("D:\\monitorVC.dll", EntryPoint = "_writeParameter", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void WriteParameter(
[In,MarshalAs(UnmanagedType.LPStruct)]
ABC abc
);
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
var abc = new ABC() {a = 123};
WriteParameter(abc);
}

Read this blog post about the very poorly chosen name for UnmanagedType.LPStruct and how it does not do what everybody thinks it does. Fix your declaration like this:
[DllImport(...)]
public static extern void WriteParameter([In] ref ABC abc);

class != struct in C#.
Also, the packing of your structure is not the same between the C# version and the C++ version.

I know that it's been a while since this question was posted but I had the same experience trying to load to a VS2010 C# project, a .dll built with CodeGear C++ Builder 2007.
The work-around was to remove all TForms from my .dll. It seemed to me that the exported symbols from these forms (which by the way I could not remove) where "leading" the loader to a stack overflow.
Regards.

Related

AccessViolationException when accessing function with char * argument in c++ dll with C#

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.

How to call C++ code, and the code it calls, from C#

TL;DR
I have legacy c++ code which does stuff (sometimes returns stuff), calls other cpp code, but is not a full class/obj. This code I cannot alter. I am making fresh c# code which I am looking to call the c++ code from. I don't understand whether to create a dll that calls the original legacy c++, or create a CLR?? which also calls the legacy cpp. Below I have example code that I have implemented (with problems).
Main
I have legacy.cpp and legacy.h which I can not alter.
This is not a class/object and only has public functions, values, and #defines.
legacy.cpp and .h both #include other files and cpp libraries to do its job.
I have a new project where I can add C#, C++ code
I am having trouble understanding what I need to do/research in order to call any of the functions defined in legacy.cpp (or the values/defines) from within my new C# code.
Some of what I have looked at include
Managed CLR wrappers
https://drthitirat.wordpress.com/2013/06/03/use-c-codes-in-a-c-project-wrapping-native-c-with-a-managed-clr-wrapper/
https://web.archive.org/web/20140806022309/http://blogs.msdn.com/b/borisj/archive/2006/09/28/769708.aspx
DLLs??
How to call C++ DLL in C#
CLR
I have currently tried to create a CLR (thought I feel like it is not what I need in this situation), but I had problems in the clr_wrapper.cpp, it could not find the reference to foo()
//Wrapper.cpp
#include "Wrapper.h"
#include "abs\path\to\legacy\code\legacy.h"
#include "abs\path\to\legacy\code\legacy.cpp"
int foo_Wrapper()
{
return foo(); //int foo() is declared in legacy.h and defined in legacy.cpp
}
#pragma once
#include "abs\path\to\legacy\code\legacy.h"
#include "abs\path\to\legacy\code\legacy.cpp"
using namespace System; //What things use this?
//Can I just prepend System:: to whatever needs it?
namespace Wrapper {
public ref class Wrapper
{
public:
int foo_Wrapper();
};
}
the foo_Wrapper() is not able to call foo().
My confusion with this method is that it looks like I would need to make the clr wrapper an object class with member functions that will be called as needed. Leading to a syntax of obj.foo(). Is this what needs to be done if I chose to do some sort of CLR wrapper?
DLL
I have also looked at making this all a dll like in (How to call C++ DLL in C#)
However I am confused on setting this up. My current idea is to have a cpp dll call the original cpp (ie create legacyDll.dll which would make calls to foo(), then my main c# would call the __declspec(dllexport) functions defined within extern "C" {}
current setup (from "How to call c dll in c sharp")
dllmain.cpp
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <iostream>
#include <Windows.h>
using namespace std;
extern "C"
{
__declspec(dllexport) void bar_Dll()
{
cout << "calling bar() in legacy code" << endl;
}
__declspec(dllexport) int foo_Dll()
{
cout << "calling foo() in legacy code" << endl;
//realistically I would have,
//return foo()
}
}
Class1.cs
using System;
using System.Runtime.InteropServices;
namespace Test_DLL_Calling
{
class Class1
{
[DllImport("dllmain.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void bar_Dll();
[DllImport("dllmain.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int foo_Dll();
public static void Main(string[] arg)
{
bar_Dll(); //The specified module could not be found (Exception)
Console.WriteLine(foo_Dll()); //Im guessing this will also ^^
}
}
}
This part I don't follow. What and why are the attributes done the way they are?
Alright, so you have a header and cpp which you need to use. In order to use it you have to have make the c++ into C code. This is pretty much what you see in the DLL example code that you showed. However, I do suggest that you remove the includes from the header as I'm unsure how that would translate to the C code. Put the includes in the cpp files instead.
I find it rather difficult to answer this questions other than just showing a whole bunch of example code. Full code in: https://github.com/ze413X/Cpp-Code-Gems/ Where "CSharpExampleUsingCpp" calls from MainWindow.xaml.cs the "DLLExample" which uses the file in the directory includes and source.
DLL:
The header which is exposing functions to be used:
#pragma once
#define DLLExport _declspec(dllexport)
extern "C" DLLExport void __cdecl GetCppText(char* str, int* strLength);
extern "C" DLLExport void __cdecl DLLPrint();
.cpp
#include "../includes/DLLExampleCode.h"
#include <string>
#include <iostream>
void __cdecl GetCppText(char* str, int* strLength)
{
std::string text = "This is called from within the DLL.\0";
if (*strLength < text.length() || str == nullptr)
{
return;
}
memset((void*)str, 0, (*strLength) * sizeof(char));
strcpy_s(str, *strLength,text.c_str());
*strLength = text.length();
}
void __cdecl DLLPrint()
{
std::cout << "This is printed from inside DLLExample.dll.\n";
}
C#:
using System.Runtime.InteropServices;
namespace CSharpExampleUsingCpp
{
public partial class MainWindow : Window
{
const string PATH = "DLLExample.dll";
[DllImport(PATH, CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern void GetCppText(byte[] str, out System.Int32 strLength);
....
private void CppInteropButton_Click(object sender, RoutedEventArgs e)
{
System.Int32 size = 256;
System.Byte[] str = new byte[size];
for (int i = 0; i < size; i++)
{
str[i] = (byte)'1';
}
GetCppText(str, out size);
string result = System.Text.Encoding.UTF8.GetString(str, 0, size);
CppInteropButtonTextBox.Text = result;
}
Although, rereading my solution of obtaining a string might not be the best way of doing it. You could probably marshal that thing to avoid all this stupid char* conversions. I probably had some good reason at that point in time when I wrote it. That should be much easier to google though.

win32 in C# debug assertion failed CrtlsValidHeapPointer(block)

I am calling exported functions from a win32 dll in C#. I am trying to record audio and then get back the data in my C# program. As for the win32, the recording and playing all work, the problem is I get a debug assertion failed when I start the record now that I am trying to use global alloc so that the memory for the data is shared globally between both applications.
the C#:
[DllImport("\\Wave Editor\\Wave Editor\\lib\\Record.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern Boolean StartDialog(Byte** d);
unsafe private void OpenRecord(object sender, EventArgs e) {
unsafe Byte** recordSamples = (Byte**)Marshal.AllocHGlobal(1);
if (StartDialog(recordSamples) == false)
Debug.WriteLine("Dialog failed");
else
Debug.WriteLine("Dialog success");
}
The important parts of the win32 code relating to pSaveBuffer (the shared memory):
PBYTE pSaveBuffer;
switch (message)
{
case WM_INITDIALOG:
// Allocate memory for save buffer
pSaveBuffer = GlobalAlloc(GMEM_FIXED | GMEM_SHARE, 1);
return 0;
case MM_WIM_CLOSE:
GlobalFree(pSaveBuffer);
return 0;
}
The win32 function that creates the dialog:
BOOL StartDialog(BYTE** b)
{
b = &pSaveBuffer;
if (CreateDialog(hdll, MAKEINTRESOURCE(DLG_RECORD), NULL, DlgProc) == NULL)
{
return FALSE;
}
return TRUE;
}
The win32 header file:
#pragma once
#pragma comment(lib, "winmm.lib")
#include <Windows.h>
#ifdef __cplusplus
#define EXPORT extern "C" __declspec (dllexport)
#else
#define EXPORT __declspec (dllexport)
#endif
EXPORT BOOL StartDialog(BYTE** p);
This isn't the actual layout of the code, I put like things closer together to try get the point across. I think CrtlsValidHeapPointer(block) means I'm overwriting someone's heap, but I don't know how to fix that.

String to Char* Without memory leaks

I had try many ways to convert a string into a Char* but always got 2 errors when I import my DLL to a C# project.
the main function of my C++ DLL is like this:
//Example
extern "C" __declspec (dllexport) void Conv(std::string str)
{
FFileList file_list;
char temp_path[1024];
sprintf(temp_path,"%s*",arg_path);
GetFindFileListWin(temp_path,".mrs",file_list);
}
So, I Need to convert "str" to a char* because GetFindFileListWin is like this:
GetFindFileListWin(char* path,char* ext,FFileList& pList);
and pass it to arg_path.
I tried to do this:
char* arg_path = new char[str.length()+1];
strcpy(arg_path, str.c_str());
sprintf(temp_path,"%s*",arg_path);
delete[] arg_path;
but when I run Conv() in my C# program It says Windows has triggered a breakpoint in Program.exe.
This may be due to a corruption of the heap, which indicates a bug in Program.exe or any of the DLLs it has loaded. (Same happens if I use _strdup).
So, I tried in other way:
std::vector<char> Chr(str.size() + 1);
std::copy(str.begin(), str.end(), Chr.begin());
char *arg_path = &Chr[0];
sprintf(temp_path,"%s*",arg_path);
And I got Attempted to read or write protected memory message
My C# program do this:
[DllImport("Mrs.dll")]
public static extern void Conv(string str);
public void Convert(TextBox Tx)
{
Conv(Tx.Text);
}
Hope someone could help me to solve this error,
Thanks in advance.
I suspect the asterisk in the sprintf statements could be causing the problem. sprintf would expect a length parameter for the asterisk in the format string.
sprintf(temp_path,"%s*",arg_path);
^
string is c++ object.So in dll it will not work
What you should do as I mentioned above just to do such
extern "C" __declspec (dllexport) void Conv(const char* str)
{
//do whatever
}
If your p/invoke signature is
[DllImport("Mrs.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern void Conv(string str);
then your C++ declaration will be simply
extern "C" __declspec(dllexport) void Conv(const char* str);
and you won't need any std::string at all.
.NET and p/invoke do not know anything about std::string.

C++ DLL Called From C# on Windows CE for ARM Always Returns 0

I am currently developing an application for Windows CE on the TI OMAP processor, which is an ARM processor. I am trying to simply call a function in a C++ DLL file from C# and I always get a value of 0 back, no matter which data type I use. Is this most likely some kind of calling convention mismatch? I am compiling the DLL and the main EXE from the same Visual Studio solution.
C# Code Snippet:
public partial class Form1 : Form
{
private void button1_Click(object sender, EventArgs e)
{
byte test = LibWrap.test_return();
MessageBox.Show(test.ToString());
}
}
public class LibWrap
{
[DllImport("Test_CE.dll")]
public static extern byte test_return();
}
C++ DLL Code Snippet:
extern "C" __declspec (dllexport) unsigned char test_return() {
return 95;
}
It worked when I changed:
extern "C" __declspec (dllexport) unsigned char test_return() {
return 95;
}
to
extern "C" __declspec (dllexport) unsigned char __cdecl test_return() {
return 95;
}
In the DLL code. Why it doesn't assume this when compiled for WinCE is beyond me.
Try exporting test_return() as follows:
unsigned char __declspec(dllexport) WINAPI test_return() {
return 95;
}
Somewhere WINAPI is being defined as
__stdcall where it should have been __cdecl
No. WINAPI is defined as __stdcall.

Categories