J'ai une bibliothèque de code écrite dans un ancien C ++ (pas de code non géré) et je porte l'application qui utilise ce code sur C #. Je suis confronté à deux options: p>
Je suis relativement nouveau à C # et je suis assez inconnu avec les implications de l'utilisation d'une bibliothèque de code non géographique dans une application C # (ou s'il y en a même). Le code lui-même est de taille modérée; Il ne fera probablement que quelques jours à réécrire en C #, mais ma pensée est que laisser le code comme un informatique me permettrait de l'utiliser dans d'autres applications (et de la compiler sur UNIX, etc.). P >
Quel genre de choses devrais-je être au courant de la prise de cette décision? Y a-t-il des inconvénients majeurs ou des gotchas à utiliser la DLL dans l'application C #? P>
3 Réponses :
Je ferais une bibliothèque d'emballage à l'aide de C ++ / CLI pour exposer la bibliothèque à C #. Cela peut laisser votre bibliothèque inchangée et simplement l'envelopper à partir de .NET, en offrant le meilleur des deux options. P>
Battez-moi de 20 secondes. Mais i i> est allé et j'ai trouvé un lien. :)
L'utilisation d'une enveloppe C ++ / CLI est la voie i> à aller. P / Invoke vous donnera des problèmes de chargement et de versions DLL. L'utilisation d'une bibliothèque non géographique rendra la ligne claire pour la compilation non gérée non gérée. Méfiez-vous de l'exposition de classes STL, cependant. Vous constaterez peut-être que votre code de bas niveau qui s'attend à utiliser STL non exploité finira par utiliser des versions gérées avec de nombreuses transitions gérées / non gérées.
Une chose que j'ai trouvée utile est de plonger dans C ++ / CLI < / a> lorsque vous traitez avec des bibliothèques C ++ non gérés. Créez une enveloppe gérée à l'aide de C ++ / CLI et appelez-le de votre code C #. L'enveloppe gérée peut inclure la bibliothèque (je suppose qu'il est lié statilement) dans sa DLL et une référence de projet est tout ce dont vous avez besoin pour votre code C #. p>
FYI: Les DLL ne sont pas liées statiquement. Bibliothèque de liaison dynamique.
@ YODAJ007: Je pense qu'il suggère de lier statiquement la bibliothèque d'origine dans l'assembly C ++ / CLI, ce qui est correct.
@ Yodaj007: @reed Copsey l'a dit mieux que je pouvais. Mais j'essaierai toujours: la bibliothèque d'origine, basée sur des choses mentionnées dans la question initiale, semble être une bibliothèque liée statiquement liée. Ce qui signifie que si la bibliothèque est utilisée par le projet C ++ / CLI, elle sera compilée (et sous-champée par) le C ++ / cli .dll.
Il n'est pas nécessaire d'écrire une enveloppe en C ++ / CLI. Vous pouvez utiliser directement une plate-forme invoke à partir de C #:
http://msdn.microsoft.com/en-us/library/aa288468%28vs.71%29.aspx p>
Edit: Si vous le faites à l'aide de C ++ / CLI, vous Besoin d'apporter des appels de chargement et de créer des pointeurs de la fonction. Ceci est nettement plus facile dans C #. Cela vient du didacticiel MSDN lié ci-dessus, mais avec mes propres commentaires ajoutés: p> EDIT: Les types complexes peuvent également être mis au rebut, bien qu'il soit nécessaire de définir les structures. Cet exemple tiré de mon propre code qui invoque GDI +. Je l'ai coupé un peu. P> 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;
}
Cela semble assez simple. Qu'en est-il du maréchalage de choses telles que des conteneurs stl et des types personnalisés complexes?
Vous n'avez pas besoin de traiter avec des pointeurs de chargement ou de fonction - C ++ / CLI facilite l'enveloppement d'une bibliothèque complexe C ++ simple. P / Invoke fonctionne bien, à condition que la bibliothèque fournisse une API C, mais il n'y a pas de moyen (facile) d'envelopper une instance de classe C ++ à l'aide de P / invoke. C'est génial pour C, mais pas si génial pour les cours C ++.
Il peut être très difficile de comprendre les signatures P / invoquer si vous passez dans des structures plus complexes, cependant. Emballage dans une DLL gérée C ++ / CLI vous permettra de simplifier les structures étant transmises, si vous ne pouvez pas comprendre un moyen ponctuel de marraler les structures.
Mono vous permettrait également de "porter" / courir sur * Nix.