2011-04-13 20 views
12

Estoy intentando llamar a mi biblioteca C++ desde mi aplicación C# (a través de C++/CLI). Seguí el ejemplo de this question (para mi aplicación específica). La configuración de mi solicitud es:Cómo usar C++/CLI dentro de la aplicación C#

  • Proyecto1: Proyecto C++ (que compila a un archivo DLL)
  • Project2: C++ Proyecto (mi envoltorio CLR, sólo el archivo de cabecera por el ejemplo anterior, las referencias Proyecto1)
  • proyecto3: C# Proyecto (referencias Project2)

por desgracia, cuando realmente vaya a acceder al objeto envoltorio CLR en mi aplicación C#, recibo el siguiente error:

The type or namespace name 'YourClass' could not be found (are you missing a using directive or an assembly reference?)

¿Tengo la configuración del proyecto incorrectamente, o hay algo más que debería investigar? (Por desgracia, no puedo enviar el código por razones de propiedad, pero es un poco muy simple de código y fácilmente sigue el ejemplo anterior.)

Actualización:

así lo hice exactamente lo que Chris dice que lo hace (Consulte la respuesta a continuación), pero aún recibo un mensaje de mi aplicación C# que dice "No se encontró el tipo o el nombre del espacio de nombres 'MiPrograma' (¿falta una directiva de uso o una referencia de ensamblado?). Aquí hay un (simulacro -up) de mi código.

  • Proyecto1 - Esta es mi aplicación C++. Compila/funciona. Lo he usado en otros lugares. (I obtener una DLL de esta compilación.)
  • Proyecto2 - Aquí está mi código para mi envoltorio.

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(); 
     } 
    } 
} 
  • proyecto 3 - Este es mi C# aplicación.

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(); 
     } 
    } 
} 

proyecto3 referencia Project2 que hace referencia a Project1. Todo se genera sin errores (excepto el error que describí anteriormente en el código C# en la línea using MyProgram).

+0

¿falta una directiva de uso o una referencia de ensamblado? –

+0

* debe * publicar algún código, si no desea publicar el original, quizás puede extraer las partes relevantes, simplificar y modificar algunos nombres, etc. –

+0

@Hans @Doc - Actualicé la pregunta con el código y más información . – JasCav

Respuesta

3

Bien, bien, ahora me siento tonto.

Resulta que el problema que tenía (que he resuelto hace un par de semanas - acabo en torno a la actualización de esta respuesta) era que yo había incluido el archivo de cabecera (véase la respuesta de Chris para eso), pero no me había 'en realidad, incluyó el archivo CPP (que está vacío, aparte de incluir el archivo de encabezado).

Una vez que hice esto, la DLL compiló correctamente y pude llamar a las funciones C++ (usando C++/CLI) desde mi código C#.

+0

Probé su ejemplo y también generé un dll pero no puedo usarlo en el programa C#. Cuando veo el dll en el navegador de objetos, está vacío. No creo haber entendido bien tu solución propuesta. – dwbrito

+2

Estoy teniendo el mismo problema ahora, pero no puedo entender su solución. Tal vez puedas explicarlo? Gracias – VladL

6

Solo incluir el encabezado de una aplicación C++ pura no es suficiente. Es necesario para envolver los objetos no administrados con los administrados en Project2 (es decir 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(); 
     } 
    } 
} 
+2

+1. No olvide mencionar que esos métodos de 'MyManagedWrapper' generalmente tendrán que hacer alguna conversión de tipos de datos .NET a tipos de datos nativos de C++ y viceversa. –

+0

@Doc: [a su servicio] (http://stackoverflow.com/questions/5655936/how-to-use-c-cli-within-c-application/5656223#5656223), supongo? – sehe

+0

Asegurar manualmente que el objeto nativo se libera correctamente se complica rápidamente. Sugiero usar un puntero inteligente en lugar de duplicar esa lógica en cada clase contenedora. [Este es uno que escribí: scoped_ptr para C++/CLI (asegúrese de que el objeto administrado libere apropiadamente el objeto nativo de propiedad)] (http://codereview.stackexchange.com/q/1695/2150) –

2

Chris que mostró el camino para crear una clase administrada que utiliza código no administrado en el interior. Hay mucho de eso que puedes hacer en C# usando inseguro (es solo que casi nadie lo hace).

Sin embargo, también es posible lo contrario: utilizar tipos .NET directamente desde un tipo/función nativo.

Lo que hay que tener en cuenta es que cualquier puntero administrado debe marcarse como tal.Para este propósito, C++/CLI define un tipo especial de smartpointergcroot<T> (imitando boost :: shared_pointer o std :: auto_ptr en cierto modo). Así que para almacenar una cadena gestionada dentro de la clase C++, utilice la siguiente:

#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 
} 

Tenga en cuenta que (si no se ha fijado en estos días) se encontrará con un poco de fricción con el desajuste entre lograron null y C/C++ NULL. No se puede escribir fácilmente, como era de esperar:

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

lugar que tendría que utilizar el estilo nativo (la envoltura inteligente gcroot maneja esto)

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)) {} 

Nota :No tengo acceso a una máquina de Windows en cualquier lugar cerca de estos días, así que he citado de memoria

+0

Para que esto funcione, debe activar CLI para el proyecto (es decir, esto no funciona para una aplicación pura de C++). –

+1

O compilar con el indicador/clr – sehe

Cuestiones relacionadas