Kurak – strona domowa

Wykorzystanie kodu C++ w C#

Opublikował/a Kurak w dniu styczeń 10, 2008

Przymierzam się do stworzenia edytora poziomów do Shadow Clones. Zamierzam użyć C# i Windows.Forms – wybór taki głównie z powodu szybkości i wygody pisania, ale także z chęci zdobycia większego doświadczenia w pisaniu na tę platformę. Na razie jednak czynię pewne przygotowania (niewiele ich jest – bo i niewiele ostatnio chce mi się robić) – między innymi uczę się ;) Ale po kolei: zamówienie na edytor wymaga ode mnie użycia części kodu gry w edytorze, by pewne rzeczy były zrealizowane identycznie (z tego, co udało mi się wymyślić, przeczytać i usłyszeć, chodzi głównie o wyszukiwanie ścieżek). Ponieważ przepisywanie kodu z C++ do C# mi się nie uśmiecha (szczególnie, jeśli byłoby tego dużo), postanowiłem poszukać sposobu na wykorzystanie kodu w C++ pod C#.

I znalazłem – zaraz go przedstawię i napiszę, co o nim myślę :)

Zakładam, że kod C++, który chcemy uruchomić spod C# jest w bibliotece DLL (nie jest to konieczne). W każdym razie, do zrobienia są 3 projekty:

  • C++, DLL – tu będzie ten ważny kod C++
  • C++/CLI, Class Library – wrapper powyższego
  • C#, Console Application – program testowy

Projekt 1 – normalny DLL, żeby można było z tego normalnie korzystać. Projekt 3 to zwykły test, w którym jedyna “ciekawa” operacja to dodanie referencji do DLLki wyprodukowanej przez najważniejsze projekt 2. To w nim jest najbrzydszy kod. Czemu? Wystarczy spojrzeć na “C++/CLI” i wszystko jasne ;)

Cała sztuka polega na tym, żeby dołączyć DLL pierwszego projektu do projektu 2 i napisać klasy .NET opakowujące i dające dostęp C#-owemu kodowi do klas C++ z projektu 1. Tutaj trzeba też dokonać ewentualnych konwersji (np. std::string <-> System::String).

Pokrótce napisałem, o co chodzi – teraz pora na komentarz. Ogólnie rzecz biorąc, to cieszę się że to wszystko jest w ogóle możliwe w dosyć prosty sposób. Natomiast samo wykonanie… właściwie aż tak strasznie złe nie jest. Co prawda ciężko się pisze w Visual C++ ze szwankującym IntelliSensem kod z typowo sisharpowymi pozagnieżdżanymi namespace’ami, konieczność używania konstrukcji typu daszki (“^”) czy “gcnew” też nie jest przyjemna, jednak wszystko to jest poukładane w miarę logicznie. Podsumowując – jest fajnie :)

Dopisane:

Okazało się, że niezbyt klarownie wytłumaczyłem, o co mi chodzi :) Dodam więc trochę kodu: to powinno być już całkiem zrozumiałe :)

Część 1: kod C++

// Plik Foo.hpp
#include <string>

class DLLDEF CFoo
{
public:
  CFoo();
  ~CFoo();

  std::string getString() const;

  unsigned int getNumber() const;
  void setNumber(unsigned int _value);

private:
  std::string mString;
  unsigned int mNumber;
};

// Plik Foo.cpp
#include "Foo.hpp"

CFoo::CFoo()
{ }
CFoo::~CFoo()
{ }

std::string CFoo::getString()
{
  return mString;
}

unsigned int CFoo::getNumber() const
{
  return mNumber;
}

void CFoo::setNumber(unsigned int _value)
{
  mNumber = _value;
}

Część 2: Kod C++/CLI

// Plik FooWrapper.h
#include "Foo.hpp"
#using <mscorlib.dll>

namespace Bar
{
public ref class Foo
{
public:
  Foo();
  ~Foo();

  System::String^ GetString();

  unsigned int GetNumber();
  void SetNumber(unsigned int value);

private:
  Foo* NativePtr;
};
}

// Plik FooWrapper.cpp
#include "FooWrapper.h"
namespace Bar
{
Foo::Foo()
{
  NativePtr = new CFoo();
}
Foo::~Foo()
{
  delete CFoo();
}

System::String^ Foo::GetString()
{
  std::string nts = NativePtr->getString();
  System::String^ out = gcnew System::String(nts.c_str());
  return out;
}

unsigned int Foo::GetNumber()
{
  return NativePtr->getNumber();
}

void Foo::SetNumber(unsigned int value)
{
  NativePtr->setNumber(value);
}
}

Do tego trzeba jakoś dolinkować implementację CFoo – można dołączyć LIB (jeśli jest w DLLce), albo po prostu dołączyć plik Foo.cpp do projektu.

Część 3 – Kod C#

// Plik FooTest.cs
using System;

namespace FooTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Bar.Foo foo = new Bar.Foo();
            string str = foo.GetString();
            uint num = foo.GetNumber();
            foo.SetNumber(666);
        }
    }
}

Trzeba tylko dołączyć referencję do wrappera do projektu. I śmiga :)

Wrapper robi tutaj konwersję std::string -> System::String, równie dobrze może konwertować obiekty pojemników STL na ich .NETowe odpowiedniki, jak również wiele innych rzeczy ;)

Odpowiedzi: 9 do “Wykorzystanie kodu C++ w C#”

  1. Xion powiedział/a

    Ekhm… A kto ci każe pisać ten wrapper w C++? Przecież bez problemu możesz napisać go w C#, importując funkcje z DLLa i korzystając z marshalingu, który potrafi np. sam zmienić typowe stringopodobne typy (LPCTSTR itd.) na System.String. Tak się importuje chociażby funkcje WinAPI do .NET – polecam pinvoke.net.

  2. Kurak powiedział/a

    1. Pisząc wrapper w C++ nie muszę wyrzucać kodu do DLL-ki :)
    2. O DllImport czytałem i nawet używałem (właśnie do wspomnianego WinAPI), ale nie dostanę za friko automagicznej konwersji pojemników STLa (a tym bardziej własnych) do .NETowych odpowiedników, i gdzieś tę konwersję trzeba zrobić :)

    [zedytowano]

  3. Reg powiedział/a

    Fajnie. Ale mógłbyś, skoro piszesz o takim temacie, podzielilć się przy tym linkami do artykułów, z których się tego uczyłeś.

  4. Kurak powiedział/a

    Dobra uwaga ;) Zamierzam w najbliższym czasie rozszerzyć tę notkę o trochę kodu i wspomniane linki, bo okazało się, że nie przekazuje tematu zbyt dobrze :)

    A techniki to nauczyłem się raczej przeglądając kod innych wrapperów, ale znalazłem też dość dobry artykuł na ten temat: http://msdn.microsoft.com/msdnmag/issues/06/06/NettingC/

  5. wtoman powiedział/a

    Ja zwróciłem uwagę na coś innego (bo ogólnie sam postępuję podobnie) – Kurak i Ty w Shadow Clones? Zaraz pół warsztatu tam będzie. Przez to nie da się znaleźć programistów do Bitter Glory ;)

  6. Kurak powiedział/a

    Szukałem jakiegoś teamu, gdzie mógłbym pokodzić nie zajmując się całą resztą organizacji, po prostu. Swoją drogą mam mnóstwo czasu, może szukacie jeszcze programistów… ? ;)

  7. Riddlemaster powiedział/a

    Owszem szukamy cały czas.

  8. Rev powiedział/a

    A ja używam właśnie tej ciekawej możliwości łączenia C, C++ i C++/CLI w pluginie do Winampa. Cała konstrukcja plugina to C/C++, a odwalanie brudnej roboty (pobieranie strony www i parsowanie jej) zostawiam C++/CLI. O dziwo to działa :) .

  9. dotnetomaniak.pl powiedział/a

    Wykorzystanie kodu C++ w C# « Kurak – strona domowa…

    Dziękujemy za publikację – Trackback z dotnetomaniak.pl…

Dodaj komentarz

XHTML: Możesz skorzystać z tych etykiet: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>