Autor Wątek: "Most" C++ to C# w C++/CLI.  (Przeczytany 2418 razy)

Offline Frondeus

  • Użytkownik

# Sierpień 08, 2010, 15:49:37
Mam drobną prośbę. Czy ktoś bardziej doświadczony przeze mnie mógłby tu napisać najprostszy sposób łączenia kodu c++ z c# przez c++/cli?
Kod: (cpp) [Zaznacz]
#include <iostream>
#include <string>
namespace Unmanaged
{
class A
{
public :
void Write(std::string str)
{
std::cout << str << std::endl;
}
};
class B
{
public :
A* GetA()
{
return new A();
}
};
}
DLLEXPORT Unmanaged::B* GetB()
{
return new B();
}
DLLEXPORT Unmanaged::A* GetA()
{
return new A();
}

I jak tu się dobrać do metody GetA skoro zmienna zwracana (dokładniej wskaznik) nie jest typu podstawowego?
Probowalem z wrapperem na C++/CLI:
(Kod pisany z pamieci wiec moze byc nieco niedokladny)
Kod: (cpp) [Zaznacz]
[include etc.]
[ladowanie dllki]
namespace Managed
{
public ref A
{
public:
UnManaged* pA;
A()
{
pA = GetA();
}
void Write(System::String Str)
{
std::string str = StringToStd(Str); // Funkcja moja... a dokładniej wygooglowana
pA->Write(str);
}
};
public ref B
{
public:
UnManaged pB;
B()
{
pB = GetB();
}
A^ GetA()
{
return gcnew A();
}
};
};
A w kodzie c# po dodaniu do projektu dllki utworzonej z powyszszego kodu:

Kod: (c#) [Zaznacz]
[...]
Managed.B B = new Managed.B();
Managed.A A = B.GetA();
A.Write("Coś");
[...]
Kompilator(w msvc#) pokazuje jednak błąd na linijce:
Kod: (cpp) [Zaznacz]
pA->Write(str);
czyli tej z dllki c++/cli.
Dlatego iż niezbyt rozumiem co tu się dzieje (co jest powodem błędu) a wyszukiwarka internetowa oraz MSD dostarczaja jedynie informacji jak załadować kod np. MessageBox w C# bez C++/CLI proszę Was o pomoc.

Z góry dziękuję za poświęcony czas temu problemowi.
« Ostatnia zmiana: Sierpień 08, 2010, 18:30:17 wysłana przez Frondeus »

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Reg

  • Administrator
    • Adam Sawicki - Home Page

# Sierpień 08, 2010, 16:59:38
Popełniłeś ciężki grzech początkującego programisty - powiedziałeś, że wyskakuje jakiś błąd nie cytując jego treści. Naprawdę nie zdajesz sobie sprawy z tego, że to o jaki błąd chodzi jest tu właśnie w tym najważniejsze?

Offline Frondeus

  • Użytkownik

# Sierpień 08, 2010, 17:10:11
Za błąd miałem na myśli:
Cytuj
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Zdaje sobie sprawe jaki lecz tutaj pokazuje mi że program próbuje się dostać do pamięci do której nie ma dostępu...
Co dla mnie jest nieco niezrozumiałe czemu tak postępuje...

Offline Dab

  • Redaktor
    • blog

# Sierpień 08, 2010, 17:18:01
To jest cały kod? Proste, pA nie jest nigdzie inicjalizowane.

Offline Frondeus

  • Użytkownik

# Sierpień 08, 2010, 17:25:15
Mowiłem że pisze z pamieci ;)
Spojrzałem na orginał i pA jest inicjalizowane. Wyedytowałem pierwszy post.
« Ostatnia zmiana: Sierpień 08, 2010, 18:30:44 wysłana przez Frondeus »

Offline ConayR

  • Użytkownik

# Sierpień 08, 2010, 21:48:41
Mowiłem że pisze z pamieci ;)
To wróć jak zaczniesz szanować czas innych i opiszesz problem dokładnie i bez miejsca na zgadywanie "co jeszcze się dzieje w Twoim kodzie". Co to jest UnManaged? Widzę tylko Unmanaged. Co to jest "include etc." i "ladowanie dllki"? Co to znaczy "kod pisany z pamięci"? Jaki problem tak naprawdę próbujesz rozwiazać? Zamiast pytać jak zaimplementować wymyślone przez siebie rozwiązanie problemu, spytaj o rozwiązanie problemu. Trzy warstwy (niezarządzane C++, zarządzane C++ i zarządzane C#) śmierdzą paskudnym designem i próbą podrapania się po lewym pułdupku prawą ręką pod lewą pachą.

Offline Zielony

  • Użytkownik
    • Ghurund Engine

# Sierpień 08, 2010, 22:28:33
Jeśli masz dostęp do kodu w C++ i nie robisz tam nic specjalnego (bez asma, wyrównywania i innych), to po prostu skompiluj kod C++ jako zarządzaną .dll.

Obłsugę pamięci niezarządzanej możesz sobie zorganizować z pomocą System.Runtime.InteropServices.Marshal. Tu masz przykład.
« Ostatnia zmiana: Sierpień 08, 2010, 22:39:17 wysłana przez Zielony »

Offline Frondeus

  • Użytkownik

# Sierpień 08, 2010, 23:31:46
Trzy warstwy (niezarządzane C++, zarządzane C++ i zarządzane C#) śmierdzą paskudnym designem i próbą podrapania się po lewym pułdupku prawą ręką pod lewą pachą.
Niby dlaczego? Zakładając że klasy MUSZ¡ być w niezarządzanym C++, a chce je użyć w zarządzanym C# to C++/CLI jest najlepszym rozwiązaniem.
Cytuj
Co to jest UnManaged?
Literówka. Nigdy Ci się nie zdarzyła?
Cytuj
Co to jest "include etc." i "ladowanie dllki"?
linijki ktore pominąłem aby oszczędzić czas i miejsce.
Cytuj
Co to znaczy "kod pisany z pamięci"?
To znaczy że nie miałem dostępu do kodu i napisałem go z pamięci. Przepraszam jeśli to Cię tak uraziło.
Jeśli masz dostęp do kodu w C++ i nie robisz tam nic specjalnego (bez asma, wyrównywania i innych), to po prostu skompiluj kod C++ jako zarządzaną .dll.

Obłsugę pamięci niezarządzanej możesz sobie zorganizować z pomocą System.Runtime.InteropServices.Marshal. Tu masz przykład.
Przyznam się bez bicia : przykład który pokazałem był jedynie początkiem. Miałem zamiar sprawdzić Jak To Działa w prostym kodzie i zastosować to do Rendera. Czyli chciałem później użyć tego do załadowania kodu(klas) obsługującego directXa bez używania Managed DirectX czy też XNA by nie pisać kodu 2x.(Który z resztą mocno różniłby się).

Czy takie działanie zalicza się do "czegoś specjalnego" tego nie wiem. Także nie wiem jak miałbym skomplikować ten kod do zarządzanej dllki. dlatego wybrałem c++/cli jako most łączący te dwa języki (c++ i c#).

Dzięki radzie dostarczonym od Dab'a (który nie trafił lecz natchnął mnie do ponownego sprawdzenia czy ten kod jest OK) udało mi się zbudować ten most poprawnie, wszystko działa i jestem szczęśliwy.Dziękuję wszystkim za uwagę i przepraszam za kłopot.


Offline ConayR

  • Użytkownik

# Sierpień 09, 2010, 00:35:07
Niby dlaczego? Zakładając że klasy MUSZ¡ być w niezarządzanym C++, a chce je użyć w zarządzanym C# to C++/CLI jest najlepszym rozwiązaniem.
Zakładając, że ten niezarządzany kod piszę ja, eksponowałbym interfejsy COM i konsumował je bezpośrednio w kodzie C#. Własnie po to, żeby się nie pałować z dodatkową warstwą kodu.

Cytuj
Literówka. Nigdy Ci się nie zdarzyła?
Nie, jeśli pytam ludzi o rozwiązanie mojego problemu. A jeśli mi się zdarza, to przepraszam i poprawiam zamiast pyskować. Ale to ja. :)

Cytuj
linijki ktore pominąłem aby oszczędzić czas i miejsce.
A może są istotne? Tak jak to new, którego zapomniałeś?

Cytuj
To znaczy że nie miałem dostępu do kodu i napisałem go z pamięci. Przepraszam jeśli to Cię tak uraziło.
Mnie jest ciężko urazić, naprawdę. Mój komentarz ma na celu skłonienie Ciebie do zadawania konkretnych, dobrze opisanych pytań. Tak, by ludzie mogli na nie konkretnie odpowiedzieć. Dzięki temu Ty zmarnujesz mniej czasu na ping pong a innym być może będzie się chciało rozwiązać Twój problem. Win-win.

Cytuj
Przyznam się bez bicia : przykład który pokazałem był jedynie początkiem. Miałem zamiar sprawdzić Jak To Działa w prostym kodzie i zastosować to do Rendera. Czyli chciałem później użyć tego do załadowania kodu(klas) obsługującego directXa bez używania Managed DirectX czy też XNA by nie pisać kodu 2x.(Który z resztą mocno różniłby się).
A wiesz, że to jest właśnie przedstawienie problemu? A nie pytanie o rozwiązanie bez opisania problemu.

Offline Zielony

  • Użytkownik
    • Ghurund Engine

# Sierpień 09, 2010, 01:10:05
Niby dlaczego? Zakładając że klasy MUSZ¡ być w niezarządzanym C++, a chce je użyć w zarządzanym C# to C++/CLI jest najlepszym rozwiązaniem.
To nie jest najlepsze rozwiązanie, bo to, co chcesz zrobić w C++/CLI da się zrobić bezpośrednio w C#, więc niepotrzebnie dokładasz jedną kompletną warstwę. Biblioteki niezarządzane da się ładować i obsługiwać bezpośrednio z poziomu C#. Od razu w C# piszesz też wrappery. Link podałem.

Przyznam się bez bicia : przykład który pokazałem był jedynie początkiem. Miałem zamiar sprawdzić Jak To Działa w prostym kodzie i zastosować to do Rendera. Czyli chciałem później użyć tego do załadowania kodu(klas) obsługującego directXa bez używania Managed DirectX czy też XNA by nie pisać kodu 2x.(Który z resztą mocno różniłby się).

Czy takie działanie zalicza się do "czegoś specjalnego" tego nie wiem. Także nie wiem jak miałbym skomplikować ten kod do zarządzanej dllki. dlatego wybrałem c++/cli jako most łączący te dwa języki (c++ i c#).
Coś specjalnego obejmuje wszystkie rzeczy, które uniemożliwiają skompilowanie kodu pod CLR. Kompilacja do biblioteki zarządzanej, to po prostu przestawienie opcji projektu. Ustawiasz Configuration Type (.dll) i Common Language Runtime support (/clr), albo stwórz nowy projekt z czarodzieja i tam wklej swój kod. C++/CLI bez problemu kompiluje klasy niezarządzane.

Z drugiej strony możesz napisać niezarządzaną bibliotekę (niekoniecznie na COM) i załadować ją w C#. Tak, czy siak pomijasz jedną warstwę, więc masz mniej do napisania.
« Ostatnia zmiana: Sierpień 09, 2010, 13:19:12 wysłana przez Zielony »

Offline Frondeus

  • Użytkownik

# Sierpień 09, 2010, 01:52:33
Hmm spróbuje bo rzeczywiście pisanie wrappera pod to wszystko jest nieco męczące choć jak widzę efekt pod postacią zgrabnego kodu w c# to wnet się zapomina o zmeczeniu ;)

Edit:
Moje spostrzeżenia:
Kod niezarządzany kompiluje się w C++/CLI lecz taki nie jest "widziany" przez c# (ładuje ją poprzez References).
Ładowanie bezpośrednio z C# - a co z faktem że np. metoda zwraca wynik na objekt?
Musiałbym stworzyć jakąś klase tego obiektu czy wszystko zostanie "wyłuskane" z dllki?

Pisanie zarządzanego wrappera na niezarządzany kod w C++/CLI - zdołałem przenieść większość kodu (który działa poprawnie - np. Obsługa XInput, Inicjacja Directx etc)  lecz zauważyłem problem z klasami abstrakcyjnymi - gdy np. Tworze obiekt klasy Triangle która dziedziczy po klasie Object, dodaje ten obiekt do wektora obiektow klasy bazowej (Zawsze stosuje tu Poliforizm)  to skutkiem jest Błąd dostępu do pamięci (AV) . A poza tym zakładając że mocno rozbuduje kod to będę mieć dużo pracy z pisaniem wrappera itp( Co z resztą sami zauważyliście).

Dlatego mam jedno pytanie, co powinienem wybrać:
-Spróbować naprawić problem i nie przejmować się dodatkową warstwą (zakładam że  to zły pomysł po Waszych sugestiach)
-Zainteresować się System.Runtime.InteropServices.Marshal (Nie wiem czy np. uda się zaimportować klase Render ktora mieszy innymi ma wskaznik na urządzenie directX, wektory na obiekty klasy Sceny etc)
-COM (j.w)
-Jakaś alternatywa którą pominąłem.
« Ostatnia zmiana: Sierpień 09, 2010, 22:27:17 wysłana przez Frondeus »