Aplikační rozhraní systému Windows
Aplikační rozhraní systému Windows, neboli Windows API (dříve známé jako Win32
API) je souhrn metod poskytujících základní funkce pro všechny Windows aplikace.
Tyto metody jsou určeny pro procedurální model programování a netvoří tedy žádný
objektový model.
Základní součásti Windows API by se daly velmi stručně shrnout do následujících
kategorií:
Základní služby (kernel32.dll)
Souborový systém, zařízení, správa paměti, procesů a vláken.
Pokročilé služby (advapi32.dll)
Správa registru a uživatelských účtů, ukončení a restart systému, řízení služeb.
Rozhraní grafického zařízení (gdi32.dll)
Grafický výstup na zařízení jako je obrazovka nebo tiskárna.
Uživatelské rozhraní (user32.dll)
Řízení uživatelského rozhraní pomocí vstupů z klávesnice a myši, základní služby
pro ovládací prvky.
Dialogová okna (comdlg32.dll)
Běžně používaná dialogová okna pro otevření souboru, uložení souboru, výběr
barvy nebo písma.
Základní ovládací prvky (comctl32.dll)
Běžně používané ovládací prvky jako například panel nástrojů, stavový panel,
záložky, nebo ukazatel průběhu.
Prostředí Windows (shell32.dll)
Funkce spojené s uživatelským rozhraním Windows (Průzkumník), jako například
správa pracovní plochy, hlavního panelu a nabídky Start.
Vývoj aplikací pomocí Windows API
Kdysi dávno bylo použití Win32 API jediný způsob, jak vytvářet aplikace pro
Windows. Vzhledem k jeho obrovské komplexnosti (API funkcí jsou řádově stovky)
bylo nutné vytvořit něco, co by programátorům usnadnilo práci. Tak vznikly
knihovny tříd jako dodnes používané a rozvíjené MFC (Microsoft Foundation
Classes) nebo konkurenční OWL (Object Windows Library) používané v produktech
firmy Borland. Později vznikly plně objektově orientované aplikační frameworky
jako je Java nebo .NET Framework (který zastřešuje téměř celé Windows API).
Pro vývoj aplikací používajících Windows API je potřeba vývojová sada Windows
SDK (dříve známá jako Platform SDK), která obsahuje kompletní dokumentaci,
příklady a užitečné nástroje. SDK je součástí produktu Visual Studio 2005/2008
Standard nebo lepšího, nebo jeho aktuální verzi lze zdarma stáhnout
zde (1,3 GB). Toto SDK obsahuje také hlavičkové soubory (*.h), ve kterých
jsou definice všech API funkcí v systému Windows (definice lze nalézt i v MSDN
bez nutnosti prohledávat hlavičkové soubory). Definice funkcí, struktur a
konstant jsou v jazyce C++, tudíž pro použití v managed jazycích je nutno použít
tzv. Marshalling, což je konverze unmanaged datových typů na managed datové
typy. Třídy pro práci s unmanaged kódem jsou umístěny ve jmenném prostoru
System.Runtime.InteropServices.
Přehledný seznam funkcí Windows API seřazených do kategorií lze nalézt
zde.
Deklarace nativních metod
V .NET jazycích lze nadeklarovat a používat libovolné exportovatelné funkce z
nativních knihoven (označit funkce jako exportovatelné musí autor nativní
knihovny). Omezení tedy není pouze na knihovny Windows API. Příklad užitečné
nativní knihovny, která nemá .NET wrapper ani COM rozhraní a přesto ji lze
použít je například FMOD Sound System, což je multiplatformí API pro práci se
zvukem (dostupné pro Windows, Linux, Mac a všechny současné herní konzole),
které se svojí funkčností vyrovná systému DirectSound.
Ve Visual Basicu .NET je možné nadeklarovat nativní metody dvěma způsoby. První
způsob je převzatý ze starého Visual Basicu 6.0 (pro kompatibilitu se starými
projekty):
[ <attributelist> ] [ accessmodifier ] [ Shadows ] [ Overloads ] _
Declare [ charsetmodifier ] [ Sub ] name Lib "libname" _
[ Alias "aliasname" ] [ ([ parameterlist ]) ]
Public Declare Function SendMessage Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
Druhý způsob je nový, čistě Frameworkový s použitím atributu (používá se i v
jazyku C#):
<DllImport("user32.dll")> _
Public Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As Integer, ByVal lParam As IntPtr) As Integer
End Function
Pro přehlednost je doporučeno umísťovat všechny deklarace nativních metod,
struktur a konstant do třídy (nebo modulu) s názvem UnsafeNativeMethods.
Následující příklad je metoda zobrazující informační bublinu pro textové pole,
což se výborně hodí například při validaci k upozornění na nesprávnou hodnotu
místo MessageBoxu, který v tomto případě vyžaduje zbytečnou interakci uživatele.
Pro zobrazení informační bubliny je použito Windows API a je to jeden z
příkladů, kdy prostředky .NET Frameworku nejsou dostačující (podobnou funkčnost
sice nabízí komponenta ToolTip, ta však není specializovaná pro textové pole).
Imports System.Runtime.InteropServices
Public Module UnsafeNativeMethods
'Konstanty pro zobrazení
Public Const ECM_FIRST As Integer = &H1500
Public Const EM_SHOWBALLOONTIP As Integer = ECM_FIRST + 3
'Atributy struktury určují rozložení struktury v paměti přesně tak
'jak je definováno a použití znakové sady Unicode pro textové řetězce.
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
Public Structure EDITBALLOONTIP
Public cbStruct As UInteger
Public pszTitle As String
Public pszText As String
Public ttiIcon As Integer
End Structure
'Definice nativní metody pro odesílání zpráv
<DllImport("user32.dll")> _
Public Function SendMessage( _
ByVal hWnd As IntPtr, _
ByVal Msg As UInteger, _
ByVal wParam As Integer, _
ByVal lParam As IntPtr) As Integer
End Function
''' <summary>
''' Zobrazí informační bublinu pro textové pole.
''' </summary>
''' <param name="textBox">Textové pole.</param>
''' <param name="text">Informační text.</param>
''' <param name="title">Text záhlaví bubliny.</param>
''' <param name="icon">Ikona bubliny.</param>
''' <remarks></remarks>
Public Sub ShowTextBoxBalloonTip(ByVal textBox As TextBox, ByVal text As String, ByVal title As String, ByVal icon As ToolTipIcon)
'Validace předávaných parametrů.
If textBox Is Nothing Then
Throw New ArgumentNullException("textBox")
End If
If Not [Enum].IsDefined(GetType(ToolTipIcon), icon) Then
Throw New ArgumentException("icon")
End If
Dim lParam As New UnsafeNativeMethods.EDITBALLOONTIP
With lParam
'Je nutno uvést velikost struktury v paměti.
.cbStruct = Marshal.SizeOf(lParam)
.pszText = text
.pszTitle = title
'Hodnota výčtového typu odpovídá konstantě pro zobrazenou ikonu.
.ttiIcon = DirectCast(icon, Integer)
End With
'Alokace místa v paměti pro strukturu.
Dim ptr As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(lParam))
Marshal.StructureToPtr(lParam, ptr, True)
'Odeslání zprávy textovému poli.
SendMessage(textBox.Handle, EM_SHOWBALLOONTIP, 0, ptr)
'Uvolnění místa v paměti zabraného strukturou.
'Toto v managed kódu automaticky zajišťuje Garbage Collector,
'v unmanaged kódu musíme vše uvolňovat ručně. Je-li kód psán nedbale,
'dochází v programu k únikům paměti (Memory Leaks), což je
'neuvolnění místa v paměti, které již není potřeba.
Marshal.FreeHGlobal(ptr)
End Sub
End End Module
Tipy pro Platform Invoke
- Při deklaracích nativních funkcí a struktur kde jsou používány textové řetězce
používejte CharSet:=CharSet.Auto. Z nějakého důvodu není výchozí hodnota Auto
ale ANSI, tudíž textové řetězce by se v případě implicitní deklarace zobrazovaly
chybně. Hodnota Auto automaticky rozhodne, zda-li je nutné použít kódování ANSI
nebo Unicode.
- Platform Invoke používejte skutečně jen v případě, že se bez něho neobejdete.
Je-li prováděn Marshalling mezi nekompatibilními typy, spotřebuje se na něj asi
10-30 instrukcí na jedno zavolání, což při častém použití může mít negativní
dopad na výkon. Marshalling mezi vzájemně kompatibilními typy (int vs.
System.Int32) nemá na výkon žádný negativní dopad.
Přehled běžných unmanaged typů a jejich .NET ekvivalentů
C/C++ |
.NET Framework |
Visual Basic .NET |
HANDLE, LPVOID, LPWORD, void* |
System.IntPtr |
System.IntPtr |
const char*, char*, LPCTSTR, LPSTR, LPWSTR, Wchar_t* |
System.String, System.Text.StringBuilder |
String, System.Text.StringBuilder |
DWORD, Ulong, unsigned long |
System.UInt32 |
UInteger |
bool |
System.Boolean |
Boolean |
LP<struktura> |
|
ByRef <struktura> |
SIZE_T |
System.UInt32 |
UInteger |
LPDWORD |
System.UInt32 |
UInteger |
LPTSTR |
System.Text.StringBuilder |
System.Text.StringBuilder |
PULARGE_INTEGER |
System.UInt64 |
ULong |
WORD |
System.UInt16 |
UShort |
Byte, unsigned char |
System.Byte |
Byte |
Short |
System.Int16 |
Short |
int, Long |
System.Int32 |
Integer |
float |
System.Single |
Single |
double |
System.Double |
Double |
NULL |
System.IntPtr.Zero |
System.IntPtr.Zero |
Uint |
System.UInt32 |
UInteger |
Pokud narazím na nějaké další používané unmanaged typy, doplním je do této
tabulky.