I am currently working on migrating our company's x86 software to x64 and have encountered a snag. Our method from our unmanaged dll that returns a char*
crashes with 0xc0000374 when the solution platform is switched to x64 and I'd like to know why.
C++ Native Code:
#include "stdafx.h"
#include <windows.h>
#include "windef.h"
#define VERSION "3.3.12"
extern "C"
{
__declspec(dllexport) char* WINAPI GetMyVersion()
{
return (char*)VERSION;
}
}
C# DllImport:
using System;
using System.Runtime.InteropServices;
using System.Security;
namespace InteropTest.Adapter
{
[SuppressUnmanagedCodeSecurity]
public unsafe class MyAdapter
{
private const string DLLName = "VersionNumber";
private const string EntryPointName = "GetMyVersion";
[DllImport(DLLName, EntryPoint = EntryPointName, CharSet = CharSet.Ansi)]
internal static extern IntPtr GetMyVersionPtr();
[DllImport(DLLName, EntryPoint = EntryPointName, CharSet = CharSet.Ansi)]
internal static extern string GetMyVersionString();
}
}
GetMyVersionString()
only works on x86 platforms and crashes on x64 ones. However, I am able to get the IntPtr
and to use the Marshal
class to convert it to a managed string
on both x86 and x64 like such:
using System.Runtime.InteropServices;
using System.Windows;
using InteropTest.Adapter;
namespace InteropTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
var ptr = MyAdapter.GetMyVersionPtr();
var convertedString = Marshal.PtrToStringAnsi(ptr);
// This crashes in x64 build
var directString = MyAdapter.GetMyVersionString();
VersionTextBlock.Text = convertedString;
}
}
}
I have also tried specifying the MarshalAs
Attribute for the return value explicitly without success. It seems like the 64 bit char*
is used as a 32 bit pointer if you do not explicitly use the Marshal
class. Is this just a bug in .NET or did I do something wrong? From My point of view, converting with the Marshal
class should be equivalent to using the MarshalAs
Attribute.
EDIT:
Here is a sample project I've made to replicate the bug: https://github.com/chenhuang444/dotNet40InteropCrash
EDIT 2:
The program and exits with code 0xc0000374 without native debugging and only states "InteropTest.exe has triggered a breakpoint" if I have native debugging on, with a stack trace of:
ntdll.dll!00007ffc0fdc4cfa() Unknown
ntdll.dll!00007ffc0fdcc806() Unknown
ntdll.dll!00007ffc0fdccad1() Unknown
ntdll.dll!00007ffc0fd69a55() Unknown
ntdll.dll!00007ffc0fd77347() Unknown
mscorlib.ni.dll!00007ffbd566759e() Unknown
[Managed to Native Transition]
> InteropTest.exe!InteropTest.MainWindow.OnLoaded(object sender, System.Windows.RoutedEventArgs e) Line 23 C#
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) Unknown
EDIT 3:
The unmanaged C++ code is a project in our solution and the x64/x86 configurations are set up, similar to the example project linked above.