Pobierz prezentację
Pobieranie prezentacji. Proszę czekać
1
Jacek Matulewski 21 listopada 2016
Programowanie Windows WinAPI i platforma .NET Jacek Matulewski 21 listopada 2016
2
Mechanizm Platform Invoke w platformie .NET
Programowanie Windows Mechanizm Platform Invoke w platformie .NET
3
Trivia Platformy .NET, WinRT (W8), UWP (W10) tworzą osobną warstwę z własną „wirtualną maszyną” Platforma .NET ułatwia odwołania do niezarządzanych DLL via mechanizm PInvoke Ważne źródło: (deklaracja, przykłady użycia, zamienniki w .NET) Tutorial w MSDN:
4
Trivia Czynności wykonywane przy Platform Invoke:
lokalizacja biblioteki eksportującej funkcję załadowanie jej do pamięci (przy pierwszym uruchomieniu) pobranie adresu funkcji wrzucanie argumentów na stos i wywołanie funkcji (przekazanie kontroli wątku do kodu niezarządzanego) pobranie i konwersja typu zwracanej wartości powrót kontroli do kodu zarządzanego
5
Trivia
6
Atrybut DllImport Atrybut DllImportAttribute pozwala na zadeklarowanie metody statycznej, która reprezentuje funkcję z biblioteki DLL Konstruktor przyjmuje nazwę niezarządzanej biblioteki DLL: public DllImportAttribute(string dllName) Wymaga zadeklarowania przestrzeni nazw: using System.Runtime.InteropServices; Składnia (w najprostszej formie): [DllImport("Biblioteka.dll")] static extern Funkcja(argumenty); Problem konwersji typów
7
Atrybut DllImport Oryginalna sygnatura funkcji WinAPI:
BOOL WINAPI MessageBeep( _In_ UINT uType //sound type ); Import (może być w klasie statycznej, ale niekoniecznie): [DllImport("User32.dll")] static extern bool MessageBeep(uint uType); Odpowiednik w platformie .NET: System.Media.SystemSounds.Beep.Play
8
Atrybut DllImport Przykład użycia: object sender, EventArgs e) {
private void button1_Click( object sender, EventArgs e) { MessageBeep(0); MessageBeep(48); }
9
Atrybut DllImport Oryginalna sygnatura funkcji WinAPI:
int WINAPI MessageBox( _In_opt_ HWND hWnd, _In_opt_ LPCTSTR lpText, _In_opt_ LPCTSTR lpCaption, _In_ UINT uType ); Przykładowe wartości uType: MB_OK (0), MB_YESNO (4), MB_ABORTRETRYIGNORE (2), MB_HELP (16384), MB_CANCELTRYCONTINUE (6), MB_ICONERROR (16), MB_ICONQUESTION (32), ...
10
Atrybut DllImport Import: Zmiana nazwy funkcji – własność EntryPoint
[DllImport("user32.dll", EntryPoint = "MessageBox", CharSet = CharSet.Unicode)] static extern int _MessageBox(IntPtr hWnd, String text, String caption, uint type); Zmiana nazwy funkcji – własność EntryPoint Zestaw znaków w argumentach-łańcuchach – CharSet (w WinAPI – tablica znaków, wersja A i W) Odpowiednik w platformie .NET: System.Windows.Forms.MessageBox.Show
11
Atrybut DllImport Przykład użycia: Odpowiednik w platformie .NET:
private void button1_Click(object sender, EventArgs e) { _MessageBox(this.Handle, "Polskie litery: ąćęłńóśżź", "Tytuł", 64); } Odpowiednik w platformie .NET: MessageBox.Show( "Polskie litery: ąćęłńóśżź", "Tytuł", MessageBoxButtons.OK, MessageBoxIcon.Information);
12
Użycie typów wyliczeniowych
Oryginalna sygnatura funkcji WinAPI: UINT WINAPI WinExec( _In_ LPCSTR lpCmdLine, _In_ UINT uCmdShow ); Wybrane wartości uCmdShow: SW_HIDE (0), SW_MAXIMIZE (3), SW_MINIMIZE (6), SW_RESTORE (9), SW_SHOW (5), SW_DEFAULT (10) Te same wartości w ShowWindow, CreateProcess itd.
13
Typy wyliczeniowe Import:
[DllImport("kernel32.dll")] static extern uint WinExec(string polecenie, uint stanOkna); To nie jest zgodne z duchem platformy .NET – typ wyliczeniowy Te sam problem w MessageBeep i MessageBox Odpowiednik w platformie .NET: System.Diagnostics.Process.Start
14
Typy wyliczeniowe Typ wyliczeniowy z określonym typem uint - konwersja: public enum StanOkna : uint { Ukryte = 0, Normalme, Zminimalizowane, Zmaksymalizowane, Nieaktywne, Domyślne = 10 }; Import: [DllImport("kernel32.dll")] static extern uint WinExec(string polecenie, StanOkna stanOkna);
15
Atrybut DllImport Przykład użycia: EventArgs e) {
private void button1_Click(object sender, EventArgs e) { uint wynik = WinExec(textBox1.Text, StanOkna.Normalme); if (wynik <= 31) MessageBox.Show("Błąd " + wynik + "! Nie udało się uruchomić " + textBox1.Text); }
16
Atrybut DllImport Przykład użycia z listą wyboru (ComboBox):
private void button1_Click(object sender, EventArgs e) { uint wynik = WinExec(textBox1.Text, (StanOkna)cbStanOkna.SelectedIndex); if (wynik <= 31) MessageBox.Show("Błąd " + wynik + "! Nie udało się uruchomić " + textBox1.Text); }
17
Demo Funkcje MessageBeep, MessageBox, WinExec
Import, wskazanie nazwy funkcji, typ wyliczeniowy
18
Zwracanie wartości przez referencje
Oryginalna sygnatura funkcji WinAPI: BOOL GetDiskFreeSpaceEx( LPCTSTR lpDirectoryName, PULARGE_INTEGER lpFreeBytesAvailableToCaller, PULARGE_INTEGER lpTotalNumberOfBytes, PULARGE_INTEGER lpTotalNumberOfFreeBytes ); Typy użyte w sygnaturze (zob. MSDN Windows Data Types): LPCTSTR = LPCWSTR (Unicode) lub LPCSTR (ASCII) typedef __nullterminated CONST CHAR *LPCSTR; PULARGE_INTEGER = wskaźnik do LARGE_INTEGER
19
Zwracanie wartości przez referencje
Liczba całkowita 64-bitowa: typedef union _LARGE_INTEGER { struct { DWORD LowPart; LONG HighPart; }; struct { DWORD LowPart; LONG HighPart; } u; LONGLONG QuadPart; } LARGE_INTEGER, *PLARGE_INTEGER; Liczba całkowita 64-bitowa: W przypadku, gdy kompilator obsługuje liczby 64-bitowe, należy używać typu LONGLONG. Jeżeli nie – dwóch DWORD.
20
Zwracanie wartości przez referencje
Import: [DllImport("kernel32.dll")] static extern bool GetDiskFreeSpaceEx( string katalog, ref long wolneMiejsceDlaUzytkownika, ref long rozmiarDysku, ref long wolneMiejsceNaDysku); Odpowiednik w platformie .NET: System.IO.DriveInfo
21
Zwracanie wartości przez referencje
Przykład użycia: private int wolneMiejsceNaDysku( string katalogGlownyDysku) { int wolneMiejsceNaDyskuProcenty; long wolneMiejsceDlaUzytkownika = 0; long rozmiarDysku = 0; long wolneMiejsceNaDysku = 0; if (GetDiskFreeSpaceEx( katalogGlownyDysku, ref wolneMiejsceDlaUzytkownika, ref rozmiarDysku, ref wolneMiejsceNaDysku)) { ...
22
Zwracanie wartości przez referencje
Przykład użycia: private int wolneMiejsceNaDysku( string katalogGlownyDysku) { ... if (GetDiskFreeSpaceEx( ... ) { wolneMiejsceNaDyskuProcenty = (int)(100 * (rozmiarDysku – wolneMiejsceNaDysku) / (double)rozmiarDysku); } else wolneMiejsceNaDyskuProcenty = -1; return wolneMiejsceNaDyskuProcenty; }
23
Zwracanie wartości przez referencje
Przykład użycia: public Form1() { InitializeComponent(); string katalogGłównyDysku = System.Environment.GetLogicalDrives()[0]; int procentZajetosci = wolneMiejsceNaDysku(katalogGlownyDysku); if (procentZajetosci >= 0) progressBar1.Value = procentZajetosci; }
24
Demo Funkcja GetDiskFreeSpaceEx Zwracanie wartości przez referencje
25
Tablica znaków – łańuchy
Oryginalna sygnatura funkcji WinAPI: UINT WINAPI GetWindowsDirectory( _Out_ LPTSTR lpBuffer, // _In_ UINT uSize ); ASCII: typedef CHAR *LPSTR; UNICODE: typedef WCHAR *LPWSTR; Import: [DllImport("kernel32.dll")] static extern uint GetWindowsDirectory( StringBuilder bufor, uint rozmiarBufora); Odpowiednik w platformie .NET: System.Environment.SystemDirectory
26
Tablica znaków – łańuchy
Przykład użycia (StringBuilder wymaga trochę wysiłku): private void button4_Click(object sender, EventArgs e) { const int MAX_PATH = 260; StringBuilder katalogWindows = new StringBuilder(MAX_PATH); uint rozmiar = (uint)katalogWindows.Capacity; GetWindowsDirectory(katalogWindows, rozmiar); MessageBox.Show(katalogWindows.ToString()); } Tak samo funkcje: GetWindowsDirectory i GetSystemDirectory
27
Color Picker Cel: chcemy pobierać kolor piksela na ekranie znajdujący się pod kursorem myszy Wyłączamy skalowanie aplikacji – skalowanie pozycji myszy: BOOL WINAPI SetProcessDPIAware(void); Pobieramy pozycję myszy: BOOL WINAPI GetPhysicalCursorPos( _Out_ LPPOINT lpPoint ); Pobieramy obraz (bitmapę) ze wskazanego prostokąta (np. 1×1): BOOL BitBlt(_In_ HDC hdcDest, _In_ int nXDest, _In_ int nYDest, _In_ int nWidth, _In_ int nHeight, _In_ HDC hdcSrc, _In_ int nXSrc, _In_ int nYSrc, _In_ DWORD dwRop); Odpowiednik w platformie .NET: System.Windows.Forms.Cursor.Position Odpowiednik w platformie .NET: System.Drawing.Graphics.CopyFromScreen
28
Color Picker Import: public static extern bool SetProcessDPIAware();
[DllImport("user32.dll")] public static extern bool SetProcessDPIAware(); public static extern bool GetCursorPos(ref Point lpPoint); public static extern bool GetPhysicalCursorPos(ref Point lpPoint); [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)] public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
29
Color Picker Przykład użycia (z projektu Huberta Wojtowicza):
private static Bitmap pikselEkranu = new Bitmap( , 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb); public static Color PobierzKolorPiksela(Point położenie) { //grafika źródła (ekran) using (Graphics gd = Graphics.FromImage(pikselEkranu)) //grafika bitmapy, w której zostanie zapisany kolor using (Graphics gs = Graphics.FromHwnd(IntPtr.Zero)) { ... } return screenPixel.GetPixel(0, 0); //zwracany jedyny piksel
30
Color Picker Przykład użycia (z projektu Huberta Wojtowicza):
private static Bitmap pikselEkranu = new Bitmap( , 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb); public static Color PobierzKolorPiksela(Point położenie) { ... IntPtr hsDC = gs.GetHdc(); //uchwyt do DC źródła IntPtr hdDC = gd.GetHdc(); //uchwyt do DC celu BitBlt( hdDC, 0, 0, 1, 1, //cel (uchwyt i geometria) hsDC, położenie.X, położenie.Y, //źródło (uchwyt i poł.) (int)CopyPixelOperation.SourceCopy); //tryb wklejania gd.ReleaseHdc(); gs.ReleaseHdc(); //zwalnianie uchwytów }
31
Color Picker Przykład użycia (wymaga kontrolek Timer, Label, Panel):
private void timer1_Tick(object sender, EventArgs e) { Point pozycjaKursora = Cursor.Position; GetPhysicalCursorPos(ref pozycjaKursora); label3.Text = pozycjaKursora.ToString(); panel1.BackColor = PobierzKolorPiksela(pozycjaKursora); }
32
Zrzut całego ekranu Przykład użycia (zmodyfikowana funkcja pobierająca piksel): public static Bitmap PobierzZrzutEkranu() { RECT obszar = new RECT(); GetWindowRect(GetDesktopWindow(), out obszar); Bitmap bitmap = new Bitmap(obszar.Right - obszar.Left, obszar.Bottom - obszar.Top); using (Graphics gd = Graphics.FromImage(bitmap)) using (Graphics gs = Graphics.FromHwnd(IntPtr.Zero)) ... } return bitmap;
33
Zrzut całego ekranu Przykład użycia (zmodyfikowana funkcja pobierająca piksel): public static Bitmap PobierzZrzutEkranu() { ... IntPtr hsDC = gs.GetHdc(); IntPtr hdDC = gd.GetHdc(); BitBlt( hdDC, 0, 0, obszar.Right - obszar.Left, obszar.Bottom - obszar.Top, hsDC, 0, 0, (int)CopyPixelOperation.SourceCopy); gd.ReleaseHdc(); gs.ReleaseHdc(); }
34
Zrzut całego ekranu Deklarowanie struktur danych na potrzeby PInvoke:
[StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; public int Top; public int Right; public int Bottom; }
35
Zrzut ekranu w obrębie okna
Zamiast funkcji BitBlt można użyć PrintWindow (służy do kopiowania zawartości okna do wskazanego DC) Nie pozwala na operacje bitowe przy kopiowaniu Oryginalna sygnatura funkcji WinAPI: BOOL PrintWindow(HWND hwnd, HDC hdcBlt, UINT nFlags); Import: [DllImport("user32.dll")] static extern bool PrintWindow( IntPtr hWnd, IntPtr hDC, int flags);
36
Zrzut ekranu w obrębie okna
Przykład użycia: public static Bitmap PobierzZrzutOkna(IntPtr uchwytOkna, bool tylkoObszarKlienta = false) { RECT obszar = new RECT(); GetWindowRect(uchwytOkna, out obszar); Bitmap bitmap = new Bitmap(obszar.Right - obszar.Left, obszar.Bottom - obszar.Top); using (Graphics gd = Graphics.FromImage(bitmap)) PrintWindow(uchwytOkna, gd.GetHdc(), tylkoObszarKlienta?1:0); } return bitmap;
37
Demo Funkcja BitBlt, PrintWindow
Pobieranie zrzutów ekanu, color picker
38
Demo Funkcja EnumWindows, EnumChildWindows
Zadanie domowe: na zrzucie ekranu zaznaczyć położenia wszystkich okien (w tym kontrolek) z tytułem i uchwytem
39
Atrybut DllImport Niektóre pola i własności atrybutu DllImportAttribute: CallingConvention (Cdecl, StdCall, ThisCall, Winapi) CharSet (zestaw znaków: Ansi, Unicode) EntryPoint (nazwa importowanej funkcji) SetLastError (wywoływana jest metoda SetLastError, jej wynik można pobrać funkcją GetLastError) MarshalAsAttribute – określa niezarządzany typ argumentu lub wartości zwracanej przez funkcję; zwykle zbędny; jedyny wyjątek to niejednoznaczność łańcuchów [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool MessageBeep(uint uType); Atrybuty In i Out określają kierunek konwersji argumentów
40
Atrybut MarshalAs Przykład dla argumentów (zał. Unicode):
[DllImport("User32.dll", CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.I4)] static extern Int32 MessageBoxEx( IntPtr hWnd, //unicode [param: MarshalAs(UnmanagedType.LPTStr)] String lpText, [param: MarshalAs(UnmanagedType.LPTStr)] String lpCaption, //32-bitowa (4-bajtowa) liczba całkowita bez znaku [param: MarshalAs(UnmanagedType.U4)] UInt32 uType, //16-bitowa (2-bajtowa) liczba całkowita bez znaku [param: MarshalAs(UnmanagedType.U2)] UInt16 wLanguageId);
41
Programowanie Windows
Komunikaty Windows
42
Identyfikacja adresata komunikatu
Funkcja FindWindow (już poznaliśmy) zwraca uchwyt okna o podanej klasie lub tytule HWND WINAPI FindWindow( _In_opt_ LPCTSTR lpClassName, _In_opt_ LPCTSTR lpWindowName ); Import: [DllImport("user32.dll")] static extern HWND FindWindow(string nazwaKlasy, string nazwaOkna);
43
Wysyłanie komunikatu Funkcje SendMessage i PostMessage (także już znane) wysyłają komunikaty do okna o podanym uchwycie LRESULT WINAPI SendMessage( _In_ HWND hWnd, _In_ UINT Msg, _In_ WPARAM wParam, _In_ LPARAM lParam); Import: [DllImport("user32.dll")] static extern int SendMessage( HWND hwnd, uint Msg, int wParam, int lParam);
44
Wysyłanie komunikatu Stałe: Lista komunikatów:
public const int WM_CLOSE = 0x0010; public const int WM_SYSCOMMAND = 0x0112; public const int WM_NCMOUSEMOVE = 0x00A0; public const int WM_PAINT = 0x000F; public const int WM_KEYDOWN = 0x0100; public const int WM_KEYUP = 0x0101; public const int WM_CHAR = 0x0102; Lista komunikatów:
45
Wysyłanie komunikatu Przykład użycia: {
private void button1_Click(object sender, EventArgs e) { string nazwaOkna = textBox1.Text; HWND uchwyt = FindWindow(null, nazwaOkna); if (uchwyt != HWND.Zero) SendMessage(uchwyt, WM_CLOSE, 0, 0); else MessageBox.Show("Nie ma okna o tytule \"" + nazwaOkna + "\""); }
46
Odbieranie komunikatu
Nadpisywanie procedury okna (ListBox, MultiColumn = true, ColumnWidth = 30): protected override void WndProc(ref Message m) { if (m.Msg!=308) listBox1.Items.Add(m.Msg); //308 = WM_CTLCOLORLISTBOX base.WndProc(ref m); }
47
Odbieranie komunikatu
Nadpisywanie procedury okna (ListBox, MultiColumn = true, ColumnWidth = 30): switch(m.Msg) { case WM_NCMOUSEMOVE: long lParam=(long)m.LParam; long x=lParam & 0x0000FFFF; long y=(lParam & 0xFFFF0000) >> 16; label1.Text = "(Komunikat) Mysz poza obszarem klienta: " + x + "," + y; break; }
Podobne prezentacje
© 2024 SlidePlayer.pl Inc.
All rights reserved.