WinAPI Tutorial: wie finde ich WinAPI Funktionen

Übersicht BlitzBasic FAQ und Tutorials

Neue Antwort erstellen

Xenon

Betreff: WinAPI Tutorial: wie finde ich WinAPI Funktionen

BeitragSo, Aug 29, 2004 19:31
Antworten mit Zitat
Benutzer-Profile anzeigen
Hi,

ich dachte mir mal ich schreibe ein Tutorial, für die, die wissen wollen, wie man auf diese ganze WinAPI Sachen kommt.
Vorerst würde ich empfehlen mit C++ ein wenig WinAPI zu programmieren (Fenster erstellen, usw.) dazu gibt es sehr gute Tutorials auf http://www.robsite.de/tutorials.php?tut=windows .
Danach ist natürlich die MSDN (Microsoft Developer Network), die Microsoft Hilfe der beste Freund in Sachen WinAPI.

Zu finden ist sie unter http://msdn.microsoft.com mit Suche und allem Drum und Dran. Ich selbst habe MSDN für MSVC++ 6 installiert, welche weniger Internettraffic braucht, ebenso kann ich damit schneller Suchen.

Also gut, wenn man jetzt etwas benötigt und den Befehl dafür weiß, kommt man zur Eintragung in die *.decls Datei, dabei empfielt es sich sie gleich nennen wie die dazugehörige DLL (zB User32.dll --> User32.decls).

Geschrieben wird folgendermaßen: Funktionsname_fuer_BB(Parameter1, Parameter2, ...):"Funktionsname_in_der_DLL"

Die Parameter müsst ihr natürlich dementsprechend benennen und auch ihre Typen bestimmen. Erwartet die DLL zB einen Floatparameter müsst ihr wie in BB # hinten dran machen.

Dies ist ebenfalls ganz wichtig für den Rückgabewert. Die meisten WinAPI Funktionen geben einen Boolean, der 4 Bytes groß ist, also ein Integer zurück, ob die Funktion geklappt hat.
Da ist es ganz wichtig, dass ihr nach dem Funktionsnamen für BB auch ein % (für Integer) macht, da BB sonst glaub es ist eine Funkion des Typs void (= kein Rückgabewert).
Ihr müsst das % natürlich auch hinmachen, wenn ihr den Rückgabewert nicht braucht, da es sonst zu Programmabstürtzen kommen kann!

Wichtig für die DLLs sind auch Pointer, zB zu Strukturen. Die Pointer kennzeichnet man nach dem Namen mit * (statt %, $ oder #).
Da BB allerdings keine Pointer hat muss hier eine Bank übergeben werden. Dabei wird dann die Adresse der Bank übergeben.
Bei der Bank ist natürlich wichtig folgendes zu beachten, dass sie mindestens so groß sein muss, wie der Variablentyp/die Struktur, auf die der Pointer zeigt.
Wird also ein Pointer zu einer Struktur mit 4 Integer Werten erwartet muss die Bank min. 16 Bytes groß sein (4*4).

Der nächste Teil sind die Konstanten die immer wieder bei WinAPI Befehlen als Parameter vorkommen. Diese müssen in dem entsprechenden C++-Header Files nachgesehen werden. Solltet ihr keinen C++ Compiler haben, saugt euch am besten DevC++(google...).
Die wichtigsten Header Files sind dabei winuser.h und winbase.h.

Und jetzt noch ein kleines Anwendungsbeispiel. (Hier arbeite ich mit der installierten MSDN, da sie einige vorteile, was das Suchen anbelangt hat)

Unser Ziel ist es den Rand eines B3D Fensters zu entfernen, zB für einen Ladebildschirm wie UT beim Start einen hat.

Wie man das macht habe ich schon mal gesehen bei einem anderen Programm, da wurde es mit den GDI Befehlen erledigt, indem man den Rand des Fensters überzeichnet.
Dieser Lösungsansatz ist vllt. nicht schlecht, aber ich würde es bessern finden wenn man das "schöner" lösen könnte, also an die Arbeit!

Bei den WinAPI Tutorials haben wir gelernt das jedes Fenster einen Style (und auch einen Extended Style) hat. Ebenso haben wir dabei gelernt, welchen Style man angeben muss, damit das Fenster jetzt einen Titelleiste, etc. hat. Also müsste die Lösung sein, den Style des Fensters zu ändern.

Jetzt begeben wir uns auf die Suche nach dem Befehl, mit dem man den Style eines Fensters ändern kann.
Da Windows die Kommunikation mit SendMessage macht schauen wir zuerst, ob wir eine passende WM (Window Message) finden.
Kurzerhand bei der MSDN unter dem Tabber Index WM_ eingegeben und schon sehen wir alle Window Message Konstanten schön aufgelistet. Wir scrollen runter zum S um etwas mit WM_SETSTYLE oder ähnlichem zu finden.
Was wir vorfinden sind die Nachrichten

WM_SETCURSOR,
WM_SETFOCUS,
WM_SETFONT,
WM_SETHOTKEY,
WM_SETICON,
WM_SETREDRAW,
WM_SETTEXT und
WM_SETTINGCHANGE.

Sicher interessante Nachrichten, aber nicht das was wir suchen.

Also suchen wir eine passende Funktion, die vllt. mit Set anfängt. Da es unter Set sehr viele Befehle geben, müssen wir das ganze genauer angeben. Wir würden uns ja den Befehl SetWindowStyle wünschen, welchen es aber nicht gibt, aber belassen wir es mal mit SetWindow und suchen wir hier:
SetWindowContextHelpId - Hier sagt uns schon der Name, das es nicht das ist, was wir suchen Wink
SetWindowExt - Nach dem anzeigen der Beschreibung und kurzem überfliegen dieser merken wir, das auch das nicht die gewünschte Funktion ist, da diese Funktion offensichtlich zur Klasse CDC gehört.
Die nächsten 2 Ext funktionen scheinen auch nicht das zu sein was wir suchen.
SetWindowLong - Mal die Beschreibung ansehen... Jaaaaa das ist es =)

Also die Funktion mal in die *.decls einarbeiten.

Code: [AUSKLAPPEN]
LONG SetWindowLong(
  HWND hWnd,       // handle of window
  int nIndex,      // offset of value to set
  LONG dwNewLong   // new value
);

Return Values
If the function succeeds, the return value is the previous value of the specified 32-bit integer.


Das sagt uns, das die Funktion einen Integer zurückgiebt und 3 Integer erwartet, also:

Code: [AUSKLAPPEN]
SetWindowLong%(hWnd%, nIndex%, dwNewLong%):"SetWindowLong"


In welcher DLL steht diese Funktion denn jetzt?

Dazu finden wir weiter unten die entsprechende Information:
Code: [AUSKLAPPEN]
QuickInfo
  Windows NT: Requires version 3.1 or later.
  Windows: Requires Windows 95 or later.
  Windows CE: Requires version 1.0 or later.
  Header: Declared in winuser.h.
  Import Library: Use user32.lib.
  Unicode: Implemented as Unicode and ANSI versions on Windows NT.


user32.lib ist also die lib Datei (die für C++ ungefähr das selbe sind, wie die decls für BB) gehört also zur user32.dll.
Ebenso hilft uns dieser teil unter Header, welcher uns sagt das die Funktion in der winuser.h deklariert ist.

Wichtig ist dieser Teil:
Code: [AUSKLAPPEN]
Unicode: Implemented as Unicode and ANSI versions on Windows NT.


Windows Funktionen dieser Art gibt es immer in 2 Versionen. Als ANSI und als Unicode Funktion. Der Unterschied ist bei den Strings. Unicode Strings benötigen doppelt so viel Speicher wie ANSI Strings, da ein Zeichen 2 Bytes verbraucht statt einem.
BB arbeitet nur mit ANSI Strings, also brauchen wir hier die Ansi Version. Diese haben nach dem Funktionsnamen immer ein A (Bei Unicodefunktionen ist nach dem Funktionsnamen ein W).
Deshalb müssen wir unseren Userlib eintrag wie folgt abändern:
Code: [AUSKLAPPEN]
SetWindowLong%(hWnd%, nIndex%, dwNewLong%):"SetWindowLongA"


Gut, soweit hätten wir das. jetzt sehen wir uns die Parameter etwas genauer an.

hWnd ist das WindowHandle unseres B3D Fensters, welches wir schon aus früheren Versuchen rausbekommen, deshalb werde ich hier nicht beschreiben wie man da rankommt.
nIndex beinhaltet die Information, welchen Wert wir ändern wollen. Da wir den Style ändern wollen wählen wir GWL_STYLE. Dies ist die erste Konstante auf die wir treffen. Darum schauen wir jetzt in der Headerdatei winuser.h nach, welchen Wert sie hat:
Code: [AUSKLAPPEN]
#define GWL_STYLE           (-16)

Das führt uns zu folgender Konstantendeklaration:
Code: [AUSKLAPPEN]
Const GWL_STYLE = -16

Der 3. Parameter enthält unseren neuen Style. Gut welchen nehmen wir da? Wir wollen den, den B3D nutzt so wenig wie möglich verändern, also müssten wir wissen, welchen Windowstyle B3D standardmäßig hat.
Die Funktion mit der wir das rausfinden ist nicht weit: GetWindowLong, wird uns in der MSDN unter "See Also" schon empfohlen.
Diesmal nur 2 Parameter:
Code: [AUSKLAPPEN]
GetWindowLong%(hWnd%, nIndex%):"GetWindowLongA"


Da wir jetzt die Funktionen haben, die wir brauchen möchte ich hier die komplette decls Datei angeben, die wir für unsere Codes brauchen:
Code: [AUSKLAPPEN]
.lib "User32.dll"

MessageBox%(hWnd%, nachricht$, titel$, style%):"MessageBoxA"

FindWindow%(class$, fenster$):"FindWindowA"
ShowWindow%(hwnd,cmdshow)

AdjustWindowRect%(rect*, style%, menu%):"AdjustWindowRect"

SystemParametersInfo%(Action%, Param1%, Param2*, WinIni%):"SystemParametersInfoA"

MoveWindow%(hwnd%, x%, y%, width%, height%, repaint%):"MoveWindow"

SetWindowLong%(hWnd%, index%, NewLong%):"SetWindowLongA"
GetWindowLong%(hWnd%,wIndex%):"GetWindowLongA"


Unter BB starten wir kurzerhand einen Test, mit dem wir den WindowStyle von BB bekommen:

Code: [AUSKLAPPEN]
Const SW_HIDE             = 0
Const SW_SHOW             = 5

Const MB_OK                       = $00000000
Const MB_ICONASTERISK             = $00000040

Const GWL_STYLE           = (-16)

AppTitle "Test"
hwnd = FindWindow("Blitz Runtime Class", "Test")   ; So nebenbei: auf den Klassennamen "Blitz Runtime Class" bin ich mit dem nützlichen Programm Spy++ gekommen, ist bei MSVC++ dabei.

If hwnd = 0 Then RuntimeError "Konnte Fenster nicht finden!"

ShowWindow(hwnd, SW_HIDE)

style = GetWindowLong(hwnd, GWL_STYLE)

MessageBox(hwnd, "Der Style ist 0x"+Hex(style)+".", "Information", MB_OK Or MB_ICONASTERISK)

End


Das Ergebnis dieses Testes lautet 0x04CA0000.
Da der Windowstyle aus sogenannten Styleflags besteht müssen wir jetzt die Einzellnen Flags rausfiltern.

Dazu muss man wissen wie die Zahlensysteme (Hexdezimal und Binär) funktionieren. Jede Hexdezimale Ziffer enthält eigentlich 4 Binäre Ziffern.
Darum hier die Wertetabelle:

0000 = 0
0001 = 1
0010 = 2
0011 = 3
0100 = 4
0101 = 5
0110 = 6
0111 = 7
1000 = 8
1001 = 9
1010 = A
1011 = B
1100 = C
1101 = D
1110 = E
1111 = F

Die Flags sind ja die einzellnen Bytes, dh. die Flagwerte sind:

0001 = 1
0010 = 2
0100 = 4
1000 = 8

Darum müssen wir jetzt die einzellnen Ziffern des Styles in diese Flags aufteilen:

4 = 4, hier ist keine Unterteilung.
C = 8 + 4.
A = 8 + 2.

Also haben wir nun die Flags die benutzt wurden:

0x04000000,
0x00800000,
0x00400000,
0x00080000 und
0x00020000.

Jetzt sehen wir einfach nach, welche Konstantennamen diese Flags haben. Dazu verwenden wir wieder die winuser.h, in der die Stylekonstanten sind:

Code: [AUSKLAPPEN]
#define WS_OVERLAPPED       0x00000000L
#define WS_POPUP            0x80000000L
#define WS_CHILD            0x40000000L
#define WS_MINIMIZE         0x20000000L
#define WS_VISIBLE          0x10000000L
#define WS_DISABLED         0x08000000L
#define WS_CLIPSIBLINGS     0x04000000L
#define WS_CLIPCHILDREN     0x02000000L
#define WS_MAXIMIZE         0x01000000L
#define WS_CAPTION          0x00C00000L
#define WS_BORDER           0x00800000L
#define WS_DLGFRAME         0x00400000L
#define WS_VSCROLL          0x00200000L
#define WS_HSCROLL          0x00100000L
#define WS_SYSMENU          0x00080000L
#define WS_THICKFRAME       0x00040000L
#define WS_GROUP            0x00020000L
#define WS_TABSTOP          0x00010000L


Daraus folgt, wir haben folgende Flags gesetzt:
WS_CLIPSIBLINGS,
WS_CAPTION (welches eigentlich in WS_BORDER und WS_DLGFRAME unterteilt ist),
WS_SYSMENU und
WS_GROUP.

Da wir ein Fenster ohne Rahmen wollen, nehmen wir den Style WS_POPUP.
Die Styles WS_CAPTION und WS_SYSMENU fallen weg.

Also sieht unser neuer Windowstyle so aus:
Code: [AUSKLAPPEN]
WS_CLIPSIBLINGS Or WS_POPUP Or WS_GROUP


Jetzt haben wir alles, was wir brauchen um unser (Lade-)bild zu zeigen:
Code: [AUSKLAPPEN]
Const SPI_GETWORKAREA     = 48

Const WS_POPUP            = $80000000
Const WS_CLIPSIBLINGS     = $04000000
Const WS_GROUP            = $00020000

Const SW_HIDE             = 0
Const SW_SHOW             = 5

Const MB_OK                       = $00000000
Const MB_ICONASTERISK             = $00000040

Const GWL_STYLE           = (-16)
Const GWL_EXSTYLE         = (-20)

Graphics 400, 300, 32, 2
SetBuffer BackBuffer()

AppTitle "Lade..."
hwnd = FindWindow("Blitz Runtime Class", "Lade...")

If hwnd = 0 Then RuntimeError "Konnte Fenster nicht finden!"

ShowWindow(hwnd, SW_HIDE)

SetWindowLong(hwnd, GWL_STYLE, WS_CLIPSIBLINGS Or WS_POPUP)
SetWindowLong(hwnd, GWL_EXSTYLE, 0)

AdjustWindowSizeMiddle(hwnd, 400, 300)

ShowWindow(hwnd, SW_SHOW)

Repeat
   Cls
   Text 200, 150, "Laden...", 1, 1
   Flip
Until KeyDown(1)
End

Function AdjustWindowSizeMiddle(hwnd, width, height, menu = False)
   Local rectbank, desktopbank
   Local dx, dy, dw, dh
   
   rectbank = CreateBank(16)
   PokeInt rectbank, 0, 0
   PokeInt rectbank, 4, 0
   PokeInt rectbank, 8, width
   PokeInt rectbank, 12, height
   
   AdjustWindowRect(rectbank, GetWindowLong(hwnd, GWL_STYLE), menu)
   
   desktopbank = CreateBank(16)
   SystemParametersInfo(SPI_GETWORKAREA, 0, desktopbank, 0)
   
   width = PeekInt(rectbank, 8)-PeekInt(rectbank, 0)
   height = PeekInt(rectbank, 12)-PeekInt(rectbank, 4)
   
   dx = PeekInt(desktopbank, 0)
   dy = PeekInt(desktopbank, 4)
   dw = PeekInt(desktopbank, 8)-dx
   dh = PeekInt(desktopbank, 12)-dy
   
   MoveWindow hwnd, dx+(dw-width)/2, dy+(dh-height)/2, width, height, True
   
   FreeBank rectbank
   FreeBank desktopbank
End Function


Ich verstecke zuerst das Fenster, weil sonst beim Fensterstyle noch WS_VISIBLE dabeiwäre (auch beim Testprogramm habe ich das gemacht!).
Dann setze ich den neuen Style. Außerdem lösche ich auch den Extendet Style, welcher bei B3D WS_EX_WINDOWEDGE (0x00000100) ist, da wir ja keine Windowedge (Fensterrahmen) haben.
Danach positioniere ich das Fenster in der Mitte des Desktops, mit ebenfalls einer Funktion, die aus WinAPI Funkionen besteht (zu finden ist diese Funktion und 2 andere im Codearchiv).
Dies hat nicht nur den Vorteil, dass das Fenster neu mittig ausgerichtet wird, sondern auch, dass bei mir (Win XP) Zeichenfehler vermieden werden (die Titelleiste war noch da, ihr könnt die Funktion ja mal auskommentieren und schaun ob bei euch der selbe Fehler auftritt).
Danach zeige ich das Fenster wieder an.

Zum Schluss dieses Tutorials will ich noch erklären, wie Strings unter C++ (und auch BB!) im Speicher gehalten werden:

Ihr wisst ja sicher das jedes Zeichen 1 Byte (bei ANSI Strings) Speicher benötigen. Dieses Byte ist der ASCII Wert dieses Zeichens. Eine ASCII Tabelle findet ihr auf www.blitzbase.de .
Damit man weiß, wann der String sein Ende erreicht hat, steht im Speicher am Ende des Strings eine 0, die den String "terminiert".

Übergeben werden bei DLL aufrufen, wenn Strings gefragt werden immer nur Pointer zu der Speicheradresse des Strings!

Auf diesem Wissen könnte man sich eine Funktion schreiben, die aus einem String einen Pointer (bzw. vorerst eine Bank) macht.

Hatte man in der decls zB folgenden Code:
Code: [AUSKLAPPEN]
Funktion%(txt$)

kann man nun diesen Code nehmen:
Code: [AUSKLAPPEN]
Funktion%(txt*)


Hier die Funktion die unseren String in eine Bank speichert:
Code: [AUSKLAPPEN]
Function StringToBank(txt$)
   Local bank, length
   
   length = Len(txt$)
   bank = CreateBank(length+1)
   
   For i=1 To length
      PokeByte bank, i-1, Asc(Mid(txt, i, 1))
   Next
   
   PokeByte bank, length, 0
   
   Return bank
End Function


Und ein Beispielcode:
Code: [AUSKLAPPEN]
bank = StringToBank("Hallo!")
Funktion(bank)
Freebank bank


Den String aber umzuwandeln und dann zu übergeben ist allerdings langsamer, da BB sonst einfach einen Pointer zum String übergibt, da es den String sowieso schon so im Speicher hat.
Dieses Beispiel soll lediglich erklären, wie ein String gespeichert wird.

So, das wars mit meinem Tutorial, war nicht wenig Arbeit Wink
Das Beispiel nach der Suche von SetWindowLong war übrigens gestellt, da ich das mit SetWindowLong schon wusste als ich wirklich nach der schöneren Methode gesucht hab, als ich den Rahmen weghaben wollte.
Man wünscht sich sowieso selten weitere Funktionalität, die man mit der WinAPI bewerkstelligen kann, es ist eher so, dass man zufällig auf so einen Code trifft und diesen dann für BB anändert.

Mfg Xenon

D2006

Administrator

BeitragMo, Sep 13, 2004 16:52
Antworten mit Zitat
Benutzer-Profile anzeigen
Dem guten Tutorial kann ich noch den WinAPI Katalog von ActiveVB hinzufügen.

http://www.activevb.de/rubrike...talog.html

Hier sind eine Vielzahl von WinAPI Funktionen mit Parameter und DLL und bei manchen auch mit Beschreibung.

MfG
 

SebastianB

BeitragFr, Okt 22, 2004 11:44
Antworten mit Zitat
Benutzer-Profile anzeigen
Das Tutorial gefällt mir sehr gut. Gut gemacht, Xenon. Ist genau das, was ich gesucht hab Wink

Hier noch eine kleine Ergänzung:
Wer den Borland C++ Compiler hat und Spy++ sucht, der verwende das Programm Winsight (ws32.exe).
Never change a running system

Neue Antwort erstellen


Übersicht BlitzBasic FAQ und Tutorials

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group