2011-01-14 23 views
7

Ho una console C++ Exe che esegue alcuni programmi. Ora volevo scrivere una GUI C# che faccia parte della programmazione eseguita dal C++ exe. Stavo pensando di alcuni approcci,Scrittura C# GUI su una DLL C++ o exe C++

  1. Scrivi la C# GUI con tutta la programmazione in C++ fatto da zero. (Io non voglio fare questo per la quantità di rilavorazione che essa comporta)
  2. Costruire una DLL C++ che fa la programmazione e lo ha importato nell'app della GUI (ora ho un problema: come posso catturare l'output delle routine in C++ dll e visualizzarlo nella GUI? dovrei restituire l'output come stringa per ogni routine che l'app chiamate.? Dal momento che non so gestito C++ iam andando a costruire una dll C++ non gestita.)
+0

Avete considerato l'aggiunta di un'interfaccia COM? Poiché, IMHO, passare argomenti e dati di programma con contenitori COM può essere una soluzione. –

risposta

7

Costruire una DLL C++/CLI non è poi così difficile. Fondamentalmente usi un codice C++ non gestito, tranne per il fatto che definisci un "public ref class" che ospita le funzioni che vuoi che il codice C# veda.

Che tipo di dati stai restituendo? Numeri singoli, matrici di numeri, oggetti complessi?

UPDATE: Dal momento che è stato chiarito che la "uscita" è iostreams, here is a project dimostrando reindirizzamento di cout ad un'applicazione .NET chiamando la biblioteca. Il reindirizzamento di clog o cerr richiederebbe solo una manciata di righe aggiuntive in DllMain con motivi dopo il reindirizzamento esistente.

The zip file include file di progetto VS2010, ma il codice sorgente dovrebbe funzionare anche nel 2005 o nel 2008.

La funzionalità di acquisizione iostreams è contenuta nel codice seguente:

// 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

Desidero visualizzare i messaggi di registro che provengono dalla DLL C++ nella GUI C#. – excray

+0

@ user97642: Quindi la DLL C++ sta scrivendo in 'std :: clog' e si desidera inviare questo output a C#? –

+0

Sì zoccolo o cout o cerr. – excray

3

Quindi vuoi chiamare la libreria C++ dal codice .net gestito?

Quindi è necessario creare un oggetto COM o una libreria p-invokable in C++. Ogni approccio ha i suoi pro e contro a seconda delle esigenze aziendali. Dovresti marshallare i dati nel tuo consumatore. Ci sono tonnellate e tonnellate di materiale su entrambi i concetti.

0

Si potrebbe semplicemente scrivere un wrapper C# GUI (come suggerito nell'opzione 2) e generare il processo C++; tuttavia, questo sarà un po 'lento (non so se questo è importante).

Per eseguire il tuo exe C++ e acquisire l'output, è possibile utilizzare il gruppo ProcessRunner. Ecco l'uso di base:

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

Iam cerca un modo per capire la seconda opzione. Una GUI C# che utilizza una DLL C++. – excray

0

Probabilmente il modo migliore per andare qui è l'uso P/Invoke o Platform Invoke. A seconda della struttura o dell'interfaccia di C++ dll, potresti volerli racchiudere in una pura interfaccia C; è più semplice se l'interfaccia utilizza solo i tipi blittable. Se si limita l'interfaccia dll a tipi blittabili (Int32, Single, Boolean, Int32 [], Single [], Double [] - Nozioni di base) non è necessario eseguire alcun marshalling complesso di dati tra gestito (C#) e spazi di memoria non gestiti (C).

Ad esempio, nel codice C# si definiscono le chiamate disponibili nella dll C/C++ utilizzando l'attributo DllImport.

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

Il piccolo [In] 's e [Out]' s non sono strettamente necessari, ma possono accelerare le cose insieme. Ora avendo aggiunto il tuo "ExactDllName.dll" come riferimento al tuo progetto C#, puoi chiamare la tua funzione C/C++ dal tuo codice C#.

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

Nota che sto passando essenzialmente puntatori avanti e indietro tra il mio DLL e codice C#. Ciò può causare errori di memoria perché le posizioni di tali array possono essere modificate dal garbage collector CLR, ma la dll C/C++ non ne sa nulla. Quindi, per evitare questo, ho semplicemente corretto quei puntatori nel mio C#, e ora non si sposteranno in memoria mentre la mia dll sta operando su quegli array. Questo è ora un codice non sicuro e dovrò compilare il mio codice C# con quel flag.

Ci sono molti dettagli sull'interop della lingua, ma questo dovrebbe farti rotolare. Attaccare a un'interfaccia C stateless di tipi blittable è una grande politica, se possibile. Ciò manterrà il codice di interoperabilità della lingua più pulito.

Buona fortuna,

Paul