2010-02-06 19 views
62

Tengo una clase implementada en C++ que es responsable del cálculo aritmético del programa, y ​​una interfaz que usa WPF. Proceso la entrada con C#, pero ¿cómo puedo usar mi clase de C++?¿Cómo puedo llamar a C++/CLI desde C#?

He visto algunos comentarios sobre cómo hacer que una clase administrada de contenedor de C++ interactúe con él, pero no sé por dónde empezar. Tampoco sé cómo iría a compilarlo junto con el resto del código. Realmente no puedo encontrar un tutorial sobre esto, y las cosas que google muestra en C++ administrado realmente no parecen útiles.

¿Hay algo por ahí para ayudarme? Esto no me parece irrazonable.

EDIT Ha intentado la solución m3rLinEz pero me está dando una BadImageFormatException, creo que es porque no se ha generado la DLL. Hice todo lo que le dije, no sé qué pasó. ¿Algunas ideas?

+0

Aquí hay un tutorial decente: http://www.codeproject.com/KB/mcpp/quickcppcli.aspx#A8 –

+1

Entiendo que natice C++ funciona más rápido que C#/WPF, pero ¿cuál es el costo general de escribir una CLI? envoltorio alrededor de su C++ nativo? ¿Todo este trabajo realmente lo vale? ¿Es C++ envuelto en CLI más rápido que simplemente portar el código a C++? –

+3

Arreglé mi BadImageFormatException porque mi proyecto CLI había predeterminado compilar en modo x86 y mi aplicación C# estaba en cualquier modo de CPU. Cambie o puede funcionar para usted. –

Respuesta

55

¿Has echado un vistazo a C++/CLI?

Permítanme darles un breve ejemplo. Aquí está el archivo fuente de un proyecto de Visual C++ -> CLR -> Class Library. Básicamente, obtiene el nombre de usuario de Windows y lo devuelve.

Tenga en cuenta que, para compilar esto, debe entrar en la configuración del proyecto y marcar "Additional Dependencies" como "Heredar de parent" porque estamos usando las librerías de Windows (kernel32.lib, user32.lib, ..)

// CSCPP.h 

#pragma once 

#include "windows.h" 

using namespace System; 

namespace CSCPP { 

    public ref class Class1 
    { 
     // TODO: Add your methods for this class here. 
    public: 
     String^ GetText(){ 
      WCHAR acUserName[100]; 
      DWORD nUserName = sizeof(acUserName); 
      if (GetUserName(acUserName, &nUserName)) { 
       String^ name = gcnew String(acUserName); 
       return String::Format("Hello {0} !", name); 
      }else{ 
       return gcnew String("Error!"); 
      } 
     } 
    }; 
} 

Ahora creamos un nuevo proyecto C# y agregamos una referencia a nuestro primer proyecto de biblioteca de clases C++/CLI. Y luego llama al método de instancia.

namespace CSTester 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      CSCPP.Class1 instance = new CSCPP.Class1(); 
      Console.WriteLine(instance.GetText()); 
     } 
    } 
} 

Esto dio el siguiente resultado en mi máquina:

Hola m3rlinez!

C++/CLI es básicamente una extensión administrada sobre el estándar C++. Le permite utilizar clases CLR y tipos de datos en su proyecto C++/CLI y también exponer esto al lenguaje administrado. Puede crear un contenedor administrado para su antigua biblioteca C++ usando esto. Hay algunas sintaxis raras como String^ para definir el tipo de referencia a CLR String. Encuentro "Quick C++/CLI - Learn C++/CLI in less than 10 minutes" para ser útil aquí.

+3

Para ir a la configuración de "Dependencias Adicionales" vaya a Proyecto -> Propiedades -> Propiedades de Configuración -> Enlazador – Kashif

4

Crearía una biblioteca de vínculos dinámicos estándar (no COM/gestionada) como described here y luego usaría DllImport attribute (invocación de plataforma) en el código C# para acceder a las funciones exportadas.

El punto clave de ese artículo:

Nota el __declspec (dllexport) modificador en las declaraciones de métodos en este código. Estos modificadores permiten que el método sea exportado por el archivo DLL de modo que pueda ser utilizado por otras aplicaciones . Para obtener más información, vea dllexport, dllimport.

Ésta es una alternativa de menor peso a una envoltura real interoperabilidad COM y evita problemas tales como el registro, etc (la DLL, simplemente se puede colocar en el directorio de la aplicación) .

Otra alternativa es It Just Works (IJW). Esta es probablemente una mejor opción si ha administrado el código C++ y necesita acceder a este desde otros lenguajes .NET. Pero esto es solo una opción si puede/feliz de convertir su C++ no administrado a C++ administrado.

+2

p/invocar es el término utilizado para describir esto. –

+0

He leído en muchos artículos que es lento, por lo que sugiero usar con precaución. Yo recomendaría usar C++ \ CLI y ensamblajes mixtos. – MasterMastic

3

Me mantendría alejado de P/Invoke, ya que es bastante lento en comparación con IJW (It Just Works). Este último le permite entrelazar sin problemas el C++ administrado y no administrado. Todo lo que tiene que hacer es crear un ensamblado administrado de C++, escribir una clase administrada que sea visible desde C# y llamar el código no administrado fuera de eso.

Uhm ... OK. Tenía la impresión de que las llamadas de P/Invoke eran más lentas, y no lo son de manera hereditaria. Sin embargo, al tener un control explícito sobre la clasificación, puede hacer que su versión de C++/CLI tenga un mejor rendimiento en muchos de los casos.

Aquí está el artículo de Microsoft sobre los dos mecanismos:

http://msdn.microsoft.com/en-us/library/ms235282.aspx

Ventajas de IJW

  • No hay necesidad de escribir DLLImport atributo declaraciones de los API no administradas del programa usos. Solo incluye el archivo de encabezado y el enlace con la biblioteca de importación.
  • El mecanismo IJW es ligeramente más rápido (por ejemplo, los stubs IJW no necesitan comprobar la necesidad de fijar o copiar elementos de datos porque el desarrollador lo hace explícitamente ).
  • Ilustra claramente los problemas de rendimiento. En este caso, el hecho de que está traduciendo desde una cadena Unicode a una cadena ANSI y que tiene una asignación de memoria asistente y desasignación. En este caso, un desarrollador de que escribe el código usando IJW se daría cuenta de que llamar a _putws y usando PtrToStringChars sería mejor para el rendimiento.
  • Si llama a muchas API no administradas utilizando los mismos datos, marcándola una vez y pasando la copia marshaled es mucho más eficiente que volver a calcular cada vez.

hay ventajas estéticas, así:

  • C# código es el código C# sin ninguna rareza interop'y.
  • Usted no tiene que definir DLLImport atributo, no tienen que definir cualquiera de las estructuras de datos (también con p/invocar atributos específicos) que podría tener este aspecto:

    [StructLayout (LayoutKind. Secuencial, CharSet = CharSet.Ansi)] public struct DevMode { [MarshalAs (UnmanagedType.ByValTStr, SizeConst = 32)] public string dmDeviceName; }

  • No tiene que convertir todos los tipos de primitivos de parámetros en sus homólogos .NET (hay una tabla en this page que enumera cómo se asignan los tipos administrados a los no administrados).
  • Tienes la oportunidad de trabajar con C++/CLI, que es muy divertido de aprender y está realmente pulido. Ha recorrido un largo camino desde VS 2003 y ahora es un lenguaje .NET con todas las funciones. La documentación de Microsoft es bastante buena, al igual que toda la información de IJW.
  • Hacer la interoperabilidad de C++ en C++/CLI se siente muy natural en comparación con C#. Esto es completamente subjetivo, pero preferiría hacer marshalling en C++ que haga Marshal.PtrToString(ptr).
  • Si expone una API, probablemente desee envolver todas las cosas de P/Invoke en otra capa, para que no tenga que lidiar con P/Invocar fealdad. De esta manera usted tiene la sobrecarga de toda la clasificación Y la capa C# alrededor de ella. Con C++/CLI, la clasificación y la abstracción de interoperabilidad están en un solo lugar, y puede elegir la cantidad de coordinación que necesita.

IMHO si está llamando a una función impar en Windows SDK, vaya con P/Invoke. Si está exponiendo una API de C++ moderadamente compleja al mundo administrado, definitivamente C++/CLI.

+0

Excepto que el OP habla sobre la creación de un contenedor administrado que he leído diciendo que ya tienen un código no administrado. Sin embargo, creo que una re-escritura en C++ administrado puede ser una opción. – Ash

+0

¿Quiere decir que la llamada de P/Invoke es comparativamente lenta o es más un impedimento de rendimiento que solo eso? –

+0

De acuerdo con la página Mono en C++, IJW THUNKS SON p/invocación de llamadas. http://www.mono-project.com/CPlusPlus –

8

Hay al menos tres formas de llamar a código no administrado desde logró en el mismo proceso:

  1. C++/CLI
  2. invocación de plataforma
  3. Envuelva su C++ en un objeto COM

En el trabajo usamos C++/CLI para esto, parece funcionar.

+3

Y ahora está [CXXI] (http://stackoverflow.com/a/9247254/332026) (pronunciado sexy) . – Justin

+2

Y CXXI ​​parece haberse transformado en [CppSharp] (https://github.com/mono/CppSharp) –

+0

¿Y qué tipo de software escribe en el trabajo? ¿Escribes código de alto rendimiento donde la velocidad es esencial? –