Как использовать С++/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 в любой точке дня, поэтому я процитировал из памяти