Rozdział XIII - Pliki odwzorowane w pamięci


Na tej lekcji pokażę, czym są pliki odwzorowane w pamięci (memory mapped files) oraz jak z nich korzystać dla własnych korzyści. Zobaczysz w dalszej części, iż ich wykorzystanie jest zupełnie proste.

 

 

Pobierz plik z przykładem {z tego archiwum}.

 

Teoria

Jeśli dokładnie przestudiujesz przykład podany w poprzednim rozdziale, to zauważysz, iż posiada on poważne ograniczenie: co będzie, jeśli odczytywany plik jest większy niż przydzielony blok pamięci? Albo co się stanie, jeśli wyszukiwany przez ciebie fragment tekstu zostanie obcięty na końcu bloku pamięci? Tradycyjna odpowiedź na pierwsze pytanie brzmi: powinieneś w kółko odczytywać dane z pliku, aż napotkasz jego koniec. Odpowiedź na drugie pytanie brzmi: powinieneś być przygotowany na specjalny przypadek na końcu bloku pamięci. Fachowo nazywa się to problemem wartości granicznej (boundary value problem). Powoduje on ból głowy u programistów i jest przyczyną niezliczonych błędów.

Byłoby miło móc przydzielić sobie bardzo duży blok pamięci, wystarczający do pomieszczenia całego pliku, lecz wtedy nasz program pochłonąłby olbrzymią ilość zasobów komputera. Na ratunek przychodzi odwzorowanie plików (file mapping). Używając odwzorowania plików możesz potraktować cały plik tak, jakby już znajdował się w pamięci i skorzystać ze wskazania pamięci do odczytu i zapisu danych w pliku. To jest właśnie tak proste. Nie ma już potrzeby stosowania funkcji API obsługujących pamięć i oddzielnych funkcji plikowego wejścia/wyjścia, przy odwzorowaniu pliku są one jednym i tym samym. Odwzorowanie plików jest również używane przy współdzieleniu danych z innymi procesami. Użycie tej technologii w ten sposób oznacza, iż w rzeczywistości nie jest tutaj zaangażowany żaden plik. To bardziej jak zarezerwowany blok pamięci, który jest widoczny dla każdego procesu. Jednakże dzielenie danych pomiędzy procesami jest delikatną sprawą, której nie wolno traktować lekko. Należy zaimplementować synchronizacje procesu i wątku, w przeciwnym razie twoja aplikacja bardzo szybko zakończy swoje działanie.

W tym kursie nie poruszymy tematu odwzorowania pliku jako środka tworzenia wspólnego obszaru pamięci. Skoncentrujemy się na sposobie wykorzystania odwzorowania pliku do umieszczenia go w pamięci. W rzeczy samej program ładujący wykorzystuje odwzorowanie pliku do załadowania plików wykonywalnych do pamięci. Jest to bardzo wygodne, ponieważ tylko niezbędne fragmenty będą odczytywane z pliku na dysku. W systemie Win32 powinieneś korzystać z tej techniki tak często, jak to możliwe.

Jednakże istnieją pewne ograniczenia przy odwzorowywaniu plików. Gdy zostanie już utworzony plik odwzorowany w pamięci, jego rozmiar nie może być zmieniony w danej sesji. Zatem odwzorowanie plików jest wspaniałe dla plików tylko do odczytu lub operacji plikowych nie zmieniających rozmiaru pliku. Nie oznacza to, iż nie możesz stosować odwzorowania pliku, gdy chcesz zwiększyć jego rozmiar. Możesz oszacować nowy rozmiar i utworzyć plik odwzorowany w pamięci na podstawie tego nowego rozmiaru, a rozmiar pliku wzrośnie. Jest to tylko pewna niewygoda, to wszystko.

Dosyć wyjaśnień. Zanurzmy się w implementację odwzorowania pliku. Oto niezbędne kroki:

  1. Wywołaj CreateFile w celu otwarcia pliku do odwzorowania.
  2. Wywołaj CreateFileMapping z uchwytem zwróconym przez CreateFile jako jeden z parametrów. Funkcja ta tworzy obiekt odwzorowania pliku na podstawie pliku otwartego przez CreateFile.
  3. Wywołaj MapViewOfFile, aby odwzorować w pamięci wybrany fragment pliku lub cały plik to m. Funkcja ta zwraca wskazanie pierwszego bajtu odwzorowanego obszaru pamięci.
  4. Wykorzystaj to wskazanie do odczytu lub do zapisu pliku.
  5. Wywołaj UnmapViewOfFile, aby zakończyć odwzorowywanie pliku.
  6. Wywołaj CloseHandle z uchwytem odwzorowanego pliku jako jednym z parametrów w celu zamknięcia go.
  7. Wywołaj ponownie CloseHandle tym razem z uchwytem pliku zwróconym przez CreateFile, aby zamknąć właściwy plik.

Przykład

Program wylistowany poniżej pozwala ci otworzyć plik poprzez okno dialogowe otwierania pliku. Otwiera on wybrany plik wykorzystując odwzorowanie pliku i jeśli mu się to powiedzie, to napis na pasku tytułowym zmieni się na nazwę otwartego pliku. Możesz zapisać ten plik pod inną nazwą wybierając z menu opcję Plik/Zapisz Jako. Program skopiuje całą zawartość otwartego pliku do nowego pliku. Zwróć uwagę, iż w tym programie nie musisz wywoływać funkcji GlobalAlloc do przydzielenia bloku pamięci.

Plik FILEMAP.ASM

 

.386

.MODEL FLAT, STDCALL

OPTION CASEMAP:NONE

WinMain PROTO :DWORD, :DWORD, :DWORD, :DWORD

INCLUDE    \masm32\include\windows.inc
INCLUDE    \masm32\include\user32.inc
INCLUDE    \masm32\include\kernel32.inc
INCLUDE    \masm32\include\comdlg32.inc
INCLUDELIB \masm32\lib\user32.lib
INCLUDELIB \masm32\lib\kernel32.lib
INCLUDELIB \masm32\lib\comdlg32.lib

.CONST

IDM_OPEN EQU   1
IDM_SAVE EQU   2
IDM_EXIT EQU   3
MAXSIZE  EQU 260

.DATA

ClassName    DB "Win32ASMFileMappingClass", 0
AppName      DB "Pliki odwzorowane w pamięci", 0
MenuName     DB "FirstMenu", 0
ofn          OPENFILENAME <>
FilterString DB "Wszystkie pliki (*.*)", 0, "*.*", 0
             DB "Pliki tekstowe (*.txt)", 0, "*.txt", 0, 0
buffer       DB MAXSIZE DUP(0)
hMapFile     HANDLE 0

.DATA?

hInstance   HINSTANCE ?
CommandLine LPSTR     ?
hFileRead   HANDLE    ?
hFileWrite  HANDLE    ?
hMenu       HANDLE    ?
pMemory     DWORD     ?
SizeWritten DWORD     ?

.CODE

start:
    INVOKE GetModuleHandle, NULL
    mov    hInstance, eax
    INVOKE GetCommandLine
    mov    CommandLine, eax
    INVOKE WinMain, hInstance, NULL, CommandLine, SW_SHOWDEFAULT
    INVOKE ExitProcess, eax

WinMain PROC hInst:     HINSTANCE,\
             hPrevInst: HINSTANCE,\
             CmdLine:   LPSTR,\
             CmdShow:   DWORD

LOCAL wc:   WNDCLASSEX
LOCAL msg:  MSG
LOCAL hwnd: HWND

    mov    wc.cbSize, SIZEOF WNDCLASSEX
    mov    wc.style, CS_HREDRAW OR CS_VREDRAW
    mov    wc.lpfnWndProc, OFFSET WndProc
    mov    wc.cbClsExtra, NULL
    mov    wc.cbWndExtra, NULL
    push   hInst
    pop    wc.hInstance
    mov    wc.hbrBackground, COLOR_WINDOW+1
    mov    wc.lpszMenuName, OFFSET MenuName
    mov    wc.lpszClassName, OFFSET ClassName
    INVOKE LoadIcon, NULL, IDI_APPLICATION
    mov    wc.hIcon, eax
    mov    wc.hIconSm, eax
    INVOKE LoadCursor, NULL, IDC_ARROW
    mov    wc.hCursor, eax
    INVOKE RegisterClassEx, ADDR wc
    INVOKE CreateWindowEx, WS_EX_CLIENTEDGE,\
                           ADDR ClassName, ADDR AppName,\
                           WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,\
                           CW_USEDEFAULT, 300, 200, NULL, NULL,\
                           hInst, NULL
    mov    hwnd, eax
    INVOKE ShowWindow, hwnd, SW_SHOWNORMAL
    INVOKE UpdateWindow, hwnd

    .WHILE TRUE
        INVOKE GetMessage, ADDR msg, NULL, 0, 0
        .BREAK .IF (!eax)
        INVOKE TranslateMessage, ADDR msg
        INVOKE DispatchMessage, ADDR msg
    .ENDW

    mov eax, msg.wParam
    ret
    
WinMain ENDP

WndProc PROC hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

    .IF uMsg==WM_CREATE
        INVOKE GetMenu, hWnd
        mov    hMenu, eax
        mov    ofn.lStructSize, SIZEOF ofn
        push   hWnd
        pop    ofn.hWndOwner
        push   hInstance
        pop    ofn.hInstance
        mov    ofn.lpstrFilter, OFFSET FilterString
        mov    ofn.lpstrFile, OFFSET buffer
        mov    ofn.nMaxFile, MAXSIZE
    .ELSEIF uMsg==WM_DESTROY
        .IF hMapFile!=0
            call CloseMapFile
        .ENDIF
        INVOKE PostQuitMessage, NULL
    .ELSEIF uMsg==WM_COMMAND
        mov eax, wParam
        .IF lParam==0
            .IF ax==IDM_OPEN
                mov  ofn.Flags, OFN_FILEMUSTEXIST OR \
                                OFN_PATHMUSTEXIST OR \
                                OFN_LONGNAMES OR \
                                OFN_EXPLORER OR \
                                OFN_HIDEREADONLY
                INVOKE GetOpenFileName, ADDR ofn
                .IF eax==TRUE
                    INVOKE CreateFile, ADDR buffer,\
                                       GENERIC_READ,\
                                       0, NULL,\
                                       OPEN_EXISTING,\
                                       FILE_ATTRIBUTE_ARCHIVE,\
                                       NULL
                    mov    hFileRead, eax
                    INVOKE CreateFileMapping, hFileRead,\
                                              NULL, PAGE_READONLY,\
                                              0, 0, NULL
                    mov    hMapFile, eax
                    mov    eax, OFFSET buffer
                    movzx  edx, ofn.nFileOffset
                    add    eax, edx
                    INVOKE SetWindowText, hWnd, eax
                    INVOKE EnableMenuItem, hMenu, IDM_OPEN, MF_GRAYED
                    INVOKE EnableMenuItem, hMenu, IDM_SAVE, MF_ENABLED
                .ENDIF
            .ELSEIF ax==IDM_SAVE
                mov    ofn.Flags, OFN_LONGNAMES OR \
                                  OFN_EXPLORER OR OFN_HIDEREADONLY
                INVOKE GetSaveFileName, ADDR ofn
                .IF eax==TRUE
                    INVOKE CreateFile, ADDR buffer,\
                                       GENERIC_READ OR GENERIC_WRITE,\
                                       FILE_SHARE_READ OR FILE_SHARE_WRITE,\
                                       NULL, CREATE_NEW,\
                                       FILE_ATTRIBUTE_ARCHIVE, NULL
                    mov    hFileWrite, eax
                    INVOKE MapViewOfFile, hMapFile, FILE_MAP_READ, 0, 0, 0
                    mov    pMemory, eax
                    INVOKE GetFileSize, hFileRead, NULL
                    INVOKE WriteFile, hFileWrite,\
                                      pMemory,\
                                      eax,\
                                      ADDR SizeWritten,\
                                      NULL
                    INVOKE UnmapViewOfFile, pMemory
                    call   CloseMapFile
                    INVOKE CloseHandle, hFileWrite
                    INVOKE SetWindowText, hWnd, ADDR AppName
                    INVOKE EnableMenuItem, hMenu, IDM_OPEN, MF_ENABLED
                    INVOKE EnableMenuItem, hMenu, IDM_SAVE, MF_GRAYED
                .ENDIF
            .ELSE
                INVOKE DestroyWindow, hWnd
            .ENDIF
        .ENDIF
    .ELSE
        INVOKE DefWindowProc, hWnd, uMsg, wParam, lParam
        ret
    .ENDIF

    xor eax, eax
    ret

WndProc ENDP

CloseMapFile PROC

    INVOKE CloseHandle, hMapFile
    mov    hMapFile, 0
    INVOKE CloseHandle, hFileRead
    ret

CloseMapFile ENDP

END start


Plik FILEMAP.RC

// Stałe dla menu

#define IDM_OPEN 1
#define IDM_SAVE 2
#define IDM_EXIT 3

FirstMenu MENU
{
    POPUP "&Plik"
    {
         MENUITEM "&Otwórz...", IDM_OPEN
         MENUITEM "&Zapisz Jako...", IDM_SAVE, GRAYED
         MENUITEM SEPARATOR
         MENUITEM "&Koniec", IDM_EXIT
    }
}

 

Analiza

    INVOKE CreateFile, ADDR buffer,\
                       GENERIC_READ,\
                       0, NULL,\
                       OPEN_EXISTING,\
                       FILE_ATTRIBUTE_ARCHIVE,\
                       NULL


Gdy użytkownik wybierze plik w oknie dialogowym otwierania pliku, wywołujemy CreateFile, aby go otworzyć. Zwróć uwagę, iż przy otwieraniu pliku określamy znacznik GENERIC_READ dla dostępu tylko do odczytu oraz zerujemy parametr dwShareMode, ponieważ nie życzymy sobie, aby inny proces modyfikował nam dane podczas tej operacji.


    INVOKE CreateFileMapping, hFileRead,\
                              NULL, PAGE_READONLY,\
                              0, 0, NULL


Następnie wywołujemy CreateFileMapping w celu utworzeniu odwzorowanego w pamięci pliku z pliku otwartego. CreateFileMapping posiada następującą składnię:


CreateFileMapping PROTO hFile:                   DWORD,\ 
                        lpFileMappingAttributes: DWORD,\ 
                        flProtect:               DWORD,\ 
                        dwMaximumSizeHigh:       DWORD,\ 
                        dwMaximumSizeLow:        DWORD,\ 
                        lpName:                  DWORD 


Powinieneś najpierw wiedzieć, iż funkcja CreateFileMapping nie musi odwzorowywać całego pliku w pamięci. Możesz wykorzystać tę funkcję od odwzorowania w pamięci jedynie fragmentu pliku. Rozmiar pliku odwzorowanego w pamięci określasz przy pomocy parametrów dwMaximumSizeHigh oraz dwMaximumSizeLow. Jeśli określisz rozmiar większy od rzeczywistego pliku, to plik ten zostanie powiększony do nowego rozmiaru. Jeśli nie chcesz zmieniać rozmiaru pliku, to ustaw oba wspomniane parametry na zero.

Możesz podać wartość NULL w parametrze lpFileMappingAttributes, aby pozwolić systemowi Windows utworzyć standardowe atrybuty zabezpieczeń dla pliku odwzorowanego w pamięci.

flProtect określa wymaganą ochronę dla pliku odwzorowanego w pamięci. W naszym przykładzie stosujemy znacznik PAGE_READONLY, który zezwala jedynie na odczyt pliku odwzorowanego w pamięci. Zwróć uwagę, aby atrybut ten nie był w sprzeczności z atrybutem użytym w wywołaniu funkcji CreateFile, w przeciwnym razie funkcja CreateFileMapping zawiedzie.

lpName wskazuje nazwę pliku odwzorowanego w pamięci. Jeśli chcesz dzielić się tym plikiem z innymi procesami, to musisz nadać mu nazwę. Jednakże w naszym przykładzie nasz proces jest jedynym, który używa tego pliku, więc parametr ignorujemy.


    mov    eax, OFFSET buffer
    movzx  edx, ofn.nFileOffset
    add    eax, edx
    INVOKE SetWindowText, hWnd, eax


Jeśli wywołanie funkcji CreateFileMapping powiedzie się, zmieniamy tytuł okna na nazwę otwartego pliku. Nazwa pliku wraz z pełną ścieżką do niego umieszczona jest w buforze, lecz my chcemy wyświetlić na pasku tytułowym okna samą nazwę pliku, zatem musimy dodać wartość pola nFileOffset struktury OPENFILENAME do adresu bufora.


    INVOKE EnableMenuItem, hMenu, IDM_OPEN, MF_GRAYED
    INVOKE EnableMenuItem, hMenu, IDM_SAVE, MF_ENABLED


Dla zabezpieczenia nie chcemy, aby użytkownik otwierał kilka plików na raz, zatem blokujemy element menu Otwórz, a uaktywniamy element menu Zapisz. Do zmiany atrybutu elementu menu wykorzystujemy funkcję EnableMenuItem.

Po tej czynności czekamy, aż użytkownik wybierze element menu Plik/Zapisz lub zamknie nasz program. Jeśli użytkownik zdecyduje się zamknąć program, musimy zamknąć plik odwzorowany w pamięci oraz plik na dysku, co wykona poniższy kod:


    .ELSEIF uMsg==WM_DESTROY
        .IF hMapFile!=0
            call CloseMapFile
        .ENDIF
        INVOKE PostQuitMessage, NULL


W powyższym fragmencie programu gdy procedura okna otrzyma wiadomość WM_DESTROY, sprawdza ona wartość zmiennej hMapFile (zawierającej uchwyt do pliku odwzorowanego w pamięci) i jeśli nie zawiera ona zera, wywołuje funkcję CloseMapFile zawierającą następujący kod:


CloseMapFile PROC

    INVOKE CloseHandle, hMapFile
    mov    hMapFile, 0
    INVOKE CloseHandle, hFileRead
    ret

CloseMapFile ENDP


CloseMapFile
zamyka plik odwzorowany w pamięci i plik na dysku, zatem nie będzie wycieku zasobów, gdy nasz program wyjdzie do systemu Windows.

Jeśli użytkownik wybierze opcję zapisu danych do innego pliku, program zaprezentuje mu okienko dialogowe zapisu pod inną nazwą. Gdy wpisze on nazwę nowego pliku, plik ten zostanie utworzony przez funkcję CreateFile.


    INVOKE MapViewOfFile, hMapFile, FILE_MAP_READ, 0, 0, 0
    mov    pMemory, eax


Bezpośrednio po utworzeniu pliku wyjściowego wywołujemy MapViewOfFile, aby odwzorować pożądaną część pliku w pamięci. Funkcja ta posiada następującą składnię:


MapViewOfFile PROTO hFileMappingObject:   DWORD,\ 
                    dwDesiredAccess:      DWORD,\ 
                    dwFileOffsetHigh:     DWORD,\ 
                    dwFileOffsetLow:      DWORD,\ 
                    dwNumberOfBytesToMap: DWORD

Po wywołaniu MapViewOfFile żądana część pliku zostaje załadowana do pamięci. Otrzymasz wskazanie bloku pamięci zawierającego dane z pliku.


    INVOKE GetFileSize, hFileRead, NULL


Dowiadujemy się ile wynosi długość pliku. Rozmiar pliku zwrócony zostanie w rejestrze eax. Jeśli plik jest większy od 4GB, starsze podwójne słowo rozmiaru pliku jest umieszczone w FileSizeHighWord. Ponieważ nie będziemy obsługiwać tak dużych plików, zatem możemy je zignorować.


    INVOKE WriteFile, hFileWrite,\
                      pMemory,\
                      eax,\
                      ADDR SizeWritten,\
                      NULL


Zapisujemy dane odwzorowane w pamięci do pliku wyjściowego.


    INVOKE UnmapViewOfFile, pMemory


Gdy skończymy z plikiem wejściowym, usuwamy z pamięci jego odwzorowanie.


    call   CloseMapFile
    INVOKE CloseHandle, hFileWrite


I zamykamy wszystkie pliki.


    INVOKE SetWindowText, hWnd, ADDR AppName


Odtwarzamy pierwotny tytuł okna.


    INVOKE EnableMenuItem, hMenu, IDM_OPEN, MF_ENABLED
    INVOKE EnableMenuItem, hMenu, IDM_SAVE, MF_GRAYED


Uaktywniamy element menu Otwórz, a blokujemy element Zapisz Jako.

Dodatek w Pascalu

Ta sama aplikacja w Pascalu:

 

{********************************
**  I Liceum Ogólnokształcące  **
**           w Tarnowie        **
**       mgr Jerzy Wałaszek    **
********************************}

program FileMap;

uses Windows;

const
  IDM_OPEN     =   1;
  IDM_SAVE     =   2;
  IDM_EXIT     =   3;
  MAXSIZE      = 260;
  ClassName    = 'Win32ASMFileMappingClass';
  AppName      = 'Pliki odwzorowane w pamięci';
  MenuName     = 'FirstMenu';
  FilterString = 'Wszystkie pliki (*.*)'#0'*.*'#0'Pliki tekstowe (*.txt)'#0'*.txt'#0#0;
var
  ofn         : OPENFILENAME;
  buffer      : array[0..MAXSIZE] of byte;
  hMapFile    : HANDLE;
  hInstance   : HINST;
  hFileRead   : HANDLE;
  hFileWrite  : HANDLE;
  hMenu       : HANDLE;
  pMemory     : pointer;
  SizeWritten : longint;

procedure CloseMapFile;
begin
  CloseHandle(hMapFile);
  hMapFile := 0;
  CloseHandle(hFileRead);
end;

function WndProc(hWnd:HWND;uMsg:UINT;wParam:WPARAM;lParam:LPARAM) : longint;
begin
  WndProc := 0;
  case uMsg of
    WM_CREATE :
      begin
        hMenu           := GetMenu(hWnd);
        ofn.lStructSize := sizeof(ofn);
        ofn.hWndOwner   := hWnd;
        ofn.hInstance   := hInstance;
        ofn.lpstrFilter := FilterString;
        ofn.lpstrFile   := @buffer;
        ofn.nMaxFile    := MAXSIZE;
      end;
    WM_DESTROY :
      begin
        if hMapFile = 0 then CloseMapFile;
        PostQuitMessage(0);
      end;
    WM_COMMAND:
      begin
        if lParam = 0 then
          case (wParam and $ffff) of
            IDM_OPEN :
              begin
                ofn.Flags := OFN_FILEMUSTEXIST or OFN_PATHMUSTEXIST or
                             OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY;
                if GetOpenFileName(@ofn) then
                begin
                  hFileRead := CreateFile(@buffer,GENERIC_READ,0,0,
                                          OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,0);
                  hMapFile  := CreateFileMapping(hFileRead,0,PAGE_READONLY,0,0,0);
                  SetWindowText(hWnd,@buffer[ofn.nFileOffset]);
                  EnableMenuItem(hMenu,IDM_OPEN,MF_GRAYED);
                  EnableMenuItem(hMenu,IDM_SAVE,MF_ENABLED);
                end;
              end;
            IDM_SAVE :
              begin
                ofn.Flags := OFN_LONGNAMES or OFN_EXPLORER or OFN_HIDEREADONLY;
                if GetSaveFileName(@ofn) then
                begin
                  hFileWrite := CreateFile(@buffer,GENERIC_READ or GENERIC_WRITE,
                                           FILE_SHARE_READ or FILE_SHARE_WRITE,
                                           0,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE,0);
                    pMemory := MapViewOfFile(hMapFile,FILE_MAP_READ,0,0,0);
                    WriteFile(hFileWrite,pMemory^,GetFileSize(hFileRead,0),
                              SizeWritten,0);
                    UnmapViewOfFile(pMemory);
                    CloseMapFile;
                    CloseHandle(hFileWrite);
                    SetWindowText(hWnd,AppName);
                    EnableMenuItem(hMenu,IDM_OPEN,MF_ENABLED);
                    EnableMenuItem(hMenu,IDM_SAVE,MF_GRAYED);
                end;
              end;
            else DestroyWindow(hWnd);
          end;
        end;
    else WndProc := DefWindowProc(hWnd,uMsg,wParam,lParam);
  end;
end;

function WinMain(hInst,hPrevInst:HINST;CmdLine:LPSTR;CmdShow:DWORD) : longint;
var
  wc   : WNDCLASSEX;
  msg  : MSG;
  hwnd : HWND;
begin
  wc.cbSize        := sizeof(WNDCLASSEX);
  wc.style         := CS_HREDRAW or CS_VREDRAW;
  wc.lpfnWndProc   := @WndProc;
  wc.cbClsExtra    := 0;
  wc.cbWndExtra    := 0;
  wc.hInstance     := hInst;
  wc.hbrBackground := COLOR_WINDOW + 1;
  wc.lpszMenuName  := MenuName;
  wc.lpszClassName := ClassName;
  wc.hIcon         := LoadIcon(0,IDI_APPLICATION);
  wc.hIconSm       := wc.hIcon;
  wc.hCursor       := LoadCursor(0,IDC_ARROW);
  RegisterClassEx(wc);
  hwnd := CreateWindowEx(WS_EX_CLIENTEDGE,ClassName,AppName,WS_OVERLAPPEDWINDOW,
                         CW_USEDEFAULT,CW_USEDEFAULT,300,200,0,0,hInst,0);
  ShowWindow(hwnd,SW_SHOWNORMAL);
  UpdateWindow(hwnd);
  while GetMessage(msg,0,0,0) do
  begin
   
    TranslateMessage(msg);
    DispatchMessage(msg);
  end;
  WinMain := msg.wParam;
end;

begin
  hInstance := GetModuleHandle(0);
  ExitProcess(WinMain(hInstance,0,GetCommandLine,SW_SHOWDEFAULT));
end.

 

Autorem kursu jest Iczelion. Kurs programowania Windows znalazł się na serwerze I LO w Tarnowie za pisemną zgodą autora.
Tłumaczenie z języka angielskiego, opracowanie HTML i konwersję przykładów programów wykonał mgr Jerzy Wałaszek.



List do administratora Serwisu Edukacyjnego Nauczycieli I LO

Twój email: (jeśli chcesz otrzymać odpowiedź)
Temat:
Uwaga: ← tutaj wpisz wyraz  ilo , inaczej list zostanie zignorowany

Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).

Liczba znaków do wykorzystania: 2048

 

W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień szeroko opisywanych w podręcznikach.



   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2017 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.