Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС
выпущен 4 ноября!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь

ПРОГРАММИРОВАНИЕ НА C и С++



Программисты долго мучаются с кодом прогаммы, изучают С++, WinAPI функции, MSDN. Потом пишут банальную систему защиты или навешивают банальный протектор, а крэкеры и реверсеры справляются с такой защитой за 5 минут. В итоге, продажи программы почти нулевые. Чтобы такого не допустить, тут самому надо немного поднабрать опыта отладки, реверсинга, тот же отладчик Ollydbg изучить или дизассемблер IDA Pro. Но где искать по крохам эти знания? Нет, конечно можно годами "методом тыка" разбираться, но куда быстрее видеокурс специальный посмотреть. Вот тут он есть: ссылка. Автор курса с большим опытом и объясняет понятно, я из этого курса много узнал про то как работает компьютер, процессор, про инструменты специальные и как с ними работать. Мои коллеги программисты на работе ничего такого и не знают, теперь я им нос утру.

Изучаем Direct X вместе на примерах  SDK 7.0

Создадим проект, который показывает первичный и вторичный буфера.

 

Перед началом работы возьмите DirectX SDK 7.0 (http://www.microsoft.com/, 128 891 648 байт или прямиком сюда), скиньте из директории Lib файлы в Visual C++, тоже в директорию Lib, и  перепишите Include в директорию Include. Откройте проект имеющий расширение *.mak, а не *.dsw. Или же, если у Вас нет терпения качать, а Шаг первый хотите сделать, возьмите отсюда: include.rar и lib.rar.

 

Затем загрузите этот проект: project.rar.

 

Откройте проект при помощи меню File->Open Workspace, и выберите ddex1.dsw. Проект загружен. Можете скомпилировать и посмотреть для начала, что происходит. Лично я применял Visual C++ 6.0 взятый из Visual Studio 6.0 (но может и на 5.0 компилироваться, если сделать выше указанные действия).

 

Итак, рассмотрим нижеследующий код:

 

#include <windows.h>

#include <ddraw.h>

#include <stdio.h>

#include <stdarg.h>

#include "resource.h"

 

int PASCAL

WinMain(HINSTANCE hInstance,

        HINSTANCE hPrevInstance,

        LPSTR lpCmdLine,

        int nCmdShow)

{

    MSG                         msg;

 

    InitApp(hInstance, nCmdShow);

 

    while (GetMessage(&msg, NULL, 0, 0))

    {

        TranslateMessage(&msg);

        DispatchMessage(&msg);

    }

    return msg.wParam;

}

 

РАЗЪЯСНЕНИЕ:

InitApp(hInstance, nCmdShow) создает класс окна, в котором инициализируется DirectDraw, ниже мы его создадим.

Цикл while – обработка сообщений с клавиатуры (постоянна, на протяжении всей программы, пока она не завершится).

 

Теперь рассмотрим InitApp более подробно:

 

static HRESULT

InitApp(HINSTANCE hInstance, int nCmdShow)

{

    HWND                        hWnd;

    WNDCLASS                    wc;

    DDSURFACEDESC2              ddsd;

    DDSCAPS2                    ddscaps;

 

    // Устанавливаем параметры окна

    wc.style = CS_HREDRAW | CS_VREDRAW;

    wc.lpfnWndProc = WindowProc;

    wc.cbClsExtra = 0;

    wc.cbWndExtra = 0;

    wc.hInstance = hInstance;

    wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON));

    wc.hCursor = LoadCursor(NULL, IDC_ARROW);

    wc.hbrBackground = (HBRUSH )GetStockObject(BLACK_BRUSH);

    wc.lpszMenuName = NAME;

    wc.lpszClassName = NAME;

    RegisterClass(&wc);

 

    // Создаем окно

    hWnd = CreateWindowEx(WS_EX_TOPMOST,

                          NAME,

                          TITLE,

                          WS_POPUP,

                          0,

                          0,

                          GetSystemMetrics(SM_CXSCREEN),

                          GetSystemMetrics(SM_CYSCREEN),

                          NULL,

                          NULL,

                          hInstance,

                          NULL);

    ShowWindow(hWnd, nCmdShow);

    UpdateWindow(hWnd);

 

    ///////////////////////////////////////////////////////////////////////////

    // Создаем главный DirectDraw объект

    ///////////////////////////////////////////////////////////////////////////

    DirectDrawCreateEx(NULL, (VOID**)&g_pDD, IID_IDirectDraw7, NULL);

 

    // Отображение окна: полноэкранный

    g_pDD->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);

 

    // Устанавливаем графический режим 640x480x8

    g_pDD->SetDisplayMode(640, 480, 8, 0, 0);

 

    // Создаем первичный буфер, у которого есть вторичный

    ZeroMemory(&ddsd, sizeof(ddsd));

    ddsd.dwSize = sizeof(ddsd);

    ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;

    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP |

                          DDSCAPS_COMPLEX;

    ddsd.dwBackBufferCount = 1;

    g_pDD->CreateSurface(&ddsd, &g_pDDSPrimary, NULL);

 

    // Делаем вторичный буфер

    ZeroMemory(&ddscaps, sizeof(ddscaps));

    ddscaps.dwCaps = DDSCAPS_BACKBUFFER;

    g_pDDSPrimary->GetAttachedSurface(&ddscaps, &g_pDDSBack);

 

    // Устанавливаем таймер     

    SetTimer(hWnd, TIMER_ID, TIMER_RATE, NULL);

 

    return DD_OK;

}

 

А также  после #include инициализируем следующие параметры:

 

#define NAME                         "DDExample1"

#define TITLE                          "Direct Draw Example 1"

 

#define TIMER_ID                    1

#define TIMER_RATE               500

 

LPDIRECTDRAW7                   g_pDD = NULL;                // Главный DirectDraw объект

LPDIRECTDRAWSURFACE7    g_pDDSPrimary = NULL;  // Первичная память DirectDraw

LPDIRECTDRAWSURFACE7    g_pDDSBack = NULL;      // Вторичная память DirectDraw

 

static char                 szMsg[] = "Page Flipping Test: Press F12 to exit";

static char                 szFrontMsg[] = "Front buffer (F12 to quit)";

static char                 szBackMsg[] = "Back buffer (F12 to quit)";

 

РАЗЪЯСНЕНИЕ:

 

Создание объекта DirectDraw, определяем  режим отображения и разрешаемую графическую способность

 

DirectDrawCreateEx(NULL, (VOID**)&g_pDD, IID_IDirectDraw7, NULL);

Перед тем как пользоваться возможностями DirectDraw, нужно его создать, что и делает эта функция. g_pDD – содержит сам объект на DirectDraw.

 

g_pDD->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);

Устанавливаем полноэкранный графический режим.

DDSCL_EXCLUSIVE – разрешает прямой доступ к видеокарте. Т.е. все операции, которые мы будем проводить позднее, они будут обрабатываться значительно быстрее. Доступен только при параметре DDSCL_FULLSCREEN.

DDSCL_FULLSCREEN – создаем полноэкранный графический режим.

 

g_pDD->SetDisplayMode(640, 480, 8, 0, 0);

Устанавливаем графический режим.

640 – точек по горизонтали.

480 – точек по вертикали.

Режимы могут быть: 320x200, 640x480, 800x600, 1024x768, 1280x1024, 1600x1280)

8 – бит цветов или 256. Также могут быть 16 бит, 24 бита и 32 бита.

 

Создаем первичный буфер

 

ZeroMemory(&ddsd, sizeof(ddsd));

Выделяем память в видеобуфере, если ее не хватит, то в оперативную или на жестком диске.

ddsd указывает на структуру DDSURFACEDESC2, которая в свою очередь содержит характеристики  буфера . Ее свойства описаны ниже:

 

ddsd.dwSize = sizeof(ddsd);

Присваиваем размер ddsd.

 

ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;

У этой поверхности (выделенной памяти), будут использоваться параметры DDSD_CAPS и DDSD_BACKBUFFERCOUNT. Т.е. для чего она будет предназначена.

 

ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;

Эта поверхность используется в качестве первичного видеобуфера (DDSCAPS_PRIMARYSURFACE), применяется переключение видеостраниц (DDSCAPS_FLIP). Параметр DDSCAOS_COMPLEX нужен, если ставишь DDSCAPS_FLIP, для быстродействия.

 

ddsd.dwBackBufferCount = 1;

Указываем, что эта поверхность будет использовать второй буфер. Если параметры второго буфера не задаем, то они ставятся по умолчанию, в зависимости от первичного буфера.

 

g_pDD->CreateSurface(&ddsd, &g_pDDSPrimary, NULL);

Выше мы определили параметры памяти ddsd, теперь их переносим в g_pDDSPrimary, которая и будет первичным видеобуфером (тот который отображается на экране). А ddsd можно использовать для другого…

 

Создаем вторичный буфер

 

ZeroMemory(&ddscaps, sizeof(ddscaps));

Выделяем память для вторичного видеобуфера. Он должен иметь структуру не LPDIRECTDRAWSURFACE7, а DDSCAPS2, хотя в большинстве параметров они схожи J

 

ddscaps.dwCaps = DDSCAPS_BACKBUFFER;

Указываем, что данная память будет предназначена в качестве вторичного видеобуфера (т.е. память, которая не отображается на экране).

 

g_pDDSPrimary->GetAttachedSurface(&ddscaps, &g_pDDSBack);

g_pDDSBack теперь содержится вторичный буфер, который, в данном моменте, подключается к первичной памяти при помощи функцииGetAttachedSurface. И стоит вызвать функцию flip, как они поменяются на экране, т.е. невидимая, станет видимой, а видимая невидимой, но это ниже…

 

SetTimer(hWnd, TIMER_ID, TIMER_RATE, NULL);

Устанавливаем время. Через определенное миллисекунд, Windows отправляет сообщение WM_TIMER, которая нам в дальнейшем пригодится.

TIMER_ID – идентификатор часов, в нашем случае он равняется 1. Можно поставить одновременно несколько таймеров, задав функцию SetTimer но уже с индификатором 2, будет создан второй таймер, и при помощи функции KillTimer(hWnd, 2), его удаляем, а первый остается.

TIMER_RATE – ставим число в миллисекундах,  1с = 1000 мс.

 

Ниже следующий код расположите перед функцией InitApp:

 

Обработка сообщений Windows

 

long FAR PASCAL

WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

{

    switch (message)

    {

         // Завершение программы

        case WM_DESTROY:

            PostQuitMessage(0);

            return 0L;

 

         // Если нажали клавишу, в данном случае Esc и F12 завершает программу

        case WM_KEYDOWN:

            switch (wParam)

            {

                case VK_ESCAPE:

                case VK_F12:

                    PostMessage(hWnd, WM_CLOSE, 0, 0);

                    return 0L;

            }

            break;

 

         // Скрываем курсор с экрана

        case WM_SETCURSOR:

            SetCursor(NULL);

            return TRUE;

       

        // Перерисовываем экран по таймеру, таймер автоматически посылает WM_TIMER

        case WM_TIMER:

            // Перерисовываем вторичный буфер

            UpdateFrame(hWnd);

 

            // Меняем местами, первичный со вторичным буфером

            g_pDDSPrimary->Flip(NULL, 0);

 

    }

    return DefWindowProc(hWnd, message, wParam, lParam);

}

 

РАЗЪЯСНЕНИЕ:

Здесь вроде и так все понятно, читай комментарии.

 

Смотрим дальше, что у нас:

 

static void

UpdateFrame(HWND hWnd)

{

    static BYTE                 phase = 0;

    HDC                         hdc;

    DDBLTFX                     ddbltfx;

    RECT                        rc;

    SIZE                        size;

 

    // Заполняем выделенную память черным цветом и затем копируем ее во вторичный буфер

    ZeroMemory(&ddbltfx, sizeof(ddbltfx));

    ddbltfx.dwSize = sizeof(ddbltfx);

    ddbltfx.dwFillColor = 0;

    g_pDDSBack->Blt(NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx);

 

    // Отрываем контекст устройства (Кто забудет это сделать, тот не сможет вывести символы)

    g_pDDSBack->GetDC(&hdc);

    SetBkColor(hdc, RGB(0, 0, 255));

    SetTextColor(hdc, RGB(255, 255, 0));

        if (phase)

        {

            // Первый раз она пропускается, так как phase = 0. Заносим текст во вторичный буфер

            GetClientRect(hWnd, &rc);

            GetTextExtentPoint(hdc, szMsg, lstrlen(szMsg), &size);

            TextOut(hdc, (rc.right - size.cx) / 2, (rc.bottom - size.cy) / 2,

                    szMsg, sizeof(szMsg) - 1);

            TextOut(hdc, 0, 0, szFrontMsg, lstrlen(szFrontMsg));

            phase = 0;

        }

        else

        {

            // При первом обращении выполняется эта функция, заносит текст во вторичный

            // буфер

TextOut(hdc, 0, 0, szBackMsg, lstrlen(szBackMsg));

            phase = 1;

        }

        // Закрываем контекст устройства

        g_pDDSBack->ReleaseDC(hdc);

}

 

РАЗЪЯСНЕНИЕ:

 

Очищаем вторичную память.

 

ZeroMemory(&ddbltfx, sizeof(ddbltfx));

Выделяем свободную память.

ddbltfx.dwSize = sizeof(ddbltfx);

Ставим ее размер.

ddbltfx.dwFillColor = 0;

Цвет заполнения ставим 0, т.е. черный.

 

g_pDDSBack->Blt(NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx);

Заполняем вторичную память черным цветом, т.е. очищаем.

 

Рисуем во вторичной памяти:

 

g_pDDSBack->GetDC(&hdc);

Открываем контекст устройства для вторичной памяти, потому что именно в ней будут происходить изменения.

 

SetBkColor(hdc, RGB(0, 0, 255));

Устанавливаем фон текста, RGB – красный, зеленый, синий.

 

SetTextColor(hdc, RGB(255, 255, 0));

Устанавливаем цвет самого текста.

 

if (phase)

   {

       GetClientRect(hWnd, &rc);

       GetTextExtentPoint(hdc, “Тест переключения страниц: нажмите F12, чтобы выйти”,

lstrlen(“Тест переключения страниц: нажмите F12, чтобы выйти”),

&size);

       TextOut(hdc, (rc.right - size.cx) / 2, (rc.bottom - size.cy) / 2, “Тест переключения страниц:

нажмите F12, чтобы выйти”, sizeof(“Тест переключения страниц: нажмите F12,

чтобы выйти”) - 1);

       TextOut(hdc, 0, 0, “Первичный буфер: (F12 выход)”.szFrontMsg, lstrlen(“Первичный буфер:

(F12 выход)”.));

       phase = 0;

   }

Выполняется в том случае, если phase равна 1 (при первом обращении она не выполняется).

 

GetClientRect(hWnd, &rc);

Заносит в структуру rc параметры окна, левого верхнего угла, и правого нижнего.

 

GetTextExtentPoint(hdc, “Тест переключения страниц: нажмите F12, чтобы выйти”,

lstrlen(“Тест переключения страниц: нажмите F12, чтобы выйти”), &size);

Заносит в структуру size ширину и высоту данной строки. Которая используется для вывода текста по середине экрана.

 

TextOut(hdc, (rc.right - size.cx) / 2, (rc.bottom - size.cy) / 2, “Тест переключения страниц:

нажмите F12, чтобы выйти”, sizeof(“Тест переключения страниц: нажмите F12,

чтобы выйти”) - 1);

Выводим текст в координатах х и y, задав его текст и размер текста. Один вычитаем, чтобы не было иероглифа.

 

Переключение страниц:

 

Затем выводим вторую строку на вторичный буфер, выполняется команда                 g_pDDSPrimary->Flip(NULL, 0); и вторичный буфер мы с Вами увидим на экране, а первичный “прячется”. Через некоторое время Windows посылает команду WM_TIMER и на этот раз выполняется if, куда заносятся две строки.





<< ВЕРНУТЬСЯ В ПОДРАЗДЕЛ

<< ВЕРНУТЬСЯ В ОГЛАВЛЕНИЕ




Материалы находятся на сайте https://exelab.ru/pro/



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS