Základy Platform Invoke

Ondřej Linhart       21.10.2008       VB.NET, WinAPI       12907 zobrazení

.NET Framework je velmi rozsáhlá knihovna, která pokrývá prakticky vše, co bychom mohli ve svých aplikacích potřebovat. Někdy se ale vyskytne požadavek na funkčnost, kdy jsou prostředky Frameworku nedostačující, nebo chybí úplně. V této situaci je na místě použít tzv. Platform Invoke, neboli volání nativních (Unmanaged) funkcí aplikačního rozhraní systému Windows. Tento článek vás seznámí se základy této problematiky.

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.

 

hodnocení článku

2 bodů / 2 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Mohlo by vás také zajímat

Windows Presentation Foundation (WPF) - díl 7.: Grid

Grid je jedna z nejdůležitější a nejpoužívanějších pozicovacích komponent ve WPF. Ulehčuje návrh formulářů a své uplatnění nachází v řadě scénářů.

Windows Presentation Foundation (WPF) - díl 9.: ContentControl, Button a TextBlock

Článěk se věnuje základům důležité komponenty ContentControl a dvěma vizuálním komponentám: Button (tlačítko) a TextBlock (textový blok).

Práce s časovými pásmy a letním časem v aplikaci a databázi - díl 1.: Úvod do časových pásem a letního času

Ve článku se snažím popsat úskalí, která přináší konverze času přes více časových pásem a pravidel pro počítání letního času.

 

 

Nový příspěvek

 

Diskuse: Základy Platform Invoke

Mám problém s připojením DLL knihovny, která je napsaná v Delphi, do projektu ve VB.NET. Knihovna není Assembly ani COM komponenta. Hlavičkové soubory s importními knihovnami nemám k dispozici. Mám pouze definici a popis metod. Zkouším tedy knihovnu připojit do VB.NET přes nativní metody, ale bohužel nevím jak definovat parametry typu PChar. Můžete mně prosím poradit? Děkuji.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Využívám Delphi dll v C#. Argumenty metod v Delphi jsou typu PChar a v C# string nebo StringBuilder. Předpokládám že v VB to bude obdobné. Jir

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Diskuse: Základy Platform Invoke

Třída StringBuilder je, právě koukám do Object Browseru, ve jmenném prostoru System.Text. Tedy ne System.StringBuilder, ale System.Text.StringBuilder.

Nebo mi něco jako obvykle uniká?

Ještě by myslím autor mohl doplnit, že v případě ukazatelů na základní datové typy je deklarace ByRef.

  int *pInt    ->     ByRef pInt As Integer
  double *pDbl ->     ByRef pDbl As Double

Tak či onak, užitečný článek, některé věci z něho mi ještě nebyly známy.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

S tím StringBuilderem máte pravdu, děkuji, opraveno.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback