Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: VB6: Suoraa muistinkäsittelyä taulukkomuuttujalla

Merri [17.06.2006 17:27:23]

#

Visual Basic 6:ssa ei varsinaisesti ole olemassa ns. pointtereita, joiden avulla voi käsitellä muistia suoraan. Kiertotie on kuitenkin olemassa ja VB:stä löytyy tuki muutamalle olennaiselle komennolle, mm. VarPtr ja StrPtr (variable pointer ja string pointer). Näiden avulla saa tietää, missä kohtaa tietokoneen muistia muuttuja oikeasti sijaitsee. Näin voi esimerkiksi käyttää CopyMemorya (RtlMoveMemory) kopioimaan tietoa jostakin muuttujasta johonkin toiseen muuttujaan.

Ongelmaksi kuitenkin muotoutuu se, että CopyMemoryn käyttö on hitaahkoa: API-kutsut ovat paljon raskaampia kuin esimerkiksi taulukkomuuttujan (array) käsitteleminen, koska API-kutsun tekeminen suorittaa muutamia "ylimääräisiä" kutsuja.

Ratkaisuna ongelmaan löytyykin kehittyneempi ja ovelampi keino. Myös taulukkomuuttujilla on olemassa muistissa oma rakenteensa. Mitäpä jos huijaamme taulukkomuuttujan osoittamaan itsetehtyyn muuttujarakenteeseen? Tämä mahdollistaa sen, että voimme tehdä taulukkomuuttujan joka osoittaa mihin tahansa kohtaan muistissa! Toisin sanoen voisimme muuttaa toisten muuttujien, vaikkapa jonkin tekstimuuttujan, sisältöä käyttämällä omaa taulukkomuuttujaamme.

Taulukkomuuttujan rakenne tunnetaan nimellä SAFEARRAY. Tämän rakenne muuttuu hieman sen mukaan, montako ulottuvuutta muuttujalla on. Helppouden, selkeyden ja nopeuden vuoksi kannattaa yleensä käyttää yksiulotteista muuttujaa.


Alle laittamani esimerkkikoodi on hyvin yksinkertainen, eikä käytä tämän menetelmän todellista potentiaalia hyväksi. Se kuitenkin esittää runsain kommentein, miten homma saadaan tehtyä. Huomattavaa on, että tämä menetelmä on erittäin nopea, joten konekielelle käännetty koodi toimii paljon nopeammin kuin esim. Mid$-komennoin tai Replace$:lla vastaavan tempun tekevä koodi. Mitä enemmän käsiteltävää, sitä suurempi on nopeusetu!

Tätä menetelmää voi käyttää myös kuvadatan käsittelyyn muistissa suoraan ilman että tarvitsee tehdä uutta kopiota kuvasta (esimerkiksi käyttäen GetBitmapBits-APIa). Mahdollisuuksia on monia, milloin vain on tarvetta nopeudelle :)

Form1.frm

Option Explicit

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef lpvDest As Any, ByRef lpvSrc As Any, ByVal cbLen As Long)
' normaali VarPtr ei ota vastaan taulukkomuuttujia, joten lisätään tuki sille tällä tavalla
Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Var() As Any) As Long

' yksiulotteisen taulukkomuuttujan rakenteen tiedot
Private Type SafeArray1D
    Dimensions As Integer       ' ulottuvuudet (aseta arvoksi aina 1)
    FeatureFlags As Integer     ' ominaisuudet (nolla kelpaa peruskäytössä)
    ElementSize As Long         ' yhden elementin koko tavuina (1 käy Bytelle, 2 Integerille ja Booleanille...)
    LockCount As Long           ' lukitukset (en tiedä tämän tarkkaa tarkoitusta)
    DataPtr As Long             ' pointteri siihen kohtaan muistissa, jossa tieto sijaitsee
    Elements1D As Long          ' ensimmäisen ulottuvuuden elementtien määrä
    LBound1D As Long            ' ensimmäisen ulottuvuuden alaindeksi (nolla on hyvä)
End Type

' määritä SA-muuttuja, joka sisältää meidän oman taulukkomuuttujamme tiedot
Dim SA As SafeArray1D
' määritä tyhjä Taulukko-muuttuja, jonka huijaamme toimimaan omien tietojemme mukaisesti
Dim Taulukko() As Integer

Form1.frm

Private Sub Form_Load()
    ' alustetaan oman taulukkomuuttujan tiedot
    With SA
        ' yksiulotteinen
        .Dimensions = 1
        ' yhden elementin koko: kaksi tavua
        .ElementSize = 2
        ' elementtien määrä: todella monta (suurin positiivinen Long-arvo)
        .Elements1D = &H7FFFFFFF
        ' asetamme elementtien määrän näin, jottei sitä tarvitse muutella jatkuvasti
    End With
    ' asetetaan Taulukko-muuttujalle nyt tämä meidän oma SafeArray-rakenteemme
    CopyMemory ByVal VarPtrArray(Taulukko), VarPtr(SA), 4&
End Sub

Private Sub Form_Unload(Cancel As Integer)
    ' palautetaan Taulukko-muuttujan alkuperäinen tila, muuten VB kaatuu
    ' tärkeää! nollan täytyy olla Long, joten &-merkki on pakollinen!
    CopyMemory ByVal VarPtrArray(Taulukko), 0&, 4&
    ' emme alustaneet Taulukkoa mitenkään (esim. käyttämälle ReDimiä),
    ' joten se ei ennestään osoittanut minnekään muistissa: sen pointterin arvo oli 0
End Sub

Form1.frm: lisää komentonappula Command1 ja sille tämä koodi

Private Sub Command1_Click()
    Dim strTesti As String
    Dim lngA As Long

    ' asetetaan testimuuttujaamme jotakin tekstiä
    strTesti = "BBB! Terve!"

    ' kerrotaan alkutilanne
    MsgBox strTesti, , "Ennen"

    ' huomioi: yksi merkki on aina kaksi tavua, siksi SA.ElementSize on kaksi!

    ' nyt jallitetaan VB:tä: muutetaan Taulukko osoittamaan tähän testimuuttujaan!
    SA.DataPtr = StrPtr(strTesti)

    ' muutetaan kaikki B-kirjaimet A-kirjaimiksi
    For lngA = 0 To Len(strTesti) - 1
        ' B:n merkistökoodi on 66, A puolestaan on 65
        If Taulukko(lngA) = 66 Then Taulukko(lngA) = 65
    Next lngA

    ' lopuksi katsotaan miltä strTesti nyt näyttää!
    MsgBox strTesti, , "Jälkeen"
End Sub

kayttaja-4976 [19.06.2006 11:16:50]

#

Vaikuttaa jännältä! Tällä tavalla siis voi tehdä raskaampaa nopeammin? Kokeilen myöhemmin...

Merri [19.06.2006 15:01:24]

#

Jep, voi. Käytännön tarkoitus on pudottaa pois turhia suuria muistinsiirtoja kuin myös mahdollistaa tietyn datan (kuten stringien) käsittely nopeammin.

Muista tallentaa usein, koska koodin pysäyttäminen IDE:ssä saattaa kaataa ohjelman ja VB:n. Itselläni kävin näin kerran tätä esimerkkiä tehdessä. Jostain kumman syystä ohjelman kaatuessa myös leikepöytä tyhjenee, joten tallentaminen on lähes ainoa keino estää tietoja katoilemasta.

nomic [20.06.2006 12:05:48]

#

Loistava pätkä! Ihmetyttää vain, miksei vb:hin ole integroitu mitään tämmöistä. Perinteistä, että pitää jallitaa kääntäjää. :)

Merri [20.06.2006 18:49:49]

#

Aika pitkälle historiallisista syistä: Visual Basic siirtyi vasta viitosversion myötä siihen, että ohjelma käännettiin konekielelle. Mahdollisesti myös pointterit olisivat päätyneet kieleen, jos Microsoft ei yksinkertaisesti olisi lopetuttanut VB:n kehitystä moneksi vuodeksi. Valitettavasti he kuitenkin keskittivät kaikki voimansa .NET:in kehittämiseen :/


Vaihtoehtobasiceistahan FreeBASICissa on ne kaikki mitä vaan tahtoo. Tosin siitä puuttuu vielä luokat, sitten kun ne löytyy niin aika kova kieli on kasassa.

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta