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
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.
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]
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ś.
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/
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
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… ?
Riddlemaster powiedział/a
Owszem szukamy cały czas.
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
.
dotnetomaniak.pl powiedział/a
Wykorzystanie kodu C++ w C# « Kurak – strona domowa…
Dziękujemy za publikację – Trackback z dotnetomaniak.pl…