Języki programowania na platformie.net cz.2 2015/16 Instrukcja laboratoryjna cz.3 Język C++/CLI Prowadzący: Tomasz Goluch Wersja: 2.0
I. Utworzenie projektu C++/CLI z interfejsem graficznym WPF 1 Cel: Zapoznanie z możliwością obsługi WPF w projekcie C++/CLI. Domyślne szablony C++/CLI dostępne w Visual Studio nie wspomagają interfejsu graficznego WPF a od wersji 2012 również Windows Forms. Pomimo, że Microsoft promuje implementację GUI WPF z wykorzystaniem VB albo C#, a następnie dołączenie kodu C++/CLI w postaci zewnętrznej biblioteki to nic nie stoi na przeszkodzie abyśmy skorzystali z GUI opisanego językiem XAML bezpośrednio w projekcie C++/CLI. Zdani jesteśmy jednak na implementację interfejsu samemu od początku. W pierwszym kroku należy do naszego projektu (CLR Empty Project Visual C++) dodać referencje do nast. bibliotek: PresentationCore, PresentationFramework, System oraz WindowsBase. Aplikacja WPF to po prostu obiekt klasy dziedziczącej po Application. Uruchomienie wymaga wywołania metody Run do której przekazujemy obiekt Window. W celu utworzenia tego obiektu możemy wykorzystać plik zawierający GUI w języku XAML i metodę XamlReader. Dodajmy nowy plik Main.cpp z nast. kodem (wymagany jest również MainWindow.xaml zawierający przynajmniej element Window): <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="583" Width="600"> </Window> 1 Więcej informacji można znaleźć tutaj: http://www.codeguru.com/cpp/cpp/cpp_managed/general/article.php/c16355/using-wpf-with-managed- C.htm 1
#include <windows.h> using namespace System; using namespace System::Windows; using namespace System::IO; using namespace System::Windows::Markup; using namespace System::Windows::Controls; public ref class MyApplication : public Application public: MyApplication(Window ^win) // add constructor code here ; [STAThread] int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmd, int ncmd) Stream^ st = File::OpenRead("MainWindow.xaml"); Window^ win = (Window^)XamlReader::Load(st, nullptr); Application^ app = gcnew MyApplication(win); app->run(win); Wewnątrz klasy aplikacji umieszczamy referencje obiektów do których dostęp u będziemy potrzebowali: RichTextBox, ListBox, TextBox, Button, Label. public ref class MyApplication : public Application private: Button ^addbtn, ^invokebtn, ^setbtn; private: RichTextBox ^codertb; ( ) Wewnątrz konstruktora klasy aplikacji przypisujemy referencje do żywych obiektów GUI i przypinamy kody obsługi interesujących nas zdarzeń (kliknięcie przycisku oraz zmiana zaznaczonego indeksu w kontrolce ListBox). public: MyApplication(Window ^win) addbtn = (Button^)win->FindName("addButton"); addbtn->click += gcnew RoutedEventHandler(this, &MyApplication::OnAddBtnClick); methodslistbox = (ListBox^)win->FindName("methodsListBox"); methodslistbox->selectionchanged += gcnew System::Windows::Controls::SelectionChangedEventHandler(this, &MyApplication::OnSelectionMethodsListBoxChanged); Metody obsługi zdarzeń umieszczamy wewnątrz klasy aplikacji private: System::Void OnSelectionMethodsListBoxChanged(Object^ sender, SelectionChangedEventArgs^ e) // Add your code here 2
II. Dynamiczna kompilacja kodu źródłowego Cel: Zapoznanie z mechanizmem CodeDOM Code Document Object Model. CodeDOM reprezentowany jest jako drzewo kontenerów, a korzeniem każdej takiej kompilowanej struktury jest CodeCompileUnit. Metoda statyczna CreateProvider klasy CodeDomProvider zwraca instancję kompilatora tej klasy dla konkretnego języka: CodeDomProvider^ prov = CodeDomProvider::CreateProvider("VisualBasic"); Klasa CompilerParameters reprezentuje parametry używane do wywołania kompilatora. CompilerParameters^ param = gcnew CompilerParameters(); Pozwala ustalić czy rezultatem kompilacji ma być program wykonywalny czy biblioteka, jego nazwę, czy mają zostać dołączone informacje dla debbugera, dodać wymagane referencje, ustalić poziom raportowania błędów i wiele innych. Nas interesuje właściwość GenerateInMemory która informuje kompilator aby moduł nie był zapisywany jako. param->generateinmemory = true; Aby skompilować nowy kod VB należy utworzyć reprezentujący go obiekt klasy CodeSnippetCompileUnit. CodeSnippetCompileUnit^ csu = gcnew CodeSnippetCompileUnit(nowy_kod); Do kompilacji modułu opisanego przy pomocy CodeDOM służy metoda CompileAssemblyFromDom obiektu kompilatora. Jako parametry przyjmuje obydwa powyższe poarametry. Obiekt param reprezentujący parametry kompilacji oraz obiekt csu reprezentujący fragment kodu podlegającego kompilacji. CompilerResults^ res = prov->compileassemblyfromdom(param, csu); Otrzymany obiekt klasy CompilerResults reprezentuje wynik kompilacji i pozwala między innymi na przeglądanie kolekcji błędów i ostrzeżeń kompilacji (właściwość Errors) albo na dostęp do skompilowanego assembly (właściwość CompiledAssembly typu Assembly). Do pobrania interesującego nas typu służy metoda GetType("namespace.type") obiektu klasy Assembly. Zwraca ona obiekt typu Type udostepniający między innymi metody GetMethods, GetProperties oraz GetFields zwracające odpowiednio kolekcje obiektów MethodInfo, PropertyInfo i FieldsInfo. Analogicznie do pobrania konkretnej metody, właściwości, itp służą metody GetMethod("nazwa_metody"), GetProperty("nazwa_właściwości"), itp Metodę możemy wywołać przy pomocy metody Invoke obiektu klasy MethodInfo. Przyjmuje ona dwa parametry. Pierwszy to obiekt którego metoda jest wykonywana (ignorowany w przypadku metody statycznej), drugi to tablica parametrów wykonywanej metody (liczba, kolejność i typy parametrów muszą być zgodne z sygnaturą wywoływanej metody). W celu pobrania bądź ustawienia wartości właściwości bądź pola obj służą metody: GetValue(obj) i GetValue(obj). 3
Do powołania instancji danego typu służy statyczna metoda CreateInstance klasy Activator: obj = Activator::CreateInstance(t); III. Zadanie laboratoryjne Cel: Pogłębienie wiedzy na temat C++/CLI i CodeDOM. Napisać program w C++/CLI z interfejsem graficznym WPF kompilujący prosty kod VB. Kod składa się z pojedynczej klasy zawierającej dwie metody i dwie właściwości/pola. Pozwala na wyświetlenie błędów kompilacji (w przypadku nie powodzenia kompilacji) bądź dostępnych metod i właściwości (w przeciwnym przypadku). Pozwala również na uruchomienie zaznaczonej metody i jej wywołanie oraz wyświetlenie wyniku. W przypadku właściwości/pola ustawienie/odczytanie wartości i odczytanie typu. Przykładowe GUI: Do wprowadzania kodu można wykorzystać kontrolkę RichTextBox natomiast błędy, metody i zmienne można wyświetlać w ListBox ie posiada on właściwość SelectedIndex określającą indeks zaznaczonego pola. Pozostałe kontrolki to zwykłe etykiety, przyciski i pola tekstowe. 4