2011-01-14 17 views
7

Tengo una consola C++ Exe que hace algunos programas. Ahora quería escribir una GUI de C# que hace parte de la programación que hace el C++ exe. Estaba pensando en algunos enfoques,Escribiendo C# GUI sobre un C++ dll o C++ exe

  1. Escribir el C# GUI con toda la programación en C++ hecho desde cero. (No quiero hacer esto por la cantidad de trabajo que conlleva)
  2. Construir un C++ DLL que hace la programación y la ha importado en la aplicación GUI. (Ahora aquí tengo una preocupación. ¿Cómo capturo el resultado de las rutinas en C++ dll y lo visualizo en la GUI? ¿Debo devolver el resultado como una cadena para cada rutina que la aplicación llamadas.? Dado que no sé administrado C++ iam va a construir un dll C++ no administrado.)
+0

¿Ha considerado la adición de una interfaz COM? Dado que, en mi humilde opinión, pasar los argumentos y los datos del programa con contenedores COM puede ser una solución. –

Respuesta

7

Construir un dll C++/CLI realmente no es tan difícil. Básicamente utiliza código C++ no administrado, excepto que define un "public ref class" que aloja las funciones que desea que vea el código C#.

¿Qué tipo de datos está devolviendo? Números individuales, matrices de números, objetos complejos?

ACTUALIZACIÓN: Dado que se ha aclarado que la "salida" es iostreams, here is a project que demuestra la redirección de cout a una aplicación .NET que llama a la biblioteca. Redirigir clog o cerr simplemente requeriría un puñado de líneas adicionales en DllMain con el patrón después de la redirección existente.

The zip file incluye archivos de proyectos VS2010, pero el código fuente también debería funcionar en el año 2005 o 2008.

La funcionalidad de captura iostreams está contenida en el siguiente código:

// compile this part without /clr 
class capturebuf : public std::stringbuf 
{ 
protected: 
    virtual int sync() 
    { 
     // ensure NUL termination 
     overflow(0); 
     // send to .NET trace listeners 
     loghelper(pbase()); 
     // clear buffer 
     str(std::string()); 
     return __super::sync(); 
    } 
}; 

BOOL WINAPI DllMain(_In_ HANDLE _HDllHandle, _In_ DWORD _Reason, _In_opt_ LPVOID _Reserved) 
{ 
    static std::streambuf* origbuf; 
    static capturebuf* altbuf; 
    switch (_Reason) 
    { 
    case DLL_PROCESS_ATTACH: 
     origbuf = std::cout.rdbuf(); 
     std::cout.rdbuf(altbuf = new capturebuf()); 
     break; 
    case DLL_PROCESS_DETACH: 
     std::cout.rdbuf(origbuf); 
     delete altbuf; 
     break; 
    } 

    return TRUE; 
} 

// compile this helper function with /clr 
void loghelper(char* msg) { Trace::Write(gcnew System::String(msg)); } 
+0

Quiero mostrar los mensajes de registro que provienen de la DLL de C++ en la GUI de C#. – excray

+0

@ user97642: ¿Entonces la DLL de C++ está escribiendo en 'std :: clog' y desea enviar esta salida a C#? –

+0

Sí clog o cout o cerr. – excray

3

¿Entonces solo quiere llamar a la biblioteca C++ desde el código .net administrado?

Luego necesitaría construir un objeto COM o una biblioteca p-invokable en C++. Cada enfoque tiene sus propios pros y contras según las necesidades de su negocio. Tendría que ordenar los datos en su consumidor. Hay toneladas y toneladas de material en ambos conceptos.

0

Puede escribir simplemente un contenedor GUI de C# (como sugiere en la opción 2) y engendrar el proceso C++; sin embargo, esto será un poco lento (no sé si eso importa).

Para ejecutar su C++ exe y capturar el resultado, puede usar el ProcessRunner que armé. Aquí está el uso básico:

using CSharpTest.Net.Processes; 
partial class Program 
{ 
    static int Main(string[] args) 
    { 
     ProcessRunner run = new ProcessRunner("svn.exe", "update"); 
     run.OutputReceived += new ProcessOutputEventHandler(run_OutputReceived); 
     return run.Run(); 
    } 

    static void run_OutputReceived(object sender, ProcessOutputEventArgs args) 
    { 
     Console.WriteLine("{0}: {1}", args.Error ? "Error" : "Output", args.Data); 
    } 
} 
+0

Estoy buscando una manera de descubrir la segunda opción. Una GUI de C# que usa una DLL de C++. – excray

0

Probablemente la mejor manera de ir aquí es el uso de P/Invoke o invocación de plataforma. Dependiendo de la estructura o su interfaz C++ dll, es posible que desee envolverlo en una interfaz C pura; es más fácil si su interfaz solo usa tipos blittable. Si limita su interfaz dll a tipos blittables (Int32, Single, Boolean, Int32 [], Single [], Double [] - los conceptos básicos) no necesitará hacer una compleja recopilación de datos entre managed (C#) y no administrados (C) espacios de memoria.

Por ejemplo, en su código C# define las llamadas disponibles en su dll C/C++ utilizando el atributo DllImport.

[DllImport, "ExactDllName.dll"] 
static extern boolean OneOfMyCoolCRoutines([In] Double[] x, [In] Double[] y, [Out] Double result) 

La pequeña [A] 's y [Fuera]' s no son estrictamente necesarias, pero pueden acelerar el proceso. Ahora que ha agregado su "ExactDllName.dll" como referencia para su proyecto C#, puede llamar a su función C/C++ desde su código C#.

fixed(Double *x = &x[0], *y = &y[0]) 
{ 
    Boolean returnValue = OneOfMyCoolCRoutines(x, y, r); 
} 

Tenga en cuenta que esencialmente estoy pasando punteros hacia atrás y cuarto entre mi dll y el código C#. Eso puede causar errores de memoria porque el colector de basura CLR puede cambiar las ubicaciones de esas matrices, pero el dll de C/C++ no sabrá nada al respecto. Así que para protegerme de eso, simplemente arreglé esos punteros en mi C#, y ahora no se moverán en la memoria mientras mi dll esté operando en esas matrices. Este es ahora un código inseguro y tendré que compilar mi código C# con esa bandera.

Hay muchos detalles finos en la interoperabilidad del lenguaje, pero eso debería hacerlo funcionar. Mantener una interfaz C sin estado de tipos blittables es una gran política si es posible. Eso mantendrá su código de interoperabilidad del idioma más limpio.

Buena suerte,

Paul