C# WPF передает строку UTF16 в функцию, которая принимает символ * - PullRequest
1 голос
/ 26 февраля 2020

Я создал проект wpf, который имеет вспомогательный класс c, который содержит весь мой бэкэнд-код на c ++. Одна из таких функций определяется как:

public static unsafe class Backend {

    [DllImport("Mandel.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    public extern static void write(void* ptr, char* path);
}
public partial class MainWindow : Window
    {
        public MainWindow()
        {
            string path = "mypath";
            InitializeComponent();
            unsafe
            {
                char *p; //convert
                void* myObj = Backend.init_obj(1920, 1080);
                Backend.gen(myObj);
                Backend.write(myObj, p);
            }
        }
    }

На самом деле void* ptr - это мой объект, отлитый для того, чтобы маршалировать его на сторону C#. Проблема, с которой я сталкиваюсь, состоит в том, что всякий раз, когда я пытаюсь вызвать это со строковым литералом в wpf, я получаю, что Visual C# не может преобразовать это, потому что строковые литералы кодируются в UTF16. Естественно, я пробовал много вещей, кроме ручного копирования соответствующих байтов в массив символов. Любые советы?

1 Ответ

1 голос
/ 26 февраля 2020

Одной из вещей, которые CLR может сделать очень хорошо для взаимодействия с кодом C / C ++, является распределение структур данных между управляемым и неуправляемым кодом. Поскольку строки очень важны, много работы ушло на то, чтобы как можно лучше сделать маршал строк.

В качестве дополнительного примечания вы используете void* для объекта контекста, который создан с помощью init и передан до write. Поскольку вы просто возвращаете его, вы можете заменить его на IntPtr и вообще избегать unsafe блоков. IntPtr всегда является размером указателя в текущей архитектуре.

Сначала давайте изменим объявление импортируемых функций. CharSet.Ansi сообщает маршальным строкам как ANSI. Параметр ptr становится IntPtr

[DllImport("Mandel.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public extern static IntPtr init(int width, int height);

[DllImport("Mandel.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public extern static void gen(IntPtr ptr);

[DllImport("Mandel.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public extern static void write(IntPtr ptr, string path);

И оттуда вы можете выяснить, как изменить функцию для освобождения ptr и любых других, которые вам нужно вызвать.

Использование этих функций становится намного проще и чище. Вам не нужен блок unsafe, и вы можете передать path непосредственно write.

public MainWindow()
{
    string path = "mypath";
    InitializeComponent();

    IntPtr myObj = Backend.init_obj(1920, 1080);
    Backend.gen(myObj);
    Backend.write(myObj, path);
}

Оригинальный комментарий, который заставил его работать:

Вместо того, чтобы пытаться создать параметр char* самостоятельно, измените объявление так, чтобы вторым параметром был string, и пусть среда выполнения соберет его для вас. Поскольку это строка ANSI, вы никогда не получите полную точность Unicode, но это проблема, созданная кодом C ++.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...