Consider following native code (which is unmodifiable for reasons):
#include <cstdio>
#include <exception>
extern "C" {
__declspec(dllexport) void terminate_me(void) {
puts("hello from C");
std::terminate();
puts("bb from C");
}
}
Which gets called from C# (which we can change in any way we want)
using System.Runtime.InteropServices;
class Program
{
[DllImport("Project1.dll")]
static extern void terminate_me();
static void Main(string[] args)
{
terminate_me();
}
}
And this is what happening:
My questions is can we crash the app without showing this window to the user? I mean okay, something bad happened to the unmanaged code, just close the app with error code, don't show anything to the user.
Is it feasible?
Use the _CrtSetReportMode functions, something like this:
extern "C" {
__declspec(dllexport) void terminate_me(void) {
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); // define that anywhere in your init code, etc.
puts("hello from C");
std::terminate();
puts("bb from C");
}
}
Note when _DEBUG is not defined (so, in release), calls to _CrtSetReportMode are removed during preprocessing.
Your BadImageFormatException error probably comes from that fact you've checked the "Prefer 32-bit" checkbox in .NET project properties in release mode. This error is always a x86-x64 mismatch issue.
Related
Suppose we have the following C++ code:
typedef int (*getIntPtr)(void);
extern "C" __declspec(dllexport) void InvokeFuncPtr(getIntPtr funcPtr) {
std::wcout << funcPtr();
}
We can match this definition in C#:
[DllImport("NativeLib.dll", CallingConvention = CallingConvention.Cdecl), SuppressGCTransition]
public static unsafe extern void InvokeFuncPtr(delegate* unmanaged[Cdecl]<int> funcPtr);
And then we can use this function like so:
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
public static int ReturnInt() => 123;
// ... Elsewhere:
unsafe {
InvokeFuncPtr(&ReturnInt);
}
When InvokeFuncPtr is marked with SuppressGCTransition this results in the program crashing with the error "Fatal error. Invalid Program: attempted to call a UnmanagedCallersOnly method from managed code.". If we remove the SuppressGCTransition attribute it works as expected, and 123 is printed to the console.
Is this expected behaviour? I imagine it amounts to the runtime seeing ReturnInt() as being invoked from managed code, simply with a couple of extra steps of indirection. If so, is there any way around this, or should I simply leave the SuppressGCTransition attribute off?
Yes, you should leave off SuppressGCTransition, as the stack is what the runtime uses to identify whether the caller is managed or unmanaged.
If there is no transition in the stack, there is no way for the runtime to tell that the stack transitioned to unmanaged.
Alternatively, you can leave off UnmanagedCallersOnly, and instead marshal the delegate with Marshal.GetFunctionPointerForDelegate.
I'm trying to call a minimal C function from C# on Windows 10. I use mingw/g++ to compile the C code into a .dll
It turns out that I have to define opterator new[] or compile the .dll using Visual Studio. Otherwise my C# program crashes with the following error:
The program '[14740] Test.exe' has exited with code -1073741819 (0xc0000005) 'Access violation'.
I'd really love to understand what exactly is happening here and how I can resolve this issue without overriding all the new/delete operators but still using mingw.
Here's the minimal example reproducing the error including a workaround (if AddNewOperator is defined operator new[] will be defined and the resulting .dll will work fine):
Test.cs (compiled/run with Visual Studio 2017):
using System;
using System.Runtime.InteropServices;
class Program
{
[DllImport("libTest", CallingConvention = CallingConvention.StdCall, ExactSpelling = true)]
public static extern int TestFunction();
static void Main(string[] args)
{
Console.WriteLine("!!" + TestFunction());
}
}
Test.cpp compiled with mingw (see below):
#include <new>
#include <cstdlib>
#ifdef AddNewOperator // This will fix the issue
void* operator new[](std::size_t sz){
return std::malloc(sz);
}
#end
extern "C" {
int __stdcall __declspec(dllexport) TestFunction() {
int* test = new int[3]; // removing this line will make everything work when building
return test[2];
}
And here's the build script:
# Remove the following # and the compiled dll will work just fine
g++ -g -s -Wall -c -fmessage-length=0 Test.cpp #-DAddNewOperator
g++ -g -shared -o libTest.dll *.o -Wl,--subsystem,windows
Edit: compiling everything for x86 instead of 64 bit also fixes the issue (which is again no option for me)
TL;DR
You must not mix allocation / deallocation between compilers!
The problem you are facing is quite tricky and actually your program should crash every time, with or without the void* operator new[](size_t){...} definition.
If you debug your program, it actually should crash while deleting your test variable. This variable is created using mingw's new operator, but deleted using MSVC delete operator, and they are not interoperable. So you have to use mingw's delete function.
for a simple test you can just do:
c++ code:
int* test = nullptr;
int __stdcall __declspec(dllexport) TestFunction() {
test = new int[3]; // note test is global
return test[2];
}
void __stdcall _declspec(dllexport) CleanUp() {
delete[] test;
}
c# code:
public static extern int TestFunction();
public static extern int CleanUp();
static void Main(string[] args)
{
Console.WriteLine("!!" + TestFunction());
CleanUp();
}
Why is your program not crashing if you redefine the new operator?!
I actually don't know for sure, but i think, the malloc implementation of mingw uses legacy C runtime which uses HeapAlloc for allocating and HeapFree for deleting your test variable. In short, you are simply lucky/unlucky it is not crashing when you custom defined your operator new and used malloc inside...
However, if you compile it using Visual Studio, both (dll and exe) are using the same runtime, so allocation/deallocation is done within the same memory space organizer. BUT still it is UB and you will run into problems! E.g.: If you create your libary with msvc10 and want to use this library with msvc14 same can happen here! I can remember some issues with code coming from a bug where the memory also was managed wrong; We used a library created with msvc11 but our code was compiled with msvc12...
I am currently working on a project with really short deadline, so I don't have much time to understand everything. Also, I am not an expert in C++ development and memory management.
So, what I am trying to do is to create a DLL in with both C and C++ code. Then, I would like to call this DLL in a C# code. Currently, the communication between C++ and C# is OK. The problem comes up when I try to transfer a string from the DLL to the C# code. The error is this one :
System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
at Microsoft.Win32.Win32Native.CoTaskMemFree(IntPtr ptr)
at System.StubHelpers.CSTRMarshaler.ClearNative(IntPtr pNative)
at NMSPRecognitionWrapper.Program.GetResultsExt()
at NMSPRecognitionWrapper.Program.<Main>b__0() in <my dir>\Program.cs:line 54
at NMSPRecognitionWrapper.Program.StartRecognitionExt()
at NMSPRecognitionWrapper.Program.Main(String[] args) in <my dir>\Program.cs:line 60
Also, I can give you some piece of code below (really simplified !). Actually, the C++ expose two methods : StartRecognition() launch operations to get some data from microphone, then process them and store the results. GetResults() return an instance of the results previously stored. The WrapperCallback() allows the C# part to be called when a Result is able for processing. The C# part, when the Callback is called, will ask to get the results using the GetResults() method.
I know the architecture may seem really inappropriate in this presentation, but I don't want to explain the whole project to validate the model, please be sure everything is correct.
To finish, the problem is when the C# callback call the GetResults() method. Trying to access to the resultsForCS seems to be impossible from the C#.
C++ part - header
// NMSPRecognitionLib.h
#pragma once
#include <iostream>
using namespace std;
extern "C" __declspec(dllexport) char* GetResults();
extern "C" static void DoWork();
extern "C" __declspec(dllexport) void StartRecognition();
C++ part - sources
#include "stdafx.h"
#include "NMSPRecognitionLib.h"
static char * resultsForCS;
static SUCCESS ProcessResult(NMSPCONNECTION_OBJECTS *pNmspConnectionObjects, LH_OBJECT hResult)
{
[...]
char* szResult;
[...]
resultsForCS = szResult;
DoWork();
[...]
return Success;
error:
return Failure;
} /* End of ProcessResult */
extern "C" __declspec(dllexport) char* GetResults()
{
return resultsForCS;
}
extern "C"
{
typedef void (*callback_function)();
callback_function gCBF;
__declspec(dllexport) void WrapperCallback(callback_function callback) {
gCBF = callback;
}
static void DoWork() {
gCBF();
}
}
extern "C" __declspec(dllexport) void StartRecognition()
{
char* argv[] = { "path", "params" };
entryPoint(2, argv);
}
C# part
class Program
{
[DllImport("NMSPRecognitionLib.dll", EntryPoint = "GetResults")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string GetResultsExt();
public delegate void message_callback_delegate();
[DllImport("NMSPRecognitionLib.dll", EntryPoint = "WrapperCallback")]
public static extern void WrapperCallbackExt(message_callback_delegate callback);
[DllImport("NMSPRecognitionLib.dll", EntryPoint = "StartRecognition")]
public static extern void StartRecognitionExt();
static void Main(string[] args)
{
WrapperCallbackExt(
delegate()
{
Console.WriteLine(GetResultsExt());
}
);
StartRecognitionExt();
Console.WriteLine("\nPress any key to finish... ");
var nothing = Console.ReadLine();
}
}
I understand that the problem comes because I am using a pointer to store the results (char *), but I actually don't know how to do this in another way. The szResults type is char * too and I can't change this !
Yes, the return type is the problem. The pinvoke marshaller must do something to release the memory that was allocated for the string. The contract is that memory allocations that need to be released by the caller must be allocated from the COM heap. CoTaskMemAlloc() in native code, also exposed in .NET as Marshal.AllocCoTaskMem().
This rarely comes to a good end, most native code allocates with malloc() or ::operator new, allocating from a heap that's created by the C runtime library. The wrong heap. So inevitably the CoTaskMemFree() call will fail. Ignored silently in Windows XP and earlier, a kaboom on Vista and up.
You must stop the pinvoke marshaller from trying to release the memory. Do so by declaring the return value as IntPtr. And use Marshal.PtrToStringAnsi() to recover the string.
You still have a Big Problem, the kind of problem that bedevils any native code that tries to use this function as well. You still have a string buffer that needs to be released. You cannot do that from C#, you can't pinvoke the correct version of free() or ::operator delete. A memory leak is inevitable. The only thing you can hope for is that the native code takes care of it, somehow. If it doesn't then you must use C++/CLI to interop with it. With the additional requirement that the native code needs to be rebuilt with the same compiler so that it uses the same shared CRT. Code that's difficult to use correctly from native code is also hard to pinvoke. That's a design flaw, always allow the caller to pass a buffer to be filled in so there's never a question who owns the memory.
Looking at:
at Microsoft.Win32.Win32Native.CoTaskMemFree(IntPtr ptr)
at System.StubHelpers.CSTRMarshaler.ClearNative(IntPtr pNative)
at NMSPRecognitionWrapper.Program.GetResultsExt()
I can see that your callback is called, but the runtime tries to free some memory. I think it assumes your pointer would be to com memory. Try converting the string yourself, it is easy!
[DllImport("NMSPRecognitionLib.dll", EntryPoint = "GetResults")]
public static extern IntPtr GetResultsExt();
[...]
string result = Marshal.PtrToStringAnsi(GetResultsExt())
No 100% guarantee, but worth a try.
I have found that it is usually easier to write a wrapper in C++/CLI around the C++ native code. A C++/CLI class can directly call and use native C++, but is accessible from C# (and any .Net language). In my experience, using DLLImport as you do leads to hard to debug and find errors.
I have a C++ Dll "TheFoo.dll" with a method "Foo()"
I have access to other C++ code that uses this method by simply calling:
Foo();
I believe the Method does have the:
__declspec( dllexport )
So, with the reading I've done about P/Invoke, i thought i should be able to simply call the same method from my C# code:
namespace PInvokeExample1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
[DllImport(#"C:\MyFolder\TheFoo.dll")]
public static extern
void Foo();
private void button1_Click(object sender, RoutedEventArgs e)
{
Foo();
}
}
}
When i run it, i get an error:
Unable to find an entry point named 'Foo' in DLL 'C:\MyFolder\TheFoo.dll'.
Any ideas why it is not found?
The C++ language supports overloading, much like C# does. You could export an function void Foo(int) and a function void Foo(double). Clearly those two functions could not both be exported as "Foo", the client of the DLL wouldn't know which one to pick. So it is not.
The C++ compiler solves that problem by decorating the name of the function. Adding extra characters that makes a Foo(int) different from a Foo(double). You can see those decorated names by running Dumpbin.exe /exports foo.dll from the Visual Studio Command Prompt, that lists the name of the exported functions. Assuming your declaration was relevant, you'd see ?Foo##YAXXZ.
So the corresponding declaration in your C# program should be:
[DllImport("foo.dll", EntryPoint = "?Foo##YAXXZ",
ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
private static extern void Foo();
There are ways to change the C++ function declaration to make the C# declaration easier. Which is actually not a good idea, these decorated names actually help catch mistakes.
You should provide more information on your C++. Try using extern "C" __declspec(dllexport) instead. C++ exports with strange names so using extern "C" avoids that.
If you didn't declare it extern "C" in your dll, its name has likely been "mangled". You can use something like Dependency Walker to see what symbols your dll exports.
I created dll in c++ and wanted to call function from it using c#. I has an error if function, which program calls, returns string.
Dll code:
#include <string>
using namespace std;
#define EXPORT_API extern "C" __declspec(dllexport)
EXPORT_API void DllFunc()
{
MessageBoxA(0,"DDL box", "Yeah!", 0);
}
EXPORT_API string DllFuncStr()
{
return "testStr";
}
C# application code:
[DllImport("dllka.dll")]
static extern void DllFunc();
[DllImport("dllka.dll")]
static extern string DllFuncStr();
private void btnStart_Click(object sender, RoutedEventArgs e)
{
DllFunc();
string str = DllFuncStr();
}
"DllFunc();" - works nice, but on line "string str = DllFuncStr();" i had an error:
The runtime has encountered a fatal error. The address of the error was at 0x5e6dceca, on thread 0x16b0. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.
What is wrong with string type? How to fix this problem?
You can't marshal std::string from C++ to C#. Instead either use a zero-terminated string marshalled with StringBuilder, or return a BSTR.
The BSTR approach is quite simple. If you prefer zero-terminated string then look on the web for sample P/Invokes from Win32 APIs, e.g. GetWindowText().
You are exporting from C++ with cdecl calling convention, but using stdcall in the C# code. You'll need to match those up once you have sorted the data type marshalling out. It doesn't matter which you use, so long as it's the same at both ends.
You will also need to broach the fact that your C++ code uses char (8 bit encoding) and C# uses Windows native UTF-16.
If it was me, I'd do it with a BSTR, as I outline in this answer to another question.
As David said, c++ std::string and c# string are different things. I did it following way in my application:
c++ DLL code:
EXPORT_API void DllFuncStr(char* sRetText, int nCapacity)
{
if(!sRetText)
return;
std::string sRetTextStr("testStr");
strcpy_s(sRetText, nCapacity, sRetTextStr.c_str());
return;
}
c# application code:
[DllImport("dllka.dll")]
static extern bool DllFuncStr(StringBuilder sStrBuilder, int nCapacity);
private void btnStart_Click(object sender, RoutedEventArgs e)
{
StringBuilder strBuilder = new StringBuilder(50);
DllFuncStr(strBuilder, strBuilder.Capacity);
string str = strBuilder.ToString();
}
(Thank you Hans for suggestions. I will repair this thing in my application, too)