2015-05-18 4 views
1

Хорошо, я предварю это, сказав, что я действительно просмотрел и прочитал много подобных вопросов и ответов здесь, прежде чем публиковать это.C# Пространство имен не найдено для DLL

Справочная информация Использование Visual Studio Professional 2013

Цель: Этот проект для собственного удовольствия и попытаться узнать как я могу.

У меня есть собственный заголовочный файл C++, называемый BinaryTree.h, который представляет собой просто простую структуру данных, которая использует рекурсивную логику для построения двоичного дерева поиска. Он отлично работает сам по себе.

Я хочу создать GUI в C#, который использует это. (Это не очень полезно или практично, я просто выбираю его, ну, потому что я хотел. Кроме того, хотя логика внутри двоичного древовидного класса сложна (ish), мне нужно только вызвать 2 метода, метод addNode и toString метод, который возвращает максимальную глубину и количество узлов).

Я выбираю использование обертки C++/cli для этого. Все, казалось, идет хорошо, сборка была успешной, и файл DLL был создан в каталоге отладки моего проекта.

Теперь я начал на стороне C#. Я добавил файл .dll к ссылкам. Однако, когда я набрал «using filename.dll;» Я получил сообщение об ошибке «Тип или пространство имен не найдено ...».

Чтобы повторить, я провел некоторое исследование. Я обнаружил (казалось, в VS2010), что различные целевые структуры могут вызвать эту ошибку. Я проверил мой, цели для обоих были net4.5, так что это не проблема.

Вот код моей оболочки C++/cli. Возможно, это связано с использованием шаблонов? Любая помощь приветствуется.

#pragma once 

#include "D:\Schoolwork 2015\Test Projects\CPPtoC#WrapperTest\CPPLogic\CPPLogic\BinaryTree.h" 

using namespace System; 

namespace BinaryTreeWrapper { 

    template<class Data> 
    public ref class BinaryTreeWrapperClass 
    { 
    public: 
     BinaryTreeWrapperClass(){ tree = new BinaryTree(); } 
     ~BinaryTreeWrapperClass(){ this->!BinaryTreeWrapperClass(); } 
     !BinaryTreeWrapperClass(){ delete tree; } 

     //methods 
     void wrapperAddNode(Data) 
     { 
      tree->addNode(Data); 
     } 
     std::string wrapperToString() 
     { 
      return tree->toString(); 
     } 

    private: 
     BinaryTree* tree; 
    }; 
} 

Скриншот ошибки:

Error

EDIT Итак, вот странно ... мой оригинальный файл построен очень хорошо с новым кодом и произвел. dll. Однако я решил попробовать новый проект, поскольку пространство имен все еще не найдено. При перемещении код снова и пытается построить, я столкнулся с 4-х ошибок:

Error 1 Ошибка C2955: 'BinaryTree': использование шаблона класса требует списка шаблонов аргумент

Ошибка 2 Ошибка C2512: «BinaryTree ': нет необходимости конструктор по умолчанию доступен

ошибка 3 ошибка C2662: 'пустота BinaryTree :: AddNode (Data)': не удается преобразовать 'это' указатель из 'BinaryTree' до 'BinaryTree &'

Error 4 ошибка C2662 : 'std :: string BinaryTree :: toString (void) const': не может преобразовать 'this' указатель из 'BinaryTre e 'to' const BinaryTree & '

Я скопировал код точно, только изменив пространство имен на «TreeWrapper» и имя класса на «TreeWrapperClass».

Чтобы помочь, у меня есть фрагмент из моего файла BinaryTree.h. Существует еще одна группа, которая определяет класс «NODE», но я не хотел загромождать его больше, чем мне нужно.

После дальнейших исследований, похоже, проблема заключается в использовании «generic». Если я переключу его все на «шаблон», он строит просто отлично, но тогда он не может использоваться как ссылка в C# (получение ошибки пространства имен). Я построил тестовый проект, используя очень простые методы (без шаблонов) и смог использовать обертку .dll, которую я сделал на C#. Поэтому проблема заключается в шаблонах и дженериках.

Последнее редактирование Я нашел, если я изменить код, чтобы начать шаблон как ВНУТР он работает просто отлично, и я могу использовать его в C#. Например:

... 
BinaryTreeWrapperClass(){ tree = new BinaryTree<int>(); } 
.... 
private: 
BinaryTree<int>* tree; 

BinaryTree.h

template<class Data> 
class BinaryTree 
{ 
private: 
    Node<Data>* root; 
    unsigned int nNodes; 
    unsigned int maxDepth; 
    unsigned int currentDepth; 
    void traverse(Node<Data>*& node, Data data); 
public: 
    BinaryTree(); 
    ~BinaryTree(); 
    void addNode(Data); 
    std::string toString() const 
    { 
     std::stringstream sstrm; 
     sstrm << "\n\t" 
      << "Max Depth:  " << maxDepth << "\n" 
      << "Number of Nodes: " << nNodes << "\n"; 
     return sstrm.str(); // convert the stringstream to a string 
    } 

}; 

template<class Data> 
BinaryTree<Data>::BinaryTree() //constructor 
{ 
    //this->root = NULL; 
    this->root = new Node<Data>();  //we want root to point to a null node. 
    maxDepth = 0; 
    nNodes = 0; 
} 

template<class Data> 
BinaryTree<Data>::~BinaryTree() //destructor 
{ 

} 

template<class Data> 
void BinaryTree<Data>::addNode(Data data) 
{ 
    traverse(root, data); //call traverse to get to the node 
    //set currentDepth to 0 
    currentDepth = 0; 
} 

template<class Data> 
void BinaryTree<Data>::traverse(Node<Data>*& node, Data data) 
{ 
    //increment current depth 
    currentDepth++; 

    if (node == NULL)  //adds new node with data 
    { 
     node = new Node<Data>(data); 
     //increment nNode 
     nNodes++; 
     //increment maxDepth if current depth is greater 
     if (maxDepth < currentDepth) 
     { 
      maxDepth = currentDepth - 1;  //currentDepth counts root as 1, even though its 0; 
     } 
     return; 
    } 

    else if (node->getData() >= data)  //case for left, getData must be bigger. The rule is, if a number is equal to getData or greater, it is added to the left node 
    { 
     Node<Data>* temp = node->getLeftNode(); 
     traverse(temp, data);    //recursive call, going down left side of tree 
     node->setLeftNode(temp); 
    } 
    else if (node->getData() < data)  //case for right, getData must be less 
    { 
     Node<Data>* temp = node->getRightNode(); 
     traverse(temp, data); 
     node->setRightNode(temp); 
    } 
    return; 
} 
+0

Я создал пустой проект библиотеки классов и скомпилировал его, а затем импортировал в проект C# и не смог дублировать эту проблему. Visual Studio увидела новое пространство имен немедленно. Я бы предложил запустить варианты сборки и убедиться, что 'Common Language Runtime Support' установлен в'/clr' – ahwm

+0

Это очень странно. Я проверяю, чтобы этот параметр был установлен в/clr, и это так. Я также попытался создать новый проект библиотеки, с той же проблемой. – Orannis

+1

@ahwm это не проблема здесь (я напишу ответ, чтобы объяснить, что не так с этим кодом) –

ответ

2

Вы объявляя template, но на самом деле не инстанцировании его. Шаблоны C++/CLI похожи на шаблоны C++ - если вы их не создаете, они просто не существуют вне модуля компиляции.

Вы ищете дженериков здесь (да, C++/CLI имеет как шаблонов и дженериков). А вот как вы объявляете родовое в CLI C++ /:

generic<class Data> 
public ref class BinaryTreeWrapperClass 
{ 
    // ... 
} 

Но вы застряли в этой точке по нескольким причинам.

Во-первых, я буду включать детали, которые OK:

generic<class Data> 
public ref class BinaryTreeWrapperClass 
{ 
public: 
    BinaryTreeWrapperClass(){ tree = new BinaryTree(); } 
    ~BinaryTreeWrapperClass(){ this->!BinaryTreeWrapperClass(); } 
    !BinaryTreeWrapperClass(){ delete tree; } 

private: 
    BinaryTree* tree; 
}; 

Вы получили это право.

Далее, давайте посмотрим на:

std::string wrapperToString() 
{ 
    return tree->toString(); 
} 

Это не хорошо, так как вы возвращаете std::string - вы не хотите использовать, что с C#, так что давайте возвращать System::String^ вместо (с использованием marshal_as) :

#include <msclr/marshal_cppstd.h> 
System::String^ wrapperToString() 
{ 
    return msclr::interop::marshal_as<System::String^>(tree->toString()); 
} 

Здесь, что гораздо лучше для использования в C#.

И, наконец, это:

void wrapperAddNode(Data) 
{ 
    tree->addNode(Data); 
} 

См ... здесь вам придется сделать некоторые реальные Interop. Вы хотите передать управляемый объект на родной для хранения. GC будет мешать вам.

GC имеет разрешение переместить любой управляемый объект (переместить его в другое место памяти), но ваш собственный код не знает об этом. Вам понадобится pin объект, чтобы GC не двигал его.

Существует несколько способов сделать это, и я не знаю, как выглядит BinaryTree::addNode, но я полагаю, что это BinaryTree::addNode(void*).

Для долгосрочного закрепления объекта вы можете использовать GCHandle.

Полный код выглядит следующим образом:

generic<class Data> 
public ref class BinaryTreeWrapperClass 
{ 
public: 
    BinaryTreeWrapperClass() 
    { 
     tree = new BinaryTree(); 
     nodeHandles = gcnew System::Collections::Generic::List<System::Runtime::InteropServices::GCHandle>(); 
    } 

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

    !BinaryTreeWrapperClass() 
    { 
     delete tree; 
     for each (auto handle in nodeHandles) 
      handle.Free(); 
    } 

    void wrapperAddNode(Data data) 
    { 
     auto handle = System::Runtime::InteropServices::GCHandle::Alloc(
      safe_cast<System::Object^>(data), 
      System::Runtime::InteropServices::GCHandleType::Pinned); 
     nodeHandles->Add(handle); 
     tree->addNode(handle.AddrOfPinnedObject().ToPointer()); 
    } 

    System::String^ wrapperToString() 
    { 
     return msclr::interop::marshal_as<System::String^>(tree->toString()); 
    } 

private: 
    BinaryTree* tree; 
    System::Collections::Generic::List<System::Runtime::InteropServices::GCHandle>^ nodeHandles; 
}; 

Это выделяет GCHandle для каждого узла, и сохраняет его в списке, чтобы освободить его позже. Освобождение дескриптора pinned создает ссылку на объект (поэтому он становится коллекционируемым, если ничто иное не ссылается на него), а также его закрепленный статус.

+0

Большое спасибо. Система :: String^имеет большой смысл, я не могу поверить, что я забыл об этом. Я вообще не знаком с кли, и это просто пошло мне в голову. GCHandle также имеет большой смысл. Я не думал о том, чтобы прикрепить его, чтобы родной класс знал, где он. Ключевое слово 'auto' похоже на «var» в native C++ correct? Снова, благодарю вас за углубленный и информативный ответ. – Orannis

+0

Есть ли определенный синтаксис для использования сгенерированного DLL в моей программе на C#? Я все еще получаю «Тип или пространство имен не найдено». Я использую синтаксис «Использование BinaryTreeWrapper;». Это имя .dll и пространства имен. Может ли это вызвать конфликт? – Orannis

+0

В C++ (и C++/CLI) 'auto' совпадает с' var' в C#. И нет специального синтаксиса для использования dll, он должен работать, если вы правильно настроили свою ссылку. Вы также можете попытаться установить платформу проекта C# так же, как и проект C++/CLI. –

0

После копания вокруг я думаю, что нашел ответ, хотя и не окончательно, поэтому, если кто-нибудь сможет перезвонить, я был бы признателен. (Извиняюсь заранее за любое неправильное использование лексики, но я думаю, что я могу понять эту идею).

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

По крайней мере, я достиг одной цели своего проекта, который учился так, как я могу ха-ха. Я смог получить обложки C++/cli для работы без шаблонов, и если я выберу тип шаблона перед созданием .dll (см. Выше)

Если у кого-либо есть идея, пожалуйста, дайте мне знать.

Смежные вопросы