Hex Viewer (Scroll)

В данной статье реализуем скроллинг.

И так первое что мы добавим - это парочку глобальных переменных:

Dim Shared As Ulongint ullVScrollPos,ullMaxCount ' позиция скрола, кол-во прокручиваемых областей


Далее в процедуре создания CreateChildProc нашего самописного контрола мы добавим такую строчку:

SendMessage(childHwnd,WM_VSCROLL,SB_THUMBTRACK,0) ' установим ползунок прокрутки на нуль


Эту строку, мы потом перенесем в другое место (после загрузки файла). Данная команда заставит систему принудительно послать сообщение WM_VSCROLL с событием SB_THUMBTRACK. Мы обработаем сообщение WM_VSCROLL , обсчитаем нужные нам переменные и окно прорисует все корректно. У меня случалось такое, когда после загрузки программы и последующего скроллинга за ползунок, контрол возвращал совсем не ту информацию, которая должна быть. Поэтому, чтобы предотвратить такие ситуации, я принудительно как бы "инициализирую" контрол.

Кроме того, пока у нас нет загрузки файла и информации о нем, мы в эту же процедуру внесем и эти строчки:

If ullSizeFile>416 Then ' если развер файла больше кол-ва символов области прокрутки
    ullMaxCount = (ullSizeFile-416)\16+1 ' высчитываем
Else
    ullMaxCount = 0 ' иначе равно 0
Endif


В этих строчках мы получаем кол-во прокручиваемых областей (точнее строк), которые как вы поняли зависят от размера файла (в нашем случае пока буфера). В последствии мы эти строчки перенесем в другую процедуру (туда где будет записан код загрузки файла и его размера).

Процедуру CopyBytesProc перепишем так:

'заполнение буфера pbBytesBuffer данными
Sub CopyBytesProc()
    Dim As Ulongint ullTempSize
   If (ullNullOffsetView + 416) < ullSizeFile Then
    ullTempSize = 416
   Else
    ullTempSize = ullSizeFile - ullNullOffsetView
   Endif
    CopyMemory( pbBytesBuffer ,@TempDIM(ullNullOffsetView) , ullTempSize)
End Sub


В этом коде проверяется условие чтения в рамках читаемого буфера\файла. В зависимости от начального смещения ullNullOffsetView , чтение начинается с нужной точки. Сама же переменная ullNullOffsetView зависит от позиции скроллинга.

Далее добавляем 2 процедуры, работающие с логикой скроллинга:

' получение информации о скроллинге
Sub GetScrollinfoProc(hwnd As hwnd)
    Dim info As SCROLLINFO
    info.cbSize = Sizeof(SCROLLINFO)
    info.fMask = SIF_TRACKPOS
    GetScrollInfo(hwnd, SB_VERT, @info) ' получаем инфу о скроллинге от системы
    ullVScrollPos = info.nTrackPos ' получаем позицию скроллинга
End Sub

' обработка информации о скроллинге
Sub ScrollProc(hwnd As HWND, param As Integer)

    Dim As Integer vs
    Select Case param
        Case SB_TOP
            ullVScrollPos = 0 ' на начало страницы
        Case SB_BOTTOM
            ullVScrollPos = ullMaxCount 'на конец страницы
        Case SB_LINEUP
            If(ullVScrollPos > 0) Then
                ullVScrollPos-=1 'сдвигаемся ближе к началу страницы на 1
            Endif
        Case SB_LINEDOWN
            If(ullVScrollPos < ullMaxCount) Then
                ullVScrollPos+=1 'сдвигаемся ближе к концу страницы на 1
            Endif
        Case SB_PAGEUP
            If(ullVScrollPos > 10) Then
                ullVScrollPos-=10 'сдвигаемся ближе к началу страницы на 10
            Endif
        Case SB_PAGEDOWN
            If(ullVScrollPos < (ullMaxCount-10)) Then
                ullVScrollPos+=10 'сдвигаемся ближе к концу страницы на 10
            Endif
        Case SB_THUMBPOSITION,SB_THUMBTRACK ' если пользовательское перемещение на неопределенное кол-во
            GetScrollinfoProc(hwnd) ' получаем информацию из системы и высчитываем
    End Select

    Dim As SCROLLINFO sInfo
    sInfo.cbSize = Sizeof(SCROLLINFO)
    sInfo.fMask = SIF_ALL ' маска для любых действий со скролом
    sInfo.nMin  = 0
    sInfo.nMax  = ullMaxCount
    sInfo.nPos  = ullVScrollPos
    sInfo.nPage = 1
    SetScrollInfo (hwnd, SB_VERT,@sInfo, TRUE)
    InvalidateRect(hwnd,0,false)
    ullNullOffsetView = ullVScrollPos*16 ' получаем начальное смещение в видимой части окна
End Sub


Процедура GetScrollinfoProc работает только, когда пользователь скроллит прямо за ползунок. Система в этом случае присылает сообщения  SB_THUMBPOSITION,SB_THUMBTRACK. Все другие сообщения обрабатываются прямо в процедуре ScrollProc по установленным правилам.  Так же в этой процедуре вычисляется переменная ullNullOffsetView (начальное смещение в области видимости окна). И принудительно устанавливается скроллинг на нужную позицию с помощью SetScrollInfo , предварительно конечно же происходит заполнение структуры SCROLLINFO. После скроллинга нужно заставить наш контрол HEX перерисовать себя. Делается это с помощью функции InvalidateRect.

И чтобы все это работало, нужно в процедуре HexWndProc в событии WM_VSCROLL подставить наш обработчик:

ScrollProc(hwnd,Loword(wparam))


Кажется, все выглядит неплохо и должно работать, но есть одна маленькая деталь. При такой реализации скроллинга, мы упремся в размер загружаемого файла (приблизительно 32 ГБ). Да это кажется неплохо, особенно если учесть реализации некоторых программистов, которые вообще ограничивают свои программы размером в 2ГБ. Однако на сегодняшний день, когда размеры архивов могут достигать под сотни гигабайт, а жесткие диски исчисляются терабайтами, размер в 32 ГБ не такой уж и большой. Хочется сделать так, чтобы программа гарантировано (с запасом на будущее) загружала любой возможный файл с жесткого диска, даже если его размер будет соотносим с размером жесткого диска. Но как это сделать, когда максимальное число прокрутки , которое можно задать в структуре SCROLLINFO 32-битное число? А это в нашем случае получится (&hFFFFFFFF * &h10) байт. Да это больше чем в нашей реализации в 2 раза, но все равно мало, да и канителиться с отрицательными числами (от &h80000000 до &hFFFFFFFF) не очень-то удобно, хотя и реализуемо. Почему отрицательными? Для того, чтобы использовать 32-битное число "под потолок" , нам придется в параметр nMin структуры SCROLLINFO поставить число &h80000000 , а в nMax занести &h7FFFFFFF . Тогда у нас получится диапазон &h80000000-...0...-&h7FFFFFFF , где числа от &h80000000 до &hFFFFFFFF отрицательные. Ведь параметры nMin и nMax имеют тип Integer. И нам придется считаться с этими неудобствами в своей программе.

Все таки есть более простое и удобное решение. Нам всего лишь надо пропорционально привести [64х-битные значения смещения в окне] к [32х-битным значениям для ползунка]. Или наоборот 32->64 , когда позицию ползунка надо преобразовать к реальному смещению в окне. И поможет нам в этом деле старая добрая пропорция.

Давайте для теории сделаем 4 определения:

nPos - позиция ползунка, которую мы будем заталкивать в структуру SCROLLINFO и скармливать функции SetScrollInfo

nPos64 - абстрактная 64х-битная позиция ползунка, которая используется для получения верхнего смещения в видимой области HEX окна 

nMax - максимальная величина кол-ва прокручиваемых областей, которую мы задаем в параметр nMax структуры SCROLLINFO (этот параметр не может быть больше &h7FFFFFFF, определим пока его именно такого размера)

nMax64 - 64х-битное значение кол-ва прокручиваемых областей, по сути это: (размер нашего файла) \ (16 символов в одной строке)

Сама пропорция:

nPos  nPos64

nMax  nMax64

Отсюда формула для вычисления nPos64 (после получения информации от функции GetScrollInfo) будет такая:

nPos64 = (nPos*nMax64)\nMax

У нас есть все нужные нам аргументы, для вычисления nPos64.
Функция GetScrollInfo вернет нам nPos.
После загрузки файла , можно легко подсчитать nMax64.
Ну а nMax мы решили, что будет равна &h7FFFFFFF. 

Формула же для вычисления nPos (перед вызовом функции SetScrollInfo) будет такая:

nPos = (nPos64*nMax)\nMax64

После вызова GetScrollInfo , у нас опять же есть все аргументы.

С точки зрения математических действий, у нас вроде полный порядок. Однако есть одна заковырка. Когда значения nPos и nMax64 будут большими, при умножении их произведение будет вылезать за рамки 64х-битного числа, что вызовет переполнение. Как результат, нашу прокрутку вместо того, чтобы опускать вниз, начнет возращать вверх. А что нам мешает поменять порядок действий? Ведь x*y\z = x\z*y. Только единственно, нам нельзя будет использовать оператор целочисленного деления (\), поскольку при x меньше чем y , всегда будет возвращаться ноль. Просто заменим его на другой оператор деления (/). 

Но и это еще не все! Если взглянуть еще раз на пропорцию, то становится ясно, что потенциальный размер загружаемого файла можно еще увеличить. И сделать это легко , если уменьшить значение nMax. Ведь мы его определили по максимальному возможному числу, которое можно отправить в структуру SCROLLINFO. Что нам мешает сделать это число  меньше, например равное &h7fff (32767) [данное число было максимальным в ранних версиях windows для scrollbar]?
Да , точность попадания ползунком на нужную позицию адреса упадет, но в реальности, на больших файлах это не имеет вообще никакого значения, поскольку и при значении &h7FFFFFFF попасть ползунком невозможно. С большими файлами единственный метод попасть на нужную позицию это точный переход (в программах "Перейти по адресу:"). Здесь правильно найти золотую середину. Например при файлах с размерами до 100ГБ использовать константу &h7FFFFFFF , а при больших размерах использовать &h7FFF. И чтобы не ущемлять точность на небольших файлах, можно ввести условия работы прокрутки. В одном случае программа должна работать так , как реализовано в коде выше, а если кол-во прокручиваемых областей больше константы &h7FFFFFFF , то проводить прорциональные вычисления.

Ладно хватит теории :) .

Вот код, который получился на данный момент:

#INCLUDE "windows.bi"
#INCLUDE "win/commctrl.bi"
#INCLUDE "win/commdlg.bi"
InitCommonControls()

Declare Function HexWndProc(hwnd As HWND, msg As Uinteger,_
wparam As WPARAM, lparam As LPARAM) As Integer

' Переменные
Dim Shared hinst As HINSTANCE : hinst = GetModuleHandle(0)
Dim msg As MSG
Dim As WNDCLASSEX wc
Dim As ZString*20 sNameClass = "HexViewer"
Dim Shared As ZString*20 szHexClass: szHexClass = "szHexClass"
Dim Shared As HFONT hFontCourierNew
Dim Shared As HWND mainHwnd
Dim Shared As HWND childHwnd
Dim Shared ofn  As OPENFILENAME
Dim Shared filename        As Zstring * 512
Dim Shared As Byte TempDIM(10000)
Dim Shared As Ulongint ullSizeFile ' размер файла
ullSizeFile = 10000
Dim Shared As Ulongint ullNullOffsetView 'смещение в самом начале видимой части окна
Dim Shared As Ubyte Ptr pbBytesBuffer ' буфер , ограниченный в размере (416 байт),
'используется для копирования в него части информации, которая отображается в видимой части окна
pbBytesBuffer = Callocate(416) ' выделим память для буфера
Dim Shared As Ulongint ullVScrollPos,ullMaxCount ' позиция скрола, кол-во прокручиваемых областей

' загрузка шрифта
Function LoadFontProc(Byval Name_     As String, _
    Byval Size      As Integer, _
    Byval corner    As Integer,_
    Byval BOLD      As Integer,_
    Byval Italic    As Integer,_
    Byval Underline As Integer,_
    Byval StrikeOut As Integer,_
    Byval CharSet As Integer) As HFONT
    Dim As Integer size_
    If BOLD=1 Then
        BOLD=700 ' жирный шрифт
    Else
        BOLD=400 ' обычный
    Endif
    Dim As HDC hdc = CreateDC("DISPLAY",0,0,0) ' создаем контекст
    size_ = -MulDiv(Size, GetDeviceCaps(hdc, LOGPIXELSY), 72) ' высчитываем размер шрифта
    DeleteDC(hdc) ' удаляем контекст
    Return CreateFont(size_,0,corner*10,0,Bold,Italic,Underline,StrikeOut, _ 'создаем шрифт
    CharSet, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH Or FF_DONTCARE,Name_)
End Function

' диалог вызова файла
Function FileOpen(Byval Title As String,Byval curentdir As String, Byval Pattern As String, Byval templateName As String = "") As String
    Dim fbguiTemp As Integer
    filename = templateName

    With ofn
        .hwndOwner       = 0
        .lStructSize     = Sizeof(OPENFILENAME)
        .lpstrFilter     = Cast(LPCTSTR,Strptr(Pattern) )
        .lpstrFile       = Strptr(filename)
        .nFileOffset     = 0
        .nMaxFile        = Sizeof(filename)
        .lpstrFileTitle  = Cast(LPTSTR,Strptr(Title))
        .nMaxFileTitle   = Sizeof(Title)
        .lpstrInitialDir = Cast(LPCTSTR,Strptr(curentdir))
        .lpstrTitle      = Cast(LPCTSTR,Strptr(Title))
        .Flags           = OFN_EXPLORER Or OFN_FILEMUSTEXIST
    End With

    If( GetOpenFileName( @ofn ) = 0 ) Then
        Return ""
    Else
        Return filename
    End If
End Function

' создание HEX окна
Sub CreateChildProc(hwnd As hwnd)
    Dim As WNDCLASSEX wc
    With wc
        .cbSize=SizeOf(WNDCLASSEX)
        .lpfnWndProc=@HexWndProc
        .hInstance=Hinst
        .hCursor=LoadCursor(0,IDC_IBEAM)
        .hbrBackground=Cast(HBRUSH,COLOR_SCROLLBAR)
        .lpszClassName=StrPtr(szHexClass)
    End With

    If RegisterClassEx(@wc)=0 Then
        Print "Register error, press any key"
        Sleep
        End
    Endif

    childHwnd = CreateWindowEx( WS_EX_CLIENTEDGE,szHexClass,"",_
    WS_VSCROLL Or WS_CHILD Or WS_VISIBLE,0,25,794,552,hwnd,Cast(HMENU,1),Hinst,0)

    If ullSizeFile>416 Then ' если развер файла больше кол-ва символов области прокрутки
        ullMaxCount = (ullSizeFile-416)\16+1 ' высчитываем
    Else
        ullMaxCount = 0 ' иначе равно 0
    Endif

    SendMessage(childHwnd,WM_VSCROLL,SB_THUMBTRACK,0) ' установим прокрутку на нуль
End Sub

' оконная процедура главного окна
Function wndproc(hwnd As HWND, msg As Uinteger,_
    wparam As WPARAM, lparam As LPARAM) As Integer

    Select Case msg
        Case WM_CREATE

            CreateChildProc(hwnd) ' Создаем свое дочернее окно для вывода HEX значений

            CreateWindowEx(0,"Button","Открыть файл",_
            WS_VISIBLE Or WS_CHILD,10,1,100,20,hwnd,Cast(HMENU,2),Hinst,0) ' Создаем обычную кнопку

        Case WM_COMMAND
            If lparam <> 0 Then ' если сообщения от контролов
                Select Case Loword(wParam)
                    Case 2
                        ? FileOpen("Открыть","C:\","All files (*.*)"+Chr(0)+"*.*"+Chr(0) ,"")
                End Select
            Endif
        Case WM_DESTROY
            DeleteObject(hFontCourierNew)
            PostQuitMessage(0)' выходим
    End Select
    Return DefWindowProc(hwnd,msg,wparam,lparam)
End Function

'заполнение буфера pbBytesBuffer данными
Sub CopyBytesProc()
    Dim As Ulongint ullTempSize
    If (ullNullOffsetView + 416) < ullSizeFile Then
        ullTempSize = 416
    Else
        ullTempSize = ullSizeFile - ullNullOffsetView
    Endif
    CopyMemory( pbBytesBuffer ,@TempDIM(ullNullOffsetView) , ullTempSize)
End Sub

' рисование на окне childHwnd
Sub PaintHexProc()
    Dim As HDC hdc,hdc2 ' контексты устройства
    Dim As PAINTSTRUCT ps ' структура, содержащая инфу для рисования
    Dim As HBITMAP hbitmap ' битмап для рисования
    Dim As HBRUSH  brush ' хендл кисти
    Dim As HPEN pen ' хендл пера
    Dim As Integer offset ' смещение
    Dim As Integer y = 21 ' высота букв

    #MACRO mRectangle(col,x,w) ' макрос рисования прямоугольников
        brush=CreateSolidBrush(col) ' создаем кисть
        SelectObject(hdc,brush) ' кисть в контекст
        pen= CreatePen(PS_SOLID ,1,Cast(COLORREF,col)) ' создаем перо
        SelectObject(hDC,Cast(HGDIOBJ,pen)) ' перо в контекст
        Rectangle(hDC,x,0,w,552) ' рисуем прямоугольник
        DeleteObject(brush) ' удаляем кисть
        DeleteObject(pen) ' удаляем перо
    #EndMacro

    #MACRO mDrawText()
        SelectObject(hdc, hFontCourierNEW) ' свой шрифт в контекст
        TextOut(hdc,10,y*i+5,Hex(i*16+ullNullOffsetView,11), 11) ' рисуем строку - адрес
        TextOut(hdc,142,y*i+3,sHex, 47)' рисуем строку - HEX
        TextOut(hdc,611,y*i+3,sString, 16)' рисуем строку - ASCII
    #EndMacro

    hdc2 = BeginPaint(childHwnd, @ps) ' начинаем рисование, получаем контекст
    hdc   = CreateCompatibleDC(hdc2) 'создаем совместимый контекст
    hbitmap = CreateCompatibleBitmap(hdc2,794,552) ' создаем совместимый битмап
    SelectObject(hdc,hbitmap) ' битмап в контекст
    mRectangle(&hFFE9E9,0,109) ' рисуем прямоугольник для адресов
    mRectangle(&hf0f0f0,110,595) 'рисуем прямоугольник для Hex символов
    mRectangle(&hE9FFE9,596,794) ' рисуем прямоугольник для ASCII символов

    CopyBytesProc() ' копируем данные в буфер, из которого будем читать информацию и рисовать
    SetBkMode(hdc,1) ' режим прозрачности для текста

    For i As Integer = 0 To 25 ' цикл по числу видимых строк
        Dim As ZString*50 sHex ' хранит HEX символы
        Dim As ZString*16 sString ' хранит ASCII символы
        For x As Integer = 0 To 15 ' цикл по числу столбцов
            If ullNullOffsetView+offset = ullSizeFile Then ' если конец буфера
                If x>0 Then ' если прошла хоть одна итерация
                    mDrawText() ' вызываем макрос
                Endif
                Exit For, For ' выход из обоих циклов
            Endif
            sHex &= (Hex(pbBytesBuffer[offset],2)& " ") ' собираем строку HEX
            Dim As Ubyte btempSimbol = pbBytesBuffer[offset]' получаем ASCCI код
            Select Case btempSimbol ' отсеиваем непечатываемые символы
                Case &h0 To &h1f,&hAD,&h98,&h7f
                    btempSimbol = &h2E ' заменяем их точками
            End Select
            sString[x] = btempSimbol ' собираем строку ASCII
            offset +=1 ' увеличиваем смещение
        Next
        mDrawText()' вызываем макрос
    Next

    BitBlt(hdc2,0,0,794,552,hdc,0,0,SRCCOPY) ' копируем содержимое контекста в другой контекст
    DeleteObject(hbitmap) ' удаляем битмап
    DeleteDC(hDC) ' удаляем совместимый контекст
    EndPaint(childHwnd, @ps) ' заканчиваем рисование
End Sub

' получение информации о скроллинге
Sub GetScrollinfoProc(hwnd As hwnd)
    Dim info As SCROLLINFO
    info.cbSize = Sizeof(SCROLLINFO)
    info.fMask = SIF_TRACKPOS
    GetScrollInfo(hwnd, SB_VERT, @info) ' получаем инфу о скроллинге от системы

    If ullMaxCount<&h7fffffffll Then ' если максимальная прокрутка меньше 7fffffff (размер файла ~32 ГБ)
        ullVScrollPos = info.nTrackPos ' тогда просто присваиваем системную инфу
    Else
        ' высчитываем процентное соотношение позиции скроллинга для файлов с размером больше 32ГБ
        ullVScrollPos = info.nTrackPos
        ullVScrollPos = (ullVScrollPos/&h7fffffffull)*ullMaxCount
    Endif
End Sub

' обработка информации о скроллинге
Sub ScrollProc(hwnd As HWND, param As Integer)

    Dim As Integer vs
    Select Case param
        Case SB_TOP
            ullVScrollPos = 0 ' на начало страницы
        Case SB_BOTTOM
            ullVScrollPos = ullMaxCount 'на конец страницы
        Case SB_LINEUP
            If(ullVScrollPos > 0) Then
                ullVScrollPos-=1 'сдвигаемся ближе к началу страницы на 1
            Endif
        Case SB_LINEDOWN
            If(ullVScrollPos < ullMaxCount) Then
                ullVScrollPos+=1 'сдвигаемся ближе к концу страницы на 1
            Endif
        Case SB_PAGEUP
            If(ullVScrollPos > 10) Then
                ullVScrollPos-=10 'сдвигаемся ближе к началу страницы на 10
            Endif
        Case SB_PAGEDOWN
            If(ullVScrollPos < (ullMaxCount-10)) Then
                ullVScrollPos+=10 'сдвигаемся ближе к концу страницы на 10
            Endif
        Case SB_THUMBPOSITION,SB_THUMBTRACK ' если пользовательское перемещение на неопределенное кол-во
            GetScrollinfoProc(hwnd) ' получаем информацию из системы и высчитываем
    End Select

    Dim As SCROLLINFO sInfo
    sInfo.cbSize = Sizeof(SCROLLINFO)
    sInfo.fMask = SIF_ALL ' маска для любых действий со скролом
    sInfo.nMin  = 0
    sInfo.nMax  = Iif (ullMaxCount>&h7fffffffull,&h7fffffff,ullMaxCount) ' ставим максимальное значение скроллинга
    vs = Iif(ullMaxCount>&h7fffffffull,(ullVScrollPos/ullMaxCount)*&h7fffffffull,ullVScrollPos) ' высчитываем реальное смещение в скроле
    sInfo.nPos  = vs
    sInfo.nPage = 1
    SetScrollInfo (hwnd, SB_VERT,@sInfo, TRUE)
    InvalidateRect(hwnd,0,false)
    ullNullOffsetView = ullVScrollPos*16 ' получаем начальное смещение в видимой части окна
End Sub

' оконная процедура своего окна, на котором будем рисовать HEX данные
Function HexWndProc(hwnd As HWND, msg As Uinteger,_
    wparam As WPARAM, lparam As LPARAM) As Integer

    Select Case msg
        Case WM_PAINT ' если требуется рисование
            PaintHexProc()
            Return 0
        Case WM_VSCROLL ' если произошло событие скроллинга
            ScrollProc(hwnd,Loword(wparam)) ' обрабатываем
    End Select
    Return DefWindowProc(hwnd,msg,wparam,lparam)

End Function

' настройки главного окна
With wc
    .cbSize=SizeOf(WNDCLASSEX)
    .style=CS_HREDRAW Or CS_VREDRAW
    .lpfnWndProc=@wndproc
    .hInstance=Hinst
    .hIcon=LoadIcon(0,IDI_QUESTION)
    .hCursor=LoadCursor(0,IDC_ARROW)
    .hbrBackground=Cast(HBRUSH,COLOR_WINDOW)
    .lpszClassName=StrPtr(sNameClass)
    .hIconSm=.hIcon
End With

' регистрация окна
If RegisterClassEx(@wc)=0 Then
    Print "Register error, press any key"
    Sleep
    End
Endif

' Загрузка шрифтов
hFontCourierNew = LoadFontProc("Courier new",11,0,0,0,0,0,DEFAULT_CHARSET)

' создаем главное окно
mainHwnd = CreateWindowEx(0,sNameClass,"HexViewer",_
WS_VISIBLE Or WS_OVERLAPPED Or WS_CAPTION Or WS_SYSMENU Or WS_MINIMIZEBOX,100,100,800,610,0,0,Hinst,0)

For i As Integer = 0 To 10000
    TempDIM(i) = Rnd*256
Next

' главный цикл
While GetMessage(@msg,0,0,0)
    TranslateMessage(@msg)
    DispatchMessage(@msg)
Wend

В следующей статье, думаю уже разобрать загрузку файлов.

содержание | назад | вперед