admin管理员组

文章数量:1516870

只要是介绍编程的书,似乎有一个不成文的规定,即第一个例子由“ Hello World ”开始,本教程也不例外。那么,如果你早已迫不及待想初尝 DirectDraw 程序编译成功后的“ 0 error(s), 0 warning(s) ”的喜悦,就让我们开始吧!

在下面的例子中,我们将利用 Visual C++5.0 来生成一个简单的 DirectDraw 应用程序。程序的创建将不使用方便的 MFC Microsoft Foundation Class Library ,微软的 C++ 基础类库)向导,而是使用最原始的 Win32 应用程序开发环境。熟悉 VC++ 的读者可能会问,为什么舍先进的 MFC 工具不用,而去使用最原始的方法呢?这是因为, MFC 主要是用于基于窗口和文档的应用软件的编程,它集成了大量的数据和方法,将许多烦琐的任务,如:应用程序初始化、文档处理、磁盘 IO 封装起来,虽然这样可以给你的编程带来了极大的便利,但是在你编制基于图形显示和多媒体的应用程序的时候,这却会给你带来极大的麻烦。首先,你无法触及系统的内核,如:你需要自己来处理每一个消息循环时,而 MFC 并没有为你留出这样一个接口;而且, MFC 为你事先建好的类,它们的许多功能对你来说是没用和低效率的,使用它们只会给你的程序带来冗余和不便。

  • 总之,MFC为你隐藏了太多技术细节,而DirectDraw编程需要系统对于开发者具有一定的透明度。

所以,在大多数情况下,我们用最基本的 Win32 应用程序开发环境来开发我们的 DirectDraw 应用程序,本教程中几乎所有的例程都是使用 Win32 开发环境。当然,这并不是说用 MFC 就不能编制基于 DirectDraw 的应用程序了,它也是可以的,这将在本教程的“ MFC 创建 DirectDraw 应用程序 ”一章中做介绍。

使用 Win32 开发环境表明,你必须从 WinMain ()开始编程,自己写每一个消息的处理程序,这的确是一项很繁重的工作。但是当你理解和熟悉了这一套方法时,你会发现它其实是相当直观和容易的。

  1. 打开 Visual C++ 5.0
  2. 选择 File 菜单的 New ,在出现的对话框中,选择 Projects 栏目(新建工程),并点取其下的 Win32 Application 项,表示使用 Win32 环境创建应用程序。先在 Locatin (路径)中填入“ c:\ ”,然后在 Project Name (项目名称)中填入“ Hello ”,其它按照缺省设置,使对话框如图所示。单击 OK 按钮。

  • 此时,一个基于Win32的工程已经创建完毕,但是它还没有包括任何文件。你需要新建一个C++文件增加到工程中。
  1. 再次选择 File 菜单的 New ,在出现的对话框中,选择 Files 栏目(新建文件),并点取其下的 C++ Source File 项,表示新建一个 C++ 源文件。在右边的 File 栏中输入“ Hello ”,最后确定让 Add to project 检查框打上勾,使整个对话框如图所示。单击 OK 按钮。
  1. 在Hello.cpp文件中输入以下源程序代码,当然,你最好的做法是将以下的代码复制到你的文件中去,确保能用。
  1. //*******************************************************************

    // 工程:hello

    // 文件:hello.cpp

    // 内容:创建第一个DirectDraw应用程序,

    //*******************************************************************

    #include <windows.h>

    #include <windowsx.h>

    #include <ddraw.h>

    LPDIRECTDRAW lpDD; // DirectDraw对象

    LPDIRECTDRAWSURFACE lpDDSPrimary; // DirectDraw主页面

    char szMsg1[] = "Hello World, I am DirectDraw boy !";

    char szMsg2[] = "按 ESC 退出";

    //函数声明

    LRESULT CALLBACK WinProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam );

    BOOL InitWindow( HINSTANCE hInstance, int nCmdShow );

    BOOL InitDDraw( void );

    void FreeDDraw( void );

    //*******************************************************************

    //函数:WinMain()

    //功能:Win32应用程序入口函数。进行初始化工作,处理消息循环

    //*******************************************************************

    int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,

    LPSTR lpCmdLine, int nCmdShow)

    {

    MSG msg;

    //初始化主窗口

    if ( !InitWindow( hInstance, nCmdShow ) )

    return FALSE;

    //初始化DirectDraw环境,并实现DirectDraw功能

    if ( !InitDDraw())

    {

    MessageBox(GetActiveWindow(), "初始化DirectDraw过程中出错!", "Error", MB_OK );

    FreeDDraw();

    DestroyWindow(GetActiveWindow());

    return FALSE;

    }

    //进入消息循环

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

    {

    TranslateMessage(&msg);

    DispatchMessage(&msg);

    }

    return msg.wParam;

    }

    //******************************************************************

    //函数:InitWindow()

    //功能:创建主窗口。

    //******************************************************************

    static BOOL InitWindow( HINSTANCE hInstance, int nCmdShow )

    {

    HWND hwnd; //窗口句柄

    WNDCLASS wc; //窗口类结构

    //填充窗口类结构

    wc.style = 0;

    wc.lpfnWndProc = WinProc;

    wc.cbClsExtra = 0;

    wc.cbWndExtra = 0;

    wc.hInstance = hInstance;

    wc.hIcon = LoadIcon( hInstance, IDI_APPLICATION );

    wc.hCursor = LoadCursor( NULL, IDC_ARROW );

    wc.hbrBackground = GetStockObject(BLACK_BRUSH);

    wc.lpszMenuName = NULL;

    wc.lpszClassName = "dxHello";

    //注册窗口类

    RegisterClass( &wc );

    //创建主窗口

    hwnd = CreateWindowEx(

    0,

    "dxHello",

    "",

    WS_POPUP,

    0, 0,

    GetSystemMetrics( SM_CXSCREEN ),

    GetSystemMetrics( SM_CYSCREEN ),

    NULL,

    NULL,

    hInstance,

    NULL );

    if( !hwnd ) return FALSE;

    //显示并更新窗口

    ShowWindow( hwnd, nCmdShow );

    UpdateWindow( hwnd );

    return TRUE;

    }

    //******************************************************************

    //函数:WinProc()

    //功能:处理主窗口消息

    //******************************************************************

    LRESULT CALLBACK WinProc( HWND hWnd, UINT message,

    WPARAM wParam, LPARAM lParam )

    {

    switch( message )

    {

    case WM_KEYDOWN://击键消息

    switch( wParam )

    {

    case VK_ESCAPE:

    PostMessage(hWnd, WM_CLOSE, 0, 0);

    break;

    }

    break;

    case WM_DESTROY://退出消息

    FreeDDraw();

    PostQuitMessage( 0 );

    break;

    }

    //调用缺省消息处理过程

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

    }

    //******************************************************************

    //函数:InitDDraw()

    //功能:初始化DirectDraw环境并实现其功能。包括:创建DirectDraw对象,

    // 设置显示模式,创建主页面,输出文字。

    //******************************************************************

    BOOL InitDDraw(void)

    {

    DDSURFACEDESC ddsd; //页面描述

    HDC hdc; //设备环境句柄

    //创建DirectCraw对象

    if ( DirectDrawCreate( NULL, &lpDD, NULL ) != DD_OK ) return FALSE;

    // 取得独占和全屏模式

    if ( lpDD->SetCooperativeLevel( GetActiveWindow(),

    DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN ) != DD_OK)

    return FALSE;

    //设置显示模式

    if ( lpDD->SetDisplayMode( 640, 480, 8 ) != DD_OK) return FALSE;

    //填充主页面信息

    ddsd.dwSize = sizeof( ddsd );

    ddsd.dwFlags = DDSD_CAPS;

    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;

    //创建主页面对象

    if ( lpDD->CreateSurface( &ddsd, &lpDDSPrimary, NULL ) != DD_OK)

    return FALSE;

    //输出文字

    if ( lpDDSPrimary->GetDC(&hdc) != DD_OK) return FALSE;

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

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

    TextOut( hdc, 220, 200, szMsg1, lstrlen(szMsg1));

    TextOut( hdc, 280, 240, szMsg2, lstrlen(szMsg2));

    lpDDSPrimary->ReleaseDC(hdc);

    return TRUE;

    }

    //******************************************************************

    //函数:FreeDDraw()

    //功能:释放所有的DirectDraw对象。

    //******************************************************************

    void FreeDDraw( void )

    {

    if( lpDD != NULL )

    {

    if( lpDDSPrimary != NULL )

    {

    lpDDSPrimary->Release();

    lpDDSPrimary = NULL;

    }

    lpDD->Release();

    lpDD = NULL;

    }

    }

  1. 为了简化代码,这第一个入门程序没有头文件。在进行编译之前,还得进行最后的设置。选择 Project 菜单的 Settings… ,出现工程设置对话框。选择 Link 栏,在 Object/Library modules 中添入“ Ddraw.lib ”。使对话框如图所示。

  • 请注意:这一步骤是将DirectDraw的静态连接库文件连接到工程中,否则,程序虽然可以正常编译,但是在连接时会产生一个“unresolved external symbol”(没有定义的外部符号)的错误。在以后所有的DirectDraw程序中,都必须将与你所用到的DirectDraw组件相应的静态连接库添入到这个设置中。

至此,一个最基本的 DirectDraw 应用程序已创建完毕,你现在不必去深究这些代码的含义,在下面及以后的章节中我们会对它们进行详细的分析。这虽不是一个最简单的 DirectDraw 应用程序,但它确实是一个能够实现最基本的输出功能的 DirectDraw 程序。

F7 编译成功后,按 Ctrl+F5 ,执行该程序,显示器将切换到 640*480*256 色模式,黑屏后,屏幕中央会打印出蓝底黄字“ Hello World, I am DirectDraw boy ! ”,除了输出这些字符外,这个程序什么也不做。按 ESC 可退出程序。程序运行结果如下图

这就是 DirectDraw ?有的人也许会对 DirectDraw 感到很失望,因为它并没有为我们表现出神奇的功能啊?但有的人却会对此感到异常兴奋,他们觉得一扇充满诱惑的房间的大门正向他们打开。这就象透过天窗,有些人只会看到黑暗的夜空,有些人却能看见满天的星星一样。第一个例子,为了使程序不至于太长而让那些初学者望而生畏,所以只能一再简化(尽管这样,整个程序还是占用了相当大的篇幅)。在后续章节的例子中,你们会看到程序会一个比一个更精彩。


下面,让我们来逐一分析一下这个程序。

分析程序应该是一个由外而内,逐步求精的过程。首先从大的方面来看,这个程序一共用到了五个函数,如果按照正常顺序,排除程序中出错的可能,它们的调用顺序依次是这样的。

WinMain ----> InitWindow ----> InitDDraw ----> WinProc ----> FreeDDraw

WinMain :所有 Win32 应用程序的入口函数,它也是应用程序关闭时的出口,一个应用程序的全生命周期就是在它的控制之下。所以,确切的说,其它四个函数是被包括在 WinMain 之内的。消息循环也是在这个函数中启动。

InitWindow :初始化和创建一个与程序的 HINSTANCE (实例句柄)相关联的主窗口,这个窗口的 HWND (窗口句柄)在初始化 DirectDraw 环境时需要用到。

InitDDraw :初始化和创建 DirectDraw 对象,并执行一定的功能。它里面包括了创建 DirectDraw 对象,创建页面,设置显示模式,创建主页面,输出文字。

WinProc :是应用程序感知外来动作和产生反应的神经中枢,相当于人的大脑。这是程序中最主要的部件之一,它和在 WinMain 中所启动的消息循环是一起工作的。

FreeDDraw :释放 DirectDraw 的各种对象,以使其不再占用内存空间。

上面这段话如果使你感到迷惑,就象是在你小学的时候有人给你讲什么是微积分,那么你仍需要事先预习一下 Win32 编程。请跳转到本教程的“ ”一章,复习一下 Win32 编程的基础知识。

如果你对 Win32 编程和 Windows 的消息机制有一定的了解的话,以上概念是比较容易理解的。


分析完程序的总结构,再让我们从最开头看起。

#include <ddraw.h>

这是把 DirectDraw 的头文件包含到文件里来。这一步在以后所有的例程中都是必不可少的。

LPDIRECTDRAW lpDD; // DirectDraw 对象

LPDIRECTDRAWSURFACE lpDDSPrimary; // DirectDraw 主页面

接着定义了三个全局指针变量,它们都是指向对象的指针。第一个是 DirectDraw 对象,表示显示硬件,它包括了显示器和显卡还有显存,用它来代表整个显示系统。第二个是 DirectDrawSurface 对象,表示页面,你可以在大脑中把它想象成一张矩形的白纸,你可以在上面绘制图象。现在你暂且不用去深究它们的含义,看下去就是了,在后面的课程中,我还会更加详细的介绍。

LPDIRECTDRAW LPDIRECTDRAWSURFACE 是在 ddraw.h 头文件里预定义的指向 DirecctDraw DirectDrawSurface 对象的长型指针,所以前面加了 LP ,代表 Long Point 。因为这些 DirectDraw 对象指针变量经常用到,所以给它起名为 lpDD lpDDSPrimary DD DirectDraw 的缩写。

  • 给一个变量命名时,在变量名前加上该变量的类型标志,在Windows编程中是一个默认约定,称为匈牙利表示法,该名称来源于微软的一个匈牙利籍资深程序员,因为他惯用此表示法,后来便成了规范。如lpDD表示一个长型指针变量,dwHeight代表一个DWORD型变量。这样的好处就在于你可以一眼就辨认出某变量的类型,而不用去追溯它的定义。

这里所说的“对象” (Object) ,并不完全等同于 C++ 中对象的概念,尽管它们使用的是同一个英文单词 Object 。这里的对象指的是 COM COM Component Object Model 的缩写,代表“部件对象模型”,它在 DirectX 中贯穿始终,无处不是它的身影。

  • 在DirectX SDK中,大多数APIs(应用程序编程接口)由对象和基于COM的接口组成。COM是致力于可重复利用接口资源的面向对象系统的基础,并且是OLE编程的核心模型。它也是一个接口规范,通过它可以设计出许多接口。它是建立在操作系统层次的对象模型。

COM C++ 类也有许多相同之处。对一个 C++ 程序员来说, COM 接口就象是一个抽象基础类。这就是说,它定义了一套关键符号( signatures )和语法( semantics ),但不是执行语句,并且没有与接口相关联的状态数据。在 C++ 抽象类中,所有的方法被定义成纯虚( pure virtual )函数,它们并没有实际的代码。在这一点上, COM 和基础类是一致的。

COM 对象与 C++ 对象的另一个相似点是:一个函数的第一个引用是接口或类的名称,在 C++ 中叫做 this 引用。因为 COM 对象和 C++ 对象是完全二进制兼容的,编译器把 COM 接口当作 C++ 抽象类来看,而且采取同样的语法。这样就可以减少代码的复杂程度。例如, this 引用在 C++ 中被当作可识元素,并且被暗中的操作, COM 中也是如此。

  • COM 是DirectX的基础,虽然它和C++中的类不近相同,但你完全可以把它当成C++的类来看待,在实际编程中,它们的语法和接口也是完全一样的。所以,对于C++程序员,进行DirectX编程并不需要你去学习新的编程方法,继续沿用你所熟知的C++,DirectX也能为你所用。

只要是你要用到 DirectDraw 接口的特性,都必须创建一个 DirectDraw 对象,它是 DirectDraw 接口的核心。它是这样被创建的:

if ( DirectDrawCreate( NULL, &lpDD, NULL ) != DD_OK ) return FALSE;

DirectDrawCreate() 函数是在 ddraw.h 中定义的,关于这个函数的详细解释,请参看本教程的“ ”一章,它的原型如下:

HRESULT DirectDrawCreate(GUID FAR * lpGUID, LPDIRECTDRAW FAR * lplpDD, IUnknown FAR * pUnkOuter);

第一个参数是 lpGUID :指向 DirectDraw 接口的全局唯一标志符( GUID Global unique identify )的指针。在这里,我们给它 NULL ,表示我们将使用当前的 DirectDraw 接口。

第二个参数是 lplpDD :这个参数是用来接受初始化成功的 DirectDraw 对象的地址。在这里,我们给它 &lpdd

第三个参数是 pUnkOuter :千万不要追问这个参数是干嘛使的,如果你不想惹麻烦,就给它 NULL 吧。 Microsoft 的说明书上是这么写的“考虑到与将来的 COM 集合特性保持兼容,当前,不管怎样,如果这个参数不是 NULL DirectDrawCreate 将返回一个错误”。

所有的 DirectDraw 函数的返回值都是 HRESULT 类型,它是一个 4 字节( 32 位)的值,用来代表某个错误或警告。 DirectDraw 头文件中已经预定义了所有可能的返回值常量,仅函数返回成功的值是用 DD_OK ”表示,所有的错误值标志开头都为“ DDERR ”,如:

DDERR_DIRECTDRAWALREADYCREATED
DDERR_GENERIC
DDERR_OUTOFMEMORY

  • Windows编程中,有一项让中国软件开发者大挠其头的就是函数名、常量名和数据结构名称中往往有长串的字符。这时,就要考验读者的英文断句水平了。如:DDERR_OVERLAYCOLORKEYONLYONEACTIVE,应该断为:DDERR-OVERLAY-COLORKEY-ONLY-ONE-ACTIVE。这对于老外来说也许并不成什么问题,但对于我们,有时候要看懂一个语句,简直就象在做一道英文题。除非你对一个名称有十足的把握,否则就应该尽量使用CP规则(原是离散数学里的一条规则,现引申为Copy-Paste,即:复制-粘贴)。而且,给读者一点忠告,在起函数、变量或常量名称时,也应该沿用老外的这条原则,以尽量清楚的表达意义为宗旨,不要担心它是否太长(当然,对于那些重复利用率很高的函数、变量或常量,还应该尽量使用缩写),否则,总是以i、n为变量,程序的可读性会变得极差,而在编程的初学者当中,这是很常见的。

DirectDrawCreate 函数调用成功后, lpDD 已经指向了一个 DirectDraw 对象,它是整个 DirectDraw 接口的最高层领导,以后的步骤都是在它的控制之下。

if ( lpDD->SetCooperativeLevel( GetActiveWindow(),

DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN ) != DD_OK)

return FALSE;

这个语句用来设置应用程序对操作系统的控制程度。它的原型如下:

HRESULT SetCooperativeLevel( HWND hWnd, DWORD dwFlags )

第一个参数是 hWnd ,我们调用 Win32 API 函数 GetActiveWindow 获得应用程序主窗口的句柄,这将使 DirectDraw 对象与主窗口的消息挂上勾。

第二个参数是 dwFlags ,控制级的标志符。我们给它 DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN ,表示我们期望 DirectDraw 以独占和全屏方式工作。

这个函数有很多用法,而且它必须在创建 DirectDraw 对象之后,立即调用。你可以用它来设置应用程序是运行于全屏还是窗口模式,是独占还是共享模式。具体用法将在以后逐步介绍。可参阅“ DirectDraw 参考手册”。

if ( lpDD->SetDisplayMode( 640, 480, 8 ) != DD_OK) return FALSE;

显而易见,这是设置显示器的显示模式,它把显示模式设为 640*480 8 位色彩模式(即 256 色)。这是绝大多数显示器所能够支持的显示模式,所以我们不用担心它会出什么问题。在以后的例程中,我们将看到可以调用 EnumDisplayModes() 来列举出显示器所支持的所有显示模式。绝不要轻易尝试直接设置一个新的显示模式,而应从列举出的显示模式中选择,这也将在后续章节讲到。

  • 要注意的是,只有当DirectDraw对象为独占访问的控制程度时才能改变显示器的显示模式,如果DirectDraw对象运行为窗口模式,调用该函数会返回一个错误。

然后的任务是要创建一个 DirectDrawSurface 对象。

DirectDrawSurface 对象代表了一个页面。页面可以有很多种表现形式,它既可以是可见的(屏幕的一部分或全部),称之为主页面( Primary Surface );也可以是作换页用的不可见页面,称之后台缓存( Back Buffer ),在换页后,它成为可见;还有一种始终不可见的,称之为离屏页面( Off-screen Surface ),用它来存储图象。其中,最重要的页面是主页面,每个 DirectDraw 应用程序都必须创建至少一个主页面,用它来代表屏幕上可见的区域,说白了,就是你的显示屏幕。

创建一个页面要分两步走,这里,我们以创建主页面为例,简要介绍一下这两个步骤,其它类型页面的创建也与之类似,在以后的例程中还会着重讲解。

  1. 在调用 CreateSurface() 函数创建一个页面之前,首先需要填充一个 DDSURFACEDESC 的结构,它是 DirectDraw Surface Description 的缩写,意思是 DirectDraw 的页面描述。该结构的详细资料请参看本教程“ ”一章。

//填充主页面信息

ddsd.dwSize = sizeof( ddsd ); //结构的大小

ddsd.dwFlags = DDSD_CAPS; //指定DDSURFACEDESC结构的ddsCaps成员为可用

ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; //指定要创建的是主页面

  • 这就象是你到银行取款,必须事先填写一张取款单,你要在上面详细描述你的姓名,你的存折序号,以及你要取的钱数等等,然后把它递给银行工作人员。
  1. 页面描述填充完毕后,把它传递给 CreateSurface() 函数即可。

//创建主页面对象

if ( lpDD->CreateSurface( &ddsd, &lpDDSPrimary, NULL ) != DD_OK)

return FALSE;

CreateSurface() 函数的第一个参数是被填充了页面信息的 DDSURFACEDESC 结构的地址,为 &ddsd ;第二个参数是接收主页面指针的地址,此处为 &lpDDSPrimary ;第三个参数现在必须为 NULL ,为该函数所保留。

如果函数调用成功, lpDDSPrimary 将代表一个合法的主页面对象。由于在前面已经设置了该程序的工作模式为独占和全屏,所以,此时主页面所代表的实际上是你的整个显示屏幕。在主页面上所绘制的图形将立即反映到你的显示屏幕上。


下面开始在主页面上输出文字。

//输出文字

if ( lpDDSPrimary->GetDC(&hdc) != DD_OK) return FALSE; //获得设备环境句柄

SetBkColor( hdc, RGB( 0, 0, 255 ) ); //设置背景颜色

SetTextColor( hdc, RGB( 255, 255, 0 ) ); //设置文字颜色

TextOut( hdc, 220, 200, szMsg1, lstrlen(szMsg1)); //输出文字

TextOut( hdc, 280, 240, szMsg2, lstrlen(szMsg2));

lpDDSPrimary->ReleaseDC(hdc); //释放资源

如果你十分熟悉 Windows GDI (图形设备接口),你会发现上面这段程序居然和在窗口中输出文字一模一样。这是因为, DirectDraw 页面和 GDI 的设备环境( DC )其实是兼容的。在调用主页面的 GetDC() 函数获得页面的设备环境句柄( HDC )之后,就可以使用 Win32 API 绘图函数来进行绘图操作了。同样,最后也必须调用主页面的 ReleaseDC() 函数来释放设备环境资源。


在程序结束之前, DirectDraw 还必须做一项扫尾工作,即:把已经创建的所有 DirectDraw 对象从内存中清除出去。这就是 FreeDDraw() 函数的作用。

void FreeDDraw( void )

{

if( lpDD != NULL ) //判断DirectDraw对象是否为空

{

if( lpDDSPrimary != NULL ) //判断主页面对象是否为空

{

lpDDSPrimary->Release(); //释放

lpDDSPrimary = NULL;

}

lpDD->Release(); //释放

lpDD = NULL;

}

}

每一个 DirectDraw 接口的对象都有 Release() 函数,以将其所引用的对象释放。这其实相当于 C++ 中的 delete 方法。及时的将不用的对象释放掉是每一个优秀的程序员都应当养成的良好习惯。


以上关于 DirectDraw 接口的编程似乎与应用程序的主窗口没有一点联系,而且执行后,也看不到窗口的影子,是不是就可以不用创建程序的主窗口了呢?当然不是。

其实,程序执行后,漆黑的背景就是程序的主窗口。在注册窗口类时,已经给 hbrBackground 成员指定了黑色。

wc.hbrBackground = GetStockObject(BLACK_BRUSH);

而且,在创建主窗口时,窗口的类型用的是 WS_POPUP ,表明创建的是一个没有标题栏,没有边框的窗口;而不是常用的 WS_OVERLAPPEDWINDOW

hwnd = CreateWindowEx(

……

WS_POPUP,

……


值得特别注意的是:在设置
DirectDraw 对象控制级的时候,使用了主窗口的句柄( HWND )。这就是说, DirectDraw 将用主窗口来接收各种消息,这样,就可以利用主窗口来实现 DirectDraw 对用户操作的反馈。如,用户想用光标键来控制游戏图象的运动,光标键按下的消息被发往主窗口,然后在主窗口的消息处理过程中操作 DirectDraw ,使之对光标键产生反应。所以,在大多数情况下,创建一个主窗口是必不可少的。

if ( lpDD->SetCooperativeLevel( GetActiveWindow() /*主窗口句柄*/ ,

DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN ) != DD_OK)

return FALSE;


DirectX 是一个功能强大而且使用复杂的工具,在这一章里,我们仅学习了一个最简单的利用 DirectDraw 接口的例子,对于庞大的 DirectX 来说,它只是冰山一角。要掌握 DirectDraw 编程是一个复杂的过程,需要从大量的实践中摸索出经验,任何读者想一蹴而就是不可能的。但是,好的开端意味着成功的一半,如果你能理解和掌握本章所学到的内容,继续下去,坚持不泄,你会发现你将一天比一天更加充实。

通过本章的学习,读者应能掌握:

  • VC++5.0 环境中使用 Win32 应用程序开发环境新建一个 DirectDraw 工程以及添加源文件。
  • DirectDrawCreate() 函数创建 DirectDraw 对象,并立即调用 SetCooperativeLevel() 设置其控制级,注意其第一个参数为应用程序主窗口句柄。
  • 只有在独占的控制级下才能调用 SetDisplayMode() 以改变显示器的显示模式。
  • 在调用 CreateSurface () 创建 DirectDrawSurface 对象之前,必须先填充一个 DDSURFACEDESC 结构以描述页面的信息。
  • 页面对象的种类,只有主页面代表了显示屏幕,为可见页面。
  • GetDC() 获得主页面的 HDC ,然后可以使用 Windows API GDI 函数进行绘图或输出文字,最后必须调用 ReleaseDC() 以释放资源。
  • DirectDraw 接口的对象必须被及时的用 Release() 函数释放。

本文标签: 对象应用程序编程