2009-07-22 21 views
6

Ho una libreria di codice scritta in semplice C++ (senza codice .NET/gestito) e sto eseguendo il porting dell'applicazione che utilizza questo codice in C#. Sono di fronte a due opzioni:Porting (non gestito) da C++ a C# e utilizzo del C++ come DLL in un'applicazione C#

  1. Riscrivere il codice C++ in C# per ottenere la stessa funzionalità;
  2. Compilare il C++ come DLL e utilizzarlo come libreria nell'applicazione C#.

Sono relativamente nuovo per C# e sono abbastanza familiarità con le implicazioni dell'utilizzo di una libreria di codice non gestito in un app C# (o se ci anche sono). Il codice stesso è di dimensioni moderate; probabilmente ci vorranno solo pochi giorni per riscrivere in C#, ma il mio pensiero è che lasciare il codice come tale mi consentirebbe di usarlo anche in altre applicazioni (e compilarlo su UNIX, ecc.).

Che tipo di cose dovrei essere a conoscenza quando si prende questa decisione? Ci sono grossi inconvenienti o trucchi per usare la DLL nell'applicazione C#?

+1

Mono consentirebbe anche di "port"/eseguire su * nix. – Tim

risposta

6

Vorrei creare una libreria wrapper utilizzando C++/CLI per esporre la libreria a C#. Questo può lasciare invariata la tua libreria e avvolgerla per l'uso da .NET, fornendo il meglio di entrambe le opzioni.

+1

Sconfiggilo di 20 secondi. Ma * I * andò e trovò un collegamento. :) – Randolpho

+1

L'uso di un wrapper C++/CLI è * la * strada da fare. P/Invoke ti darà problemi con il caricamento e la versione di dll. L'utilizzo di una libreria non gestita renderà la linea chiara per la compilazione gestita non gestita. Attenzione però a esporre le classi STL. Potresti scoprire che il tuo codice di basso livello che prevede l'utilizzo di STL non gestito finirà per utilizzare le versioni gestite con molte transizioni gestite/non gestite. – plinth

2

Una cosa che ho trovato utile è di approfondire C++/CLI quando si tratta di librerie C++ non gestite. Crea un wrapper gestito usando C++/CLI e chiamalo dal tuo codice C#. Il wrapper gestito può includere la libreria (presumo che sia collegata staticamente) nella sua DLL e un riferimento al progetto è tutto ciò che serve per il codice C#.

+0

FYI: le DLL non sono collegate in modo statico. Libreria di collegamento dinamico. – Amy

+1

@ yodaj007: credo che suggerisse di collegare staticamente la libreria originale nell'assembly C++/CLI, che è corretto. –

+0

@ yodaj007: @Reed Copsey lo ha detto meglio di quanto potrei. Ma ci proverò ancora: la libreria originale, basata sulle cose menzionate nella domanda originale, sembra essere una libreria collegata staticamente. Ciò significa che se la libreria viene utilizzata dal progetto C++/CLI, verrà compilata (e inclusa) da C++/CLI .DLL. – Randolpho

0

Non è necessario scrivere un wrapper in C++/CLI. È possibile utilizzare direttamente Platform Invoke da C#:

http://msdn.microsoft.com/en-us/library/aa288468%28VS.71%29.aspx

EDIT: Se lo fate con C++/CLI, avrete bisogno di fare chiamate LoadLibrary e la creazione di puntatori a funzione. Questo è significativamente più semplice in C#. Questo è dal tutorial MSDN linkato sopra, ma con i miei commenti aggiunti:

class PlatformInvokeTest 
{ 
    [DllImport("msvcrt.dll")] // Specify the DLL we're importing from 
    public static extern int puts(string c); // This matches the signature of the DLL function. The CLR automatically marshals C++ types to C# types. 
    [DllImport("msvcrt.dll")] 
    internal static extern int _flushall(); 

    public static void Main() 
    { 
     puts("Test"); 
     _flushall(); 
    } 
} 

EDIT: I tipi complessi può anche essere marshalling, anche se è necessario definire le strutture. Questo esempio tratto dal mio codice che richiama GDI +. L'ho ritagliata un po '.

private static int SRCCOPY = 0x00CC0020; 
private static uint BI_RGB = 0; 
private static uint DIB_RGB_COLORS = 0; 


[DllImport("gdi32.dll")] 
private static extern bool DeleteObject(IntPtr hObject); 

[StructLayout(LayoutKind.Sequential)] 
private struct BITMAPINFO 
{ 
    public uint biSize; 
    public int biWidth; 
    public int biHeight; 
    public short biPlanes; 
    public short biBitCount; 
    public uint biCompression; 
    public uint biSizeImage; 
    public int biXPelsPerMeter; 
    public int biYPelsPerMeter; 
    public uint biClrUsed; 
    public uint biClrImportant; 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] 
    public uint[] cols; 
} 

public static Bitmap Downsample(Bitmap input, int bpp) 
{ 
    Bitmap retval = null; 

    // We will call into this GDI functionality from C#. Our plan: 
    // (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed) 
    // (2) Create a GDI monochrome hbitmap 
    // (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above) 
    // (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed) 

    IntPtr inputHandle = input.GetHbitmap(); 

    // 
    // Step (2): create the monochrome bitmap. 
    // 
    BITMAPINFO bmi = new BITMAPINFO(); 
    bmi.biSize = 40; // the size of the BITMAPHEADERINFO struct 
    bmi.biWidth = input.Width; 
    bmi.biHeight = input.Height; 
    bmi.biPlanes = 1; 
    bmi.biBitCount = (short)bpp; // 1bpp or 8bpp 
    bmi.biCompression = BI_RGB; 
    bmi.biSizeImage = (uint)(((input.Width + 7) & 0xFFFFFFF8) * input.Height/8); 
    bmi.biXPelsPerMeter = 0; // not really important 
    bmi.biYPelsPerMeter = 0; // not really important 

    // 
    // Create the color palette. 
    // 
    uint numColors = (uint)1 << bpp; // 2 colors for 1bpp; 256 colors for 8bpp 
    bmi.biClrUsed = numColors; 
    bmi.biClrImportant = numColors; 
    bmi.cols = new uint[256]; 

    if (bpp == 1) 
    { 
     bmi.cols[0] = MAKERGB(0, 0, 0); 
     bmi.cols[1] = MAKERGB(255, 255, 255); 
    } 
    else 
    { 
     for (int i = 0; i < numColors; i++) 
     { 
      bmi.cols[i] = MAKERGB(i, i, i); 
     } 
    } 

    // 
    // Now create the indexed bitmap 
    // 
    IntPtr bits0; 
    IntPtr indexedBitmapHandle = CreateDIBSection(IntPtr.Zero, ref bmi, DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0); 
    IntPtr sourceDC = GetDC(IntPtr.Zero); 
    IntPtr hdc = CreateCompatibleDC(sourceDC); 
    IntPtr hdc0 = CreateCompatibleDC(sourceDC); 

    SelectObject(hdc, inputHandle); 
    SelectObject(hdc0, indexedBitmapHandle); 

    BitBlt(hdc0, 0, 0, input.Width, input.Height, hdc, 0, 0, SRCCOPY); 

    retval = Bitmap.FromHbitmap(indexedBitmapHandle); 

    // 
    // Dispose of the crud 
    // 
    DeleteDC(hdc); 
    DeleteDC(hdc0); 
    ReleaseDC(IntPtr.Zero, sourceDC); 
    DeleteObject(inputHandle); 
    DeleteObject(indexedBitmapHandle); 

    return retval; 
} 
+0

Questo sembra abbastanza semplice. Che dire del marshalling di oggetti come container STL e tipi personalizzati complessi? –

+3

Non è necessario occuparsi di LoadLibrary o dei puntatori di funzione - C++/CLI rende avvolgente una libreria C++ complessa SEMPLICE.P/Invoke funziona alla grande, a condizione che la libreria fornisca una C api, ma non esiste un modo (facile) per avvolgere un'istanza di classe C++ usando P/Invoke. È fantastico per C, ma non così eccezionale per le classi C++. –

+0

Tuttavia, può essere davvero difficile capire le firme P/Invoke se si passano strutture più complesse. Il wrapping in una DLL C++/CLI gestita consente di semplificare le strutture passate, se non si riesce a trovare un modo efficace per eseguire il marshall delle strutture. –

Problemi correlati