• Nie Znaleziono Wyników

WinAPI i platforma .NET

N/A
N/A
Protected

Academic year: 2021

Share "WinAPI i platforma .NET"

Copied!
48
0
0

Pełen tekst

(1)

WinAPI i platforma .NET

Jacek Matulewski

21 listopada 2016 Programowanie Windows

http://www.fizyka.umk.pl/~jacek/dydaktyka/winprog/

(2)

Mechanizm Platform Invoke w platformie .NET

Programowanie Windows

(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:

http://www.pinvoke.net/index.aspx

(deklaracja, przykłady użycia, zamienniki w .NET)

• Tutorial w MSDN: https://msdn.microsoft.com/en-

us/library/aa288468(v=vs.71).aspx

(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

https://msdn.microsoft.com/en-us/library/aa719485(v=vs.71).aspx

(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:

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:

[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:

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:

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)

• 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)) { ...

Zwracanie wartości przez referencje

(22)

• Przykład użycia:

private int wolneMiejsceNaDysku(

string katalogGlownyDysku) {

...

if (GetDiskFreeSpaceEx( ... ) { wolneMiejsceNaDyskuProcenty = (int)(100 * (rozmiarDysku – wolneMiejsceNaDysku) /

(double)rozmiarDysku);

} else wolneMiejsceNaDyskuProcenty = -1;

return wolneMiejsceNaDyskuProcenty;

}

Zwracanie wartości przez referencje

(23)

• Przykład użycia:

public Form1() {

InitializeComponent();

string katalogGłównyDysku =

System.Environment.GetLogicalDrives()[0];

int procentZajetosci =

wolneMiejsceNaDysku(katalogGlownyDysku);

if (procentZajetosci >= 0)

progressBar1.Value = procentZajetosci;

}

Zwracanie wartości przez referencje

(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)

• 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

Tablica znaków – łańuchy

(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:

[DllImport("user32.dll")]

public static extern bool SetProcessDPIAware();

[DllImport("user32.dll")]

public static extern bool GetCursorPos(ref Point lpPoint);

[DllImport("user32.dll")]

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)

• Przykład użycia (z projektu Huberta Wojtowicza):

private static Bitmap pikselEkranu = new Bitmap(

1, 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 }

Color Picker

(30)

• Przykład użycia (z projektu Huberta Wojtowicza):

private static Bitmap pikselEkranu = new Bitmap(

1, 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 }

...

Color Picker

(31)

• 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);

}

Color Picker

(32)

• 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;

}

Zrzut całego ekranu

(33)

• 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();

} ...

}

Zrzut całego ekranu

(34)

• Deklarowanie struktur danych na potrzeby PInvoke:

[StructLayout(LayoutKind.Sequential)]

public struct RECT {

public int Left;

public int Top;

public int Right;

public int Bottom;

}

Zrzut całego ekranu

(35)

• 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);

Zrzut ekranu w obrębie okna

(36)

• 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;

}

Zrzut ekranu w obrębie okna

(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);

• https://www.codeproject.com/articles/66245/marshaling-with-

csharp-chapter-1-introducing-marsh.aspx

(41)

Komunikaty Windows

Programowanie Windows

(42)

• 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);

Identyfikacja adresata komunikatu

(43)

• 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);

Wysyłanie komunikatu

(44)

• Stałe:

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:

http://www.pinvoke.net/default.aspx/Enums/WindowsMessages.html

Wysyłanie komunikatu

(45)

• 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 + "\"");

}

Wysyłanie komunikatu

(46)

• 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);

}

Odbieranie komunikatu

(47)

• 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;

}

Odbieranie komunikatu

(48)

Przykładowe pytania

• Jaka klasa atrybutu służy do importowania funkcji z niezarządzanych bibliotek DLL?

• Jakie modyfikatory muszą mieć deklaracje funkcji importowanych w Platform Invoke.

• Jaka własność atrybutu DllImport służy do wskazania nazwy importowanej funkcji, a jaka – zestawu znaków?

• Z jakim modyfikatorem powinny być deklarowane argumenty, jeżeli importowane funkcje zwracają przez nie wartości?

• Funkcja z biblioteki DLL zwraca wartość przez argument

będący łańcuchem. Jakiego typu (zarządzanego) powinien być

argument w deklaracji funkcji w platformie .NET?

Cytaty

Powiązane dokumenty

Pewne elementy wspólne dostrzec można, jak się wydaje, z młodo- prądnickim inwentarzem z jaskini Ciemnej w Ojcowie, ze spągowej części starszego lessu würmskiego 6..

Analizując wiele dostępnych artykułów, materia- łów różnych firm i publikacji internetowych, wydaje się rozsądne przyjęcie założenia, że w odległej per- spektywie czasowej

W pewnym szpitalu badano wagę noworodków przebywających na oddziale położniczym. a) Podaj najczęściej występującą wagę noworodka. b) Podaj wagę środkową noworodka na

Zadanie 2. Zbadano profile klientów banku A pod względem liczby osób w rodzinie.. Jeśli tak, to określić jej kierunek. Ocenić czy istnieje korelacja pomiędzy tymi cechami i jaki

Po pewnym czasie marszu ten Niemiec podszedł do nas, rozejrzał się czy inni Niemcy na nas nie patrzą, zaczął mówić, że na to co się tu dzieje nie może patrzeć bo odchodząc

Since the above two-hop neighborhood design motivates creating new trust relationships with friends of friends for better network connectivity under heavy churn, an important ques-

Jednakże browar z Piotrkowa Trybunalskiego jest zdania, że przymiotnik trybunalskie zbyt wyraźnie odnosi się współcześnie do nazwy tego miasta i dla- tego należy go

Maximum gdy funkcja jest najpierw rosnąca, a potem malejąca... Szukamy ekstremów