Úvod
V tomto článku si ukážeme, ako používať vo Visual Basic .NET funkcie z knižníc Windows Application Interface(WIN API). Pre názornú ukážku som naprogramoval jednoduchú škodoradostnú aplikáciu, ktorá po spustení roztrasie všetky otvorené okná v systéme, pričom svoje okno nezobrazí ani na ploche ani na taskbare. Pri pokuse o zavretie okna aplikácie program zachytí event a nastaví e.cancel = true. Čiže stornuje zavretie okna.
Technické riešenie
Vytvoríme si prázdnu štandardnú aplikáciu vo Visual Studiu. Hneď pod deklaráciu triedy okna vpíšeme deklaráciu funkcií WIN API, ktoré budeme používať. Pri deklarovaní API funkcie sa používajú rezervované slová
Declare Function <menoFunkcie> Lib <menoDllKniznice> (<atr1>,<atr2>,<atr3>,<…>) as <typ>
Pre naše potreby si teda deklarujeme nasledujúce dve funkcie :
Declare Function MoveWindow Lib "user32.dll" (ByVal hwnd As IntPtr, ByVal x As Integer, ByVal y As Integer, ByVal Width As Integer, ByVal Height As Integer, ByVal Repaint As Integer) As Integer
Declare Function GetWindowRect Lib "user32.dll" (ByVal hwnd As IntPtr, ByRef lpRect As RECT) As Integer
Ako vidno obe sa nachádzajú v knižnici user32.dll. Funkcia MoveWindow presunie okno na pozície zadané v argumentoch tejto funkcie. Čiže x – Xová pozícia ľavého horného rohu okna, y – Ynová pozícia ľavého horného rohu okna, width – Šírka okna v pixeloch rátaná od premennej x, height – Výška okna v pixeloch rátaná od premennej y. Pozorný čitateľ si určite všimol, že som nespomenul argument hwnd. Urobil som tak naschvál lebo mu bude venovaný nasledujúci odstavec.
Window handle
Operačný systém Windows dostal názov aký má pravdepodobne preto, že sa skladá celý z okien. Pričom oknom nie je iba okno aplikácie, ale pri troche nadhľadu môžeme oknom pomenovať každý kontrol, ktorý je v systéme(napr. aj každé tlačítko má svoje vlastné hwnd). Aby sme vedeli s jednotlivými oknami rozumne pracovať potrebujeme aby každý prvok mal svoj jedinečný identifikátor. Tým je window handle, číslo, ktoré jednoznačne identifikuje okno v systéme. Takže do prvého parametru funkcie MoveWindow zadáme postupne identifikátory všetkých spustených okien.
Druhá WIN API funkcia, ktorú sme si zadeklarovali je GetWindowRect, ktorá nám zistí aktuálnu pozíciu otvoreného okna. Ako prvý parameter ide už teraz známi hwnd. S druhým parametrom je to trošku komplikovanejšie. Vstupom do tohto argumentu je štruktúra RECT, ktorá vypadá nasledovne.
Public Structure RECT
Public left As Integer
Public top As Integer
Public right As Integer
Public bottom As Integer
End Structure
Zaujímavé ďalej je, že štruktúra vstupuje do funkcie s ByRef, čo znamená, že čokoľvek WIN API funkcia zmení v našej štruktúre ostane zmenené aj po dobehnutí funkcie. Jednoducho povedané funkcia naplní do štruktúry polohu okna.
Left – pozícia ľavého rohu okna od ľavého rohu monitora v pixeloch
Right – pozícia pravého rohu okna od ľavého rohu monitora v pixeloch
Top – pozícia horného rohu okna od horného rohu monitora v pixeloch
Bottom – pozícia dolného rohu okna od horného rohu monitora v pixeloch
Ďalšou vecou, čo musíme ešte na začiatku programu zadefinovať je timer, ktorý nám bude zabezpečovať nekonečnú slučku. Zadefinujeme ho cez rezervované slovo WithEvents aby sme dokázali zachytiť udalosť tiknutia timera.
Private WithEvents timer As System.Windows.Forms.Timer
Štartujeme
Vytvoríme si metódu, ktorá zachytáva udalosť nášho formulára Load. Ako prvé pri štarte aplikácie nastavíme formuláru aby sa nezobrazil v lište nastavením jeho vlastnosti ShowInTaskBar na false a následne ho skryjeme aby užívateľ nemohol kliknúť na krížik pomocou jeho metódy Hide. Následne vytvoríme timer a nastavíme mu opakovanie tikania každú 1 milisekundu a spustíme ho. Timer.Enabled = True.
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Me.ShowInTaskbar = False
Me.Hide()
timer = New System.Windows.Forms.Timer
timer.Interval = 1
timer.Enabled = True
End Sub
Jadro aplikácie
Jadrom našej škodoradostnej aplikácie je určite metóda timer_Tick, ktorá sa vykoná pri každom tiknutí časovača. Ako teda celé jadro funguje. Najprv si vytvoríme zoznam všetkých procesov, ktoré sú v systéme. Pomocou cyklu prechádzame celý zoznam pričom zaujímavé sú pre nás iba tie procesy, ktoré majú okno s vyplneným titulkom. Ak sa nájde také okno vygeneruje sa náhodné číslo od -5 po 5 a uloží sa do premennej ranNum. Pomocou funkcie GetWindowRect zistíme aktuálnu polohu okna a uložíme ju do štruktúry r. Nakoniec zavoláme funkciu MoveWindow a do parametrov jej zadáme položky zo štruktúry r a pričítame náhodne vygenerované číslo. V poslednom argumente funkcie je zadané číslo jedna. Tento argument totiž môže nadobúdať iba dva stavy. Nula a Jedna. Nula znamená, že pri presune okna sa jeho stará pozícia neprekreslí a vykreslí sa len nová. V praxi to potom vypadá tak, akoby okno za sebou nechávalo pás. Keď nastavíme argument na jednotku okno sa štandardne pri presune prekresľuje. Keď túto operáciu opakujeme rýchlo a často vytvára sa efekt akoby okno kmitalo okolo určitého miesta.
Private Sub timer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles timer.Tick
Dim processes() As Process = Process.GetProcesses 'Nacitame vsetky procesy v systeme
Dim r As RECT, random As New Random, ranNum As Integer
For i As Integer = 0 To processes.Length - 1 'Cyklus cez vsetky procesy
If Not processes(i).MainWindowTitle = String.Empty Then 'Ak ma okno procesu vyplneny titulok
ranNum = random.Next(-5, 5) 'Vygenerujeme nahodne cislo
GetWindowRect(processes(i).MainWindowHandle, r) 'Do r nahrame aktualnu poziciu okna
'Presunieme okno
MoveWindow(processes(i).MainWindowHandle, r.left + ranNum, r.top + ranNum, r.right - r.left + ranNum, r.bottom - r.top + ranNum, 1)
End If
Next
End Sub
Nedám sa zavrieť
Cieľom našej škodoradostnej aplikácie je ostať čo najdlhšie v behu. To znamená nedať sa zatvoriť. V metóde Form1_FormClosing potom nastavíme CancelArgument rovné True. To spôsobí, že sa aplikácia pri požiadavke systému o zavretie nezavrie.
Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
e.Cancel = True
End Sub
Záver
Naša aplikácia je hotová a pripravená na poslanie mailom neskúsenému kamarátovi alebo na predvedenie v školských učebniach :-). Dúfam, že som aspoň načrtol cestu ako sa dá pracovať vo Visual Basic .NET s Windows Application Interface.
Všetky konštruktívne otázky, poznámky a opravy k článku sú vítané.
Celý zdrojový kód si môžete stiahnuť tu