Как получить доступ к классу в С++/CLI из С#?

Я пишу инструмент графического интерфейса в С# для анализа и отображения вывода данных другой программы, написанной на C. Для анализа данных мне нужно знать структуры данных, которые указаны в ряде файлов заголовков C. Таким образом, мне нужно включить эти файлы заголовков C в мой проект С#. Мои вопросы:

1) После некоторых исследований я пришел к выводу, что лучший способ - создать новый проект С++/CLI в моем решении, импортировать файлы заголовков C в этот новый проект, написать несколько классов С++/CLI, которые действуют как тонкие обертки для структур данных, определенных в файлах заголовков C, затем ссылайтесь на классы оболочки С++/CLI из кода С#. Это лучший подход, или есть лучший способ?

2) Я столкнулся с проблемой сравнения. Вот мой упрощенный код, чтобы проиллюстрировать проблему:

Исходный заголовок C в проекте С++/CLI

#define ABC  0x12345

Класс Wrapper в проекте С++/CLI

#include "my_c_header.h"

namespace C_Wrappers {
    public ref class MyWrapper {
        public:
            property unsigned int C_ABC {
                unsigned int get() { return ABC; }
            }
    }
 }

Пользовательский класс в проекте С#

using C_Wrappers;
using System;

namespace DataViewer {
    public partial class MainForm : Form {
        private  MyWrapper myWrapper = new MyWrapper();
        public MainForm() {
            Console.WriteLine(myWrapper.C_ABC.ToString());
        }
    }
 }

В проекте С# я добавил ссылку на проект С++/CLI (используя правый щелчок > Добавить ссылку). Во время сборки я получил ошибку в проекте С#: "Тип или пространство имен" C_Wrappers "не удалось найти (вам не хватает директивы using или ссылки на сборку?).

Я думал, что сделал все, что должен был. Что делать, чтобы исправить эту ошибку?

Ответ 1

Я нашел эту ссылку невероятно полезной, когда мне нужно было связать С++ с С# с WPF. К сожалению, это написано на французском языке, поэтому я связал переведенную версию, предоставленную google.

В моем собственном решении у меня было 4 проекта:

  • проект С++ и тестовый код
  • проект С++ DLL, который только компилирует DLL из первого источника проекта с использованием динамических ссылок
  • проект оболочки, который является только адаптером, использующим С++/CLI, который обертывает необработанный С++
  • проект С# WPF, который был моим графическим интерфейсом.

Вот мой перевод предоставленной ссылки выше.

Проект DLL на С++

Сделайте свой код на С++ в библиотеку DLL.

  • Внутри Visual Studio перейдите в Файл > Создать > Проект, выберите Проект Win32 на вкладке Visual С++.
  • Выберите имя для проекта и решения, решение будет иметь все проекты внутри.
  • Внутри помощника для приложения Win32 нажмите далее, установите флажок DLL, затем Пустое проект, затем нажмите Готово.

Project creationProject assistant

Код для добавления

Это мой заголовок С++ для dll (минус много вещей).

Token.h

#pragma once
#define DLLEXP   __declspec( dllexport )

DLLEXP void pop_back(std::string& str);

DLLEXP std::string testReturnString();

DLLEXP int getRandomNumber();

В CPP ничего не изменится.

Создайте проект, у вас должна быть DLL и LIB файл для включения в файл отладки проекта С#.

Обертка С++/CLI

Этот проект служит интерфейсом между собственным кодом из предыдущего проекта и управляемым кодом GUI.

  • Добавить новый проект (библиотека классов в Visual С++) (называемый "Wrapper" в этом примере) к решению
  • Добавьте путь к исходному проекту с дополнительными каталогами Include
  • Добавьте исходный проект в качестве ссылки для нового проекта (r ight click > Ссылки... > Добавить новую ссылку)
  • В Свойствa > Линкерa > Ввод поместите имя родной DLL в поле "Задержка загрузки DLL" (Computations.dll в этом примере)

Код С++/CLI

Моя обертка - это только класс, который выглядит примерно так (минус мой собственный код).

Wrapper.h

#include "Token.h" // include your C++ header

#include <string>
#include <iostream>

namespace Wrapper {

    // noticed the ref?
    public ref class TokenAnalyzer
    {

    public:
        TokenAnalyzer(){
        };

        void Init();
            // use the C++/CLI type, like String^ (handles)
        System::String^ ProcessLine(int lineNbr, System::String^ line);
    };
}

Ничего особенного внутри CPP, кроме того, что вы должны включить #include "stdafx.h".

Он также должен встраиваться в DLL, которую вы будете включать в директорию отладки С#.

Просто полезная функция, которую я нашел где-то на SO, но не помню, где это может понадобиться. Он преобразует дескриптор С++/CLI String в стандартную строку С++.

std::string MarshalString (String ^ s) {
        using namespace Runtime::InteropServices;
        const char* chars = 
            (const char*)(Marshal::StringToHGlobalAnsi(s)).ToPointer();
        std::string os = chars;
        Marshal::FreeHGlobal(IntPtr((void*)chars));
        return os;
    }

Проект С#

Добавьте к решению новый проект (С# Windows Form или WPF или что угодно!) и установите его как проект запуска ( нажмите > установить как проект запуска).

  • Добавить проект С++/CLI в качестве ссылки для нового проекта
  • Добавить директиву using Wrapper; в форме исходного кода

Используйте его как:

/// Store the C++/CLI Wrapper object.</summary>
private Wrapper.TokenAnalyzer mTokenAnalyzer = new TokenAnalyzer();

Обновление 2016/05/06

Ashwin потратил время на создание образца проекта и учебник по блогам, который может помочь в дальнейшем.

Ответ 2

Для меня ответ на этот аналогичный вопрос решил ту же ошибку в С#.

(т.е. у меня был заголовочный файл оболочки в моем проекте С++\CLI, но забыли также включить файл .cpp(хотя он содержит только две строки: #include "stdafx.h" и #include "Wrapper.h" ))