呵呵,今天早上一起来就听到老婆的手机不行了,唉。我也想换一个,怎么舍不得咯。
不管了,我今天要学DirectDraw了。一定要在一周之内做完。
首先用的第一个例子是directdraw.chm 里面的一个DirectDraw入门程序
- 选择Link栏,在Object/Library modules中添入“Ddraw.lib”。
- 请注意:这一步骤是将DirectDraw的静态连接库文件连接到工程中,否则,程序虽然可以正常编译,但是在连接时会产生一个“unresolved external symbol”(没有定义的外部符号)的错误。在以后所有的DirectDraw程序中,都必须将与你所用到的DirectDraw组件相应的静态连接库添入到这个设置中。
我按照文档提示的来,并没有出现文档所提示的效果。有点郁闷,我只能往下看了。我昨天也调试了很久,没有调出来。我开始以为是TEXTOUT这个函数显示字符串的问题,但现在看来,又好像不是,我迷惑了。
函数调用顺序依次是这样的。
WinMain ----> InitWindow ----> InitDDraw ----> WinProc ----> FreeDDraw
WinMain:所有Win32应用程序的入口函数,它也是应用程序关闭时的出口,一个应用程序的全生命周期就是在它的控制之下。所以,确切的说,其它四个函数是被包括在WinMain之内的。消息循环也是在这个函数中启动。
InitWindow:初始化和创建一个与程序的HINSTANCE(实例句柄)相关联的主窗口,这个窗口的HWND(窗口句柄)在初始化DirectDraw环境时需要用到。
InitDDraw:初始化和创建DirectDraw对象,并执行一定的功能。它里面包括了创建DirectDraw对象,创建页面,设置显示模式,创建主页面,输出文字。
WinProc:是应用程序感知外来动作和产生反应的神经中枢,相当于人的大脑。这是程序中最主要的部件之一,它和在WinMain中所启动的消息循环是一起工作的。
FreeDDraw:释放DirectDraw的各种对象,以使其不再占用内存空间。
第一个是DirectDraw对象,表示显示硬件,它包括了显示器和显卡还有显存,用它来代表整个显示系统。
第二个是DirectDrawSurface对象,表示页面,你可以在大脑中把它想象成一张矩形的白纸,你可以在上面绘制图象。
这里所说的“对象”(Object),并不完全等同于C++中对象的概念,尽管它们使用的是同一个英文单词Object。这里的对象指的是COM,COM是Component Object Model 的缩写,代表“部件对象模型”,它在DirectX中贯穿始终,无处不是它的身影
HRESULT DirectDrawCreate(GUID FAR * lpGUID, LPDIRECTDRAW FAR * lplpDD, IUnknown FAR * pUnkOuter); ---àThis function creates an instance of a DirectDraw object.
第一个参数是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
这个语句用来设置应用程序对操作系统的控制程度。它的原型如下:
HRESULT SetCooperativeLevel( HWND hWnd, DWORD dwFlags )
IDirectDraw4::SetCooperativeLevel
The IDirectDraw4::SetCooperativeLevel method determines the top-level behavior of the application. 这个函数在MSDN2001里才有,2005里面已经找不到这个函数了。
第一个参数是hWnd,我们调用Win32的API函数GetActiveWindow获得应用程序主窗口的句柄,这将使DirectDraw对象与主窗口的消息挂上勾。
第二个参数是dwFlags,控制级的标志符。我们给它DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN,表示我们期望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应用程序都必须创建至少一个主页面,用它来代表屏幕上可见的区域,说白了,就是你的显示屏幕。
创建一个页面要分两步走,这里,我们以创建主页面为例
- 在调用CreateSurface()函数创建一个页面之前,首先需要填充一个DDSURFACEDESC的结构,它是DirectDraw Surface Description的缩写,意思是DirectDraw的页面描述。该结构的详细资料请参MSDN。
//填充主页面信息
ddsd.dwSize = sizeof( ddsd ); //结构的大小
ddsd.dwFlags = DDSD_CAPS; //指定DDSURFACEDESC结构的ddsCaps成员为可用
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; //指定要创建的是主页面
- 这就象是你到银行取款,必须事先填写一张取款单,你要在上面详细描述你的姓名,你的存折序号,以及你要取的钱数等等,然后把它递给银行工作人员。
- 页面描述填充完毕后,把它传递给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); //释放资源
这里有一点点的小问题,这里和VC2005有区别。在注意了。TEXTOUT
通过本章的学习,读者应能掌握:
· 在VC++5.0环境中使用Win32应用程序开发环境新建一个DirectDraw工程以及添加源文件。
· 用DirectDrawCreate()函数创建DirectDraw对象,并立即调用SetCooperativeLevel()设置其控制级,注意其第一个参数为应用程序主窗口句柄。
· 只有在独占的控制级下才能调用SetDisplayMode()以改变显示器的显示模式。
· 在调用CreateSurface ()创建DirectDrawSurface对象之前,必须先填充一个DDSURFACEDESC结构以描述页面的信息。
· 页面对象的种类,只有主页面代表了显示屏幕,为可见页面。
· 用GetDC()获得主页面的HDC,然后可以使用Windows API的GDI函数进行绘图或输出文字,最后必须调用ReleaseDC()以释放资源。
· DirectDraw接口的对象必须被及时的用Release()函数释放。
这里的代码看完了,我依然不知道为什么看不到代码所提示的现象,所以,我要看看例子程序了。我把整个例子程序的代码都复制到我的学习DirectDraw目录下面。DDraw
这里我看一下第一个例子。它也是在屏幕上面输出文字。
睡了一觉醒来,还是不知道哪里错了。
该函数完成一次换页操作。将与后台缓冲区相关联的页面内存换页成与前台缓冲区页面相关联。
HRESULT Flip(
LPDIRECTDRAWSURFACE3 lpDDSurfaceTargetOverride,
DWORD dwFlags
);
lpDDSurfaceTargetOverride -- >The default for this parameter is NULL, in which case DirectDraw cycles through the buffers in the order that they are attached to each other. 该参数缺省值是NULL,在这种情况下,DirectDraw从换页链中按照前后隶属关系依次换页。
Access to the surface is refused because the surface memory is gone. Call the IDirectDrawSurface7::Restore method on this surface to restore the memory associated with it.
The previous blit operation that is transferring information to or from this surface is incomplete.
The IDirectDrawSurface7::Restore method restores a surface that has been lost. This occurs when the surface memory associated with the DirectDrawSurface object has been freed. 恢复丢失的页面。与DirectDrawSurface对象相关联的页面内存被释放时,会导致页面丢失
经过一天的折腾,我决定把今天看到的代码自己写一遍,加深印象。
1:新建一个基于WIN32的应用程序,我这里加的不是CONSOLE应用程序,要注意拉。
2:
//*******************************************************************
//包含头文件
//*******************************************************************
#include <windows.h>
#include <ddraw.h>
#include <stdio.h>
#include <stdarg.h> //定义成一个可变参数列表的指针
#include "resource.h"
3: //*******************************************************************
//函数:WinMain()
//功能:Win32应用程序入口函数。进行初始化工作,处理消息循环
//*******************************************************************
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nShowCmd //这里跟示例中的不一样,int PASCAL WinMain
)
{
MSG msg;
初始化主窗口
if (InitApp(hInstance, nCmdShow) != DD_OK)
return FALSE;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
4://******************************************************************
//函数:InitApp(HINSTANCE hInstance, int nCmdShow)
//功能:创建主窗口。初始化DirectDraw环境并实现其功能。包括:创建DirectDraw对象,
// 设置显示模式,创建主页面。
//******************************************************************/
static HRESULT
InitApp(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
WNDCLASS wc; //初始化窗口用
DDSURFACEDESC2 ddsd; //The DDSURFACEDESC2 structure contains a description of a surface
DDSCAPS2 ddscaps;
HRESULT hRet; //初始化DirectDraw环境
// Set up and register window class
wc.style = CS_HREDRAW | CS_VREDRAW; //窗口重绘时,水平垂直重画
wc.lpfnWndProc = WindowProc; //WindowProc是一个函数指针,指向窗口过程函数
wc.cbClsExtra = 0; //类附加内存,默认为
wc.cbWndExtra = 0; //窗口附加内存,默认为
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MAIN_ICON));
//MAKEINTRESOURCE宏把资源ID标识符转换为需要的LPCTSTR类型
/*HICON LoadIcon(
HINSTANCE hInstance,
LPCTSTR lpIconName
);
*/
wc.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));//这里的这个宏是我自己加的
wc.hbrBackground = (HBRUSH )GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = NAME;
wc.lpszClassName = NAME;
RegisterClass(&wc);
// Create a window
hWnd = CreateWindowEx(WS_EX_TOPMOST,
//WS_EX_TOPMOS 的含义Specifies that a window created with this style should be placed above all
//non-topmost windows and should stay above them
NAME,
TITLE,
WS_POPUP,//窗口的类型用的是WS_POPUP,表明创建的是一个没有标题栏,没有边框的窗口;
//而不是常用的WS_OVERLAPPEDWINDOW
0,
0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
NULL,
NULL,
hInstance,
NULL);
if (!hWnd)
return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
SetFocus(hWnd); //功能设置键盘焦点到指定的窗口
///
// Create the main DirectDraw object
///
hRet = DirectDrawCreateEx(NULL, (VOID**)&g_pDD, IID_IDirectDraw7, NULL);
//第二个参数是用来接受初始化成功的DirectDraw对象的地址
//第三个参数This parameter must be set to IID_IDirectDraw7
if (hRet != DD_OK)
return InitFail(hWnd, hRet, "DirectDrawCreateEx FAILED");
// Get exclusive mode
hRet = g_pDD->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
if (hRet != DD_OK)
return InitFail(hWnd, hRet, "SetCooperativeLevel FAILED");
// Set the video mode to 640x480x8
hRet = g_pDD->SetDisplayMode(640, 480, 8, 0, 0);
if (hRet != DD_OK)
return InitFail(hWnd, hRet, "SetDisplayMode FAILED");
// Create the primary surface with 1 back buffer
ZeroMemory(&ddsd, sizeof(ddsd));
ddsd.dwSize = sizeof(ddsd);
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
//The dwBackBufferCount and ddsCaps member is valid.
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP |
DDSCAPS_COMPLEX;
//DDSCAPS2 structure that contains the capabilities of the surface.
//DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DSCAPS_COMPLEX; 要查看MSDN
ddsd.dwBackBufferCount = 1;//Number of back buffers.
hRet = g_pDD->CreateSurface(&ddsd, &g_pDDSPrimary, NULL);
if (hRet != DD_OK)
return InitFail(hWnd, hRet, "CreateSurface FAILED");
// Get a pointer to the back buffer
ZeroMemory(&ddscaps, sizeof(ddscaps));
ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
hRet = g_pDDSPrimary->GetAttachedSurface(&ddscaps, &g_pDDSBack);
if (hRet != DD_OK)
return InitFail(hWnd, hRet, "GetAttachedSurface FAILED");
// Create a timer to flip the pages
if (TIMER_ID != SetTimer(hWnd, TIMER_ID, TIMER_RATE, NULL))
return InitFail(hWnd, hRet, "SetTimer FAILED");
return DD_OK;
}
因为中间有很调用的函数和变量,所以要在程序中定义。
/*******************************************************************
//局部定义
//*******************************************************************
#define NAME "DDExample1"
#define TITLE "Direct Draw Example 1"
//*******************************************************************
//全局变量
//*******************************************************************
LPDIRECTDRAW7 g_pDD = NULL; // DirectDraw object
LPDIRECTDRAWSURFACE7 g_pDDSPrimary = NULL;// DirectDraw primary surface
LPDIRECTDRAWSURFACE7 g_pDDSBack = NULL; // DirectDraw back surface
BOOL g_bActive = FALSE; // Is application active?
//*******************************************************************
// 默认设置
//*******************************************************************
#define TIMER_ID 1
#define TIMER_RATE 500
函数部分
//******************************************************************/
// Name: InitFail()
// Desc: This function is called if an initialization function fails
//这个函数在我所学的代码里面没有。但在Win32应用程序入口函数里有代码实现了这个功能。
/*if ( !InitDDraw())
{
MessageBox(GetActiveWindow(),L"初始化DirectDraw过程中出错!",L"Error", MB_OK );
FreeDDraw();
DestroyWindow(GetActiveWindow());
return FALSE;
}*/
//******************************************************************/
HRESULT
InitFail(HWND hWnd, HRESULT hRet, LPCTSTR szError,...)
{
char szBuff[128];
va_list vl;
va_start(vl, szError); //va在这里是variable-argument(可变参数)的意思.
vsprintf(szBuff, szError, vl);
ReleaseAllObjects();
MessageBox(hWnd, szBuff, TITLE, MB_OK);
DestroyWindow(hWnd);
va_end(vl);
return hRet;
}
//这里的关于可变参数的问题我没有深入。好烦的。
//******************************************************************/
// Name: ReleaseAllObjects()
// Desc: Finished with all objects we use; release them
// 这里的代码跟我所学的代码差不多,只是符号有所变化。
//******************************************************************/
static void
ReleaseAllObjects(void)
{
if (g_pDD != NULL) //g_pDD is DirectDraw object
{
if (g_pDDSPrimary != NULL) //g_pDDSPrimary is DirectDraw primary surface
{
g_pDDSPrimary->Release();
g_pDDSPrimary = NULL;
}
g_pDD->Release();
g_pDD = NULL;
}
}
5:这里我就不接着写了,写几个关键的地方,
第一个就是函数的顺序的问题。
第二个要在Project->Configuration Properties->General->Character Set,选择“Use Unicode Character Set”就是使用Uncode字符集,选择” Use Multi-Byte Character Set”就是多字节字符集。
第三个Project->Configuration Properties->linker->additional dependenices ->dxguid.lib ddraw.lib 加入这两个库
第四个,注意修改int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine, //这个参数的类型要改一下
int nCmdShow ) //这个参数的名字要改一下