.NET 4.0 char * в строку Interop Crashes On x64 0xc0000374 - PullRequest
0 голосов
/ 11 октября 2018

В настоящее время я работаю над переносом программного обеспечения x86 нашей компании на x64 и столкнулся с проблемой.Наш метод из нашей неуправляемой dll, которая возвращает char*, аварийно завершает работу с 0xc0000374, когда платформа решения переключается на x64, и я хотел бы знать почему.

Собственный код C ++:

#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() работает только на платформах x86 и вылетает на платформах x64.Однако я могу получить IntPtr и использовать класс Marshal для преобразования его в управляемый string на x86 и x64, например:

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;
        }
    }
}

Я также попытался указатьMarshalAs Атрибут для возвращаемого значения явно без успеха.Кажется, что 64-битный char* используется в качестве 32-битного указателя, если вы явно не используете класс Marshal.Это просто ошибка в .NET или я сделал что-то не так?С моей точки зрения, преобразование с классом Marshal должно быть эквивалентно использованию атрибута MarshalAs.

РЕДАКТИРОВАТЬ:

Вот пример проекта, который я сделал для репликацииошибка: https://github.com/chenhuang444/dotNet40InteropCrash

РЕДАКТИРОВАТЬ 2:

Программа и завершает работу с кодом 0xc0000374 без встроенной отладки и сообщает только, что «InteropTest.exe вызвал точку останова», если у меня включена встроенная отладка, страссировка стека:

    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

РЕДАКТИРОВАТЬ 3:

Неуправляемый код C ++ - это проект в нашем решении, и настройки x64 / x86 настроены, как и в примере, приведенном выше..

1 Ответ

0 голосов
/ 11 октября 2018

Ну, ваша версия не является "строкой", это действительно указатель на необработанную память..NET Framework будет пытаться освободить этот указатель, если вы скажете, что это строка.

Итак, у вас есть два варианта:

  • использовать IntPtr, как вы, что нормально.
  • выделите настоящую строку, которую может понять .NET, используя COM-распределитель, поэтому в вашем случае ваш код C ++ может быть заменен следующим:

.

__declspec(dllexport) char* WINAPI GetMyVersion() {
      char* p = (char*)CoTaskMemAlloc(7);
      CopyMemory(p, VERSION, 7);
      return p;
}

примечание: вы также можете использовать BSTR.

...