Как использовать С++/CLI внутри приложения С#

Я пытаюсь вызвать свою С++-библиотеку из моего приложения С# (через С++/CLI). Я последовал примеру этого вопроса (для моего конкретного приложения). Настройка моего приложения:

  • Project1: проект С++ (я компилирую это в DLL)
  • Project2: проект С++ (моя оболочка CLR, просто файл заголовка в примере выше, ссылки Project1)
  • Project3: проект С# (ссылки Project2)

К сожалению, когда я нахожусь для доступа к объекту оболочки CLR в моем приложении С#, я получаю следующую ошибку:

Тип или имя пространства имен 'YourClass' не удалось найти (вы не видите используя директиву или сборку ссылка?)

У меня неправильная настройка проекта, или есть что-то еще, на что я должен смотреть? (К сожалению, я не могу опубликовать код по проприетарным причинам, но это очень простой бит кода и легко следует приведенному выше примеру.)

Update:

Итак, я сделал именно то, что сказал Крис (см. ответ ниже), но я все еще получаю сообщение от своего приложения на С#: "Тип или имя пространства имен" MyProgram "не удалось найти (вам не хватает директивы using или ссылку на сборку?). Вот (макет) моего кода.

  • Project1 - это мое приложение на С++. Он компилирует/работает. Я использовал его в другом месте. (Я получаю DLL из этой сборки.)
  • Project2 - Вот мой код для моей оболочки.

MyWrapper.h

#pragma once

#include "myorigapp.h"

using namespace System;

namespace MyProgram
{
    public ref class MyWrapper
    {
    private:
        myorigapp* NativePtr;

    public:
        MyWrapper() 
        {
            NativePtr = new myorigapp();
        }

        ~MyWrapper() 
        { 
            delete NativePtr;
            NativePtr = NULL;
        }

        void dostuff()
        { 
            NativePtr->dostuff(); 
        }
    }
}
  • Project3 - это мое приложение С#.

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using MyProgram;

namespace Testing
{
    class Program
    {
        static void Main(string[] args)
        {
            MyWrapper p = new MyWrapper();
            p.dostuff();
        }
    }
}

Project3 ссылается на Project2, который ссылается на Project1. Все строит без ошибок (кроме ошибки, описанной выше в коде С# в строке using MyProgram).

Ответ 1

Хорошо, ну, теперь я чувствую себя глупым.

Оказывается, проблема, с которой я столкнулась (которую я решил пару недель назад, только что обновил этот ответ), заключалась в том, что я включил заголовочный файл (см. ответ Криса для этого), но у меня не было " t фактически включал файл CPP (который пуст, кроме включения файла заголовка).

Как только я это сделал, DLL скомпилировалась правильно, и я мог вызвать функции С++ (используя С++/CLI) из своего кода на С#.

Ответ 2

Просто включение заголовка из чистого приложения на С++ недостаточно. Вам необходимо обернуть неуправляемые объекты управляемыми в Project2 (т.е. public ref class YourClassDotNet)

#include "YourHeader.h"

namespace MyManagedWrapper
{
    public ref class YourClassDotNet
    {
    private:
        YourClass* ptr;

    public:
        YourClassDotNet()
        {
            ptr = new YourClass();
        }

        ~YourClassDotNet()
        {
            this->!YourClassDotNet();
        }

        !YourClassDotNet()
        {
            delete ptr;
            ptr = NULL;
        }

        void SomeMethod()
        {
            ptr->SomeMethod();
        }
    }
}

Ответ 3

Крис показал вам способ создания управляемого класса, который использует неуправляемый код внутри. Существует много того, что вы можете делать на С#, используя небезопасные (это просто так мало кто знает).

Однако также возможно обратное: использование типов .NET непосредственно из родного типа/функции.

Следует следить за тем, что любой управляемый указатель должен быть помечен как таковой. С этой целью С++/CLI определяет особый тип smartpointer gcroot<T> (например, имитация boost:: shared_pointer или std:: auto_ptr). Итак, чтобы сохранить управляемую строку внутри вашего класса С++, используйте следующее:

#include <string>
#include <vcclr.h>
using namespace System;

class CppClass {
public:
   gcroot<String^> str;   // can use str as if it were String^
   CppClass(const std::string& text) : str(gcnew String(text.c_str())) {}
};

int main() {
   CppClass c("hello");
   c.str = gcnew String("bye");
   Console::WriteLine( c.str );   // no cast required
}

Обратите внимание, что (если он не был исправлен в эти дни), вы столкнетесь с небольшим трением с несоответствием между управляемыми null и C/С++ NULL. Вы не можете легко напечатать, как и следовало ожидать:

gcroot<Object^> the_thing;
...
if (the_thing != nullptr)
 ...
}

Вместо этого вам нужно будет использовать собственный стиль (для этого используется смарт-обертка gcroot)

gcroot< Object^ > the_thing;
if ( the_thing != NULL ) {} // or equivalently...
if ( the_thing ) {}

// not too sure anymore, but I thought the following is also possible:
if ( the_thing != gcroot<Object>(nullptr) ) {}

Примечание. У меня нет доступа к машине Windows в любой точке дня, поэтому я процитировал из памяти