为了正常的体验网站,请在浏览器设置里面开启Javascript功能!
首页 > mfc_openGL学习笔记

mfc_openGL学习笔记

2018-04-10 50页 doc 146KB 14阅读

用户头像

is_569018

暂无简介

举报
mfc_openGL学习笔记mfc_openGL学习笔记 MFC_OPENGL学习笔记 ^l : 手动换行符,即一个向下箭头,产生办法 shift + enter(回车)替换为“”(空。) ^p:自动换行符, 产生办法:enter(回车) 目录 像素格式PIXELFORMATDESCRIPTOR ................................................................................... 2 ->GetSafeHdc()获得窗口句柄 .......................
mfc_openGL学习笔记
mfc_openGL学习笔记 MFC_OPENGL学习笔记 ^l : 手动换行符,即一个向下箭头,产生办法 shift + enter(回车)替换为“”(空。) ^p:自动换行符, 产生办法:enter(回车) 目录 像素格式PIXELFORMATDESCRIPTOR ................................................................................... 2 ->GetSafeHdc()获得窗口句柄 .................................................................................................. 3 c++ typedef 指针详细 ................................................................................................ 4 给一个指向成员函数的指针赋值 ................................................................................................... 5 使用typedef................................................................................................................................... 5 void类型的指针 ............................................................................................................................. 6 this指针 .......................................................................................................................................... 8 struct ............................................................................................................................................... 9 DECLARE_MESSAGE_MAP() ................................................................................................... 9 RC与DC的介绍与使用 .............................................................................................................. 10 LPCREATESTRUCT .................................................................................................................... 11 WM_CREATE ............................................................................................................................... 13 hInstance = ((LPCREATESTRUCT) lParam)->hInstance ; ........................................ 14 vc6转vc2005时肯定会出不少错误 ........................................................................................ 14 __thiscall __cdecl和__stdcall ............................................................................................ 15 函数调用方式 ................................................................................................................................. 17 在VISUAL C++ 2008上配置OPENGL开发环境 ............................................................... 21 CMyView* m_pNewView= new CMyView(); .................................................................. 25 控件ID与声明对象map地址 ................................................................................................... 25 UINT .............................................................................................................................................. 26 VC++配置OpenGL .................................................................................................................... 26 CEdit 对像与opengl对象内存冲突 ......................................................................................... 28 CDC与HDC的区别 .................................................................................................................... 28 HDC,CDC,CClientDC的区别和联系 ....................................................................................... 29 SwapBuffers(m_pDC->GetSafeHdc())==SwapBuffers(m_pDC) ........................... 30 cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN; .............................................. 30 SetTimer函数的用法 ................................................................................................................. 30 LPCTSTR是什么类型 ................................................................................................................. 33 Invalidate() UpdateAllViews()与UpdateWindow( ) ....................................................... 33 static_cast < type-id > ( expression ) 用法 ................................................................... 35 VS2008与VC6.0的变化 .......................................................................................................... 36 Mfc通过ado连接数据库............................................................................................................ 37 _variant_t .................................................................................................................................... 38 Sql语句错误导致数据异常 ......................................................................................................... 38 _T ................................................................................................................................................... 38 Eof和Bof的用法 ......................................................................................................................... 39 Ado封装类 .................................................................................................................................... 40 像素格式PIXELFORMATDESCRIPTOR 明确了OpenGL绘制平面的特性,如象素缓冲区是单缓冲还是双缓冲,数据是 RGBA方式还是Color Index 方式等。每个OpenGL显示设备一般用名为PIXELFORMATDESCRIPTOR的结构来表示某个的像素格式, 这个结构包含26个属性信息。Win32定义PIXELFORMATDESCRIPTOR如下所示: typedef struct tagPIXELFORMATDESCRIPTOR { // pfd WORD nSize; WORD nVersion; DWORD dwFlags; BYTE iPixelType; BYTE cColorBits; BYTE cRedBits; BYTE cRedShift; BYTE cGreenBits; BYTE cGreenShift; BYTE cBlueBits; BYTE cBlueShift; BYTE cAlphaBits; BYTE cAlphaShift; BYTE cAccumBits; BYTE cAccumRedBits; BYTE cAccumGreenBits; BYTE cAccumBlueBits; BYTE cAccumAlphaBits; BYTE cDepthBits; BYTE cStencilBits; BYTE cAuxBuffers; BYTE iLayerType; BYTE bReserved; DWORD dwLayerMask; DWORD dwVisibleMask; DWORD dwDamageMask; } PIXELFORMATDESCRIPTOR; nSize是象素格式描述子结构的大小,sizeof(PIXELFORMATDESCRIPTOR)设定其值; nVersion是PIXELFORMATDESCRIPTOR结构的版本,一般设为1; dwFlags是一组表明象素缓冲特性的标志位,如缓冲是否支持GDI或OpenGL等; iPixelType 说明象素数据类型是RGBA还是颜色索引; cColorBits 每个颜色缓冲区中颜色位平面的数目,对颜色索引方式是缓冲区大小; cRedBits 每个RGBA颜色缓冲区中红色位平面的数目; cRedShift 每个RGBA颜色缓冲区中红色位平面的偏移数; cGreenBits 每个RGBA颜色缓冲区中绿色位平面的数目; cGreenShift每个RGBA颜色缓冲区中绿色位平面的偏移数; cBlueBits 每个RGBA颜色缓冲区中蓝色位平面的数目; cBlueShift 每个RGBA颜色缓冲区中蓝色位平面的偏移数; cAlphaBits 每个RGBA颜色缓冲区中alpha位平面的数目(保留的,现不支持); cAlphaShift每个RGBA颜色缓冲区中alpha位平面的偏移数(保留的,现不支持); cAccumBits 累加缓冲区中全部位平面的数目; cAccumRedBits 累加缓冲区中红色位平面的数目; cAccumGreenBits累加缓冲区中绿色位平面的数目; cAccumBlueBits 累加缓冲区中蓝色位平面的数目; cAccumAlphaBits累加缓冲区中alpha位平面的数目; cDepthBits Z(深度)缓冲区的深度; cStencilBits 模板缓冲区的深度; cAuxBuffers 轴向缓冲区的数量(一般1.0版本不支持); iLayerType 被忽略,为了一致性而包含的; bReserved 表层和底层平面的数量::位0-3表最多15层表层平面,位4-7表底层; dwLayerMask 被忽略,为了一致性而包含的; dwVisibleMask 是透明色彩的值(RGBA方式)或是一个底层平面的索引(Index); dwDamageMask被忽略,为了一致性而包含的 ->GetSafeHdc()获得窗口句柄 CScrollView::OnDraw(CDC* pDC) { HDC hdc1 = ::GetDC(GetSafeHwnd()); HDC hdc2 = pDC->GetSafeHdc(); } hdc1 和hdc2有和区别; 我用hdc1画图,操作滚动条可以自动移动图形,但是DPtoLP不对,结果总是正值; 我用hdc2画图,操作滚动条不能自动移动图形,但是DPtoLP正确; CScrollView::OnDraw(CDC* pDC) { HDC hdc1 = ::GetDC(GetSafeHwnd()); //GetSafeHwnd()得到的是View的句柄 HDC hdc2 = pDC->GetSafeHdc(); //得到的是客户区DC的句柄 } 不是在OnDraw()里的话,就相同,如: CDC* pDC = GetDC(); HDC hdc1 = ::GetDC(GetSafeHwnd()); HDC hdc2 = pDC->GetSafeHdc(); hdc1和hdc2作用一样 在OnDraw()里直接使用OnDraw()的参数pDC就不一样了,没记错的话,那个pDC是裁剪过的 c++ typedef 函数指针详细说明 函数指针 一个函数在编译时被分配一个入口地址,将这个入口地址称为函数的指针,可 以用一个指针变量指向该函数指针,然后通过该变量来调用函数。 有关说明: 1、函数指针的声明格式: 函数返回值类型(,指针变量名)(参数类型列表) 或者是: typedef 函数返回值类型 (,指针变量名)(参数类型列表) 2、一个函数指针只能指向一种类型的函数,即具有相同的返回值和相同的参 数的函数 ,、关于函数指针的加减运算没有意义 函数指针数组定义: 函数定义: void fun1(void *p); void fun2(void *p); void fun3(void *p); 函数指针数组定义: void(*fun[3])(void*); //typedef void(*pfun)(void*);pfun fun[3]; 指针赋值: fun[0] = fun1; fun[1] = fun2; fun[2] = fun3; 函数调用: fun[0](&a); //int a; fun[1](&b); //int b; fun[3](&c); //int c; 声明一个指向成员函数的指针 一个指向成员函数的指针包括成员函数的返回类型,带::符号的类名称,函数参数表。虽然这一语法看似复杂,其实它和普通的指针是一样的。指向外部函数的指针可如下声明: void (*pf)(char *, const char *); void strcpy(char * dest, const char * source); pf=strcpy; 相应指向类A的成员函数的指针如下表示: void (A::*pmf)(char *, const char *); 以上pmf是指向类A的一个成员函数的指针,传递两个变量char *和 const char *,没有返回值。注意星号前面的A::符号,这和前面的声明是一致的。 给一个指向成员函数的指针赋值 为了给一个指向成员函数的指针赋值,可以采用成员函数名并再其前面加一个&的方式 使用typedef 你可以使用typedef来隐藏一些指向成员函数的复杂指针。例如,下面的代码定义了一个类A中的成员函数的指针PMA,并传递char *和const char *参数。 typedef void(A::*PMA)(char *, const char *); PMA pmf= &A::strcat; // use a typedef to define a pointer to member 使用typedef特别有用,尤其是对于指向成员函数的数组指针。 void类型的指针 void含义: void是“无类型”,void*则为无类型指针,void*可以指向任何类型的数据。 void a;//此变量没有任何实际意义,无法编译通过“illegal use of type” void 的作用: 1、对程序返回的限定 2、对函数参数的限定 我们知道,如何指针p1和p2的类型相同,那么我们可以直接在p1和p2间赋值,如果不同,必须使用强制类型转换。 如:float *p1; int *p2; 若:p1 = p2; 编译出错:“can not covert from int* to float*” 必须为:p1 = (float*)p2; 而void*不同,任何类型的指针都可以直接赋为它,不需要强制类型转换: 如:void *p1; int *p2; 可作:p1 =p2; 无类型可以包容有类型,有类型不能包容无类型: 必须为:p2 = (int*)p1; viod 和 void*使用规则总结: ? 如果函数没有返回值,那么应声明为void类型 在C语言中,凡不加返回值类型限定的函数,就会被编译器作为返回整型值处理。但是许多程序员却误以为其为void类型. 林锐博士《高质量C/C++编程》中提到:“C++语言有很严格的类型安全检查,不允许上 述情况(指函数不加类型声明)发生”。可是编译器并不一定这么认定,譬如在Visual C++6.0中上述add函数的编译无错也无警告且运行正确,所以不能寄希望于编译器会做严格的类型检查。 因此,为了避免混乱,我们在编写C/C++程序时,对于任何函数都必须一个不漏地指定其类型。如果函数没有返回值,一定要声明为void类型。这既是程序良好可读性的需要,也是编程规范性的要求。另外,加上void类型声明后,也可以发挥代码的“自注释”作用。代码的“自注释”即代码能自己注释自己。 ? 如果函数无参数,那么应声明其参数为void ? 小心使用void指针类型 按照ANSI的,不能对void指针进行算法操作,即下列操作是不合法的: void *pvoid; pvoid ++; //ansi错误 pvoid += 1; //ansi 错误 ansi标准之所以这样认定,是因为它坚持,进行算法操作的指针必须是确定知道其指向数据类型的大小的。 但GUN(GUN’s Not Unix)则不这么认为,它指定void*的算法操作与char*一致。因此在GUN编译器中上述语句是正确的。 在实际的程序中,为了迎合ansi标准,并提高程序的可移植性,我们可以这样实现同样功能的代码: void *pvoid; (char*)pvoid++; (char*)pvoid += 1; ? 如果函数的参数可以是任意类型指针,那么应声明其参数为void * 典型的如内存操作函数memcpy和memset的函数原型分别为: void* memcpy(void *dest, const void *src, size_t len); void* memset(void *buffer,int c, size_t num); 这样,任何类型的指针都可以传入memcpy和memset中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不论内存是什类型。 ? void不能代表一个真实的变量 void a; //错误 function(void a); //错误 this指针 《深入浅出MFC》中解释: 定义类CRect,定义两个对象rect1、rect2,各有自己的m_color成员变量,但rect1.setcolor和rect2.setcolor 却都是通往唯一的CRect::setcolor成员函数,那么CRect::setcolor如何处理不同对象的m_color,答案是由一个隐藏参数,名为this指针。当你调用: rect1.setcolro(2); rect2.setcolor(3); 时,编译器实际上为你做出来一下的代码: CRect::setcolor(2,(CRect*)&rect1); CRect::setcolor(3,(CRect*)&rect2); 多出来的参数,就是所谓的this指针。 class CRect { …… public: void setcolor(int color){m_color = color}; }; 被编译后,其实为: class CRect { …… public: void setcolor(int color,(CRect*)this){this->m_color = color}; }; struct (1) struct{ int x; int y; }test1; 好,定义了 结构 test1, test1.x 和 test1.y 可以在语句里用了。 (2) struct test {int x; int y; }test1; 好,定义了 结构 test1, test1.x 和 test1.y 可以在语句里用了。 与 1 比,省写 了 test (3) typedef struct test {int x; int y; // 你漏打分号,给你添上 }text1,text2; 只说了 这种结构 的(类型)别名 叫 text1 或叫 text2 真正在语句里用,还要写: text1 test1; 然后好用 test1.x test1.y 或写 text2 test1; 然后好用 test1.x test1.y (4)type struct {int x; int y; }test1; 这个不可以。 改 typedef ... 就可以了。 但也同 (3)一样,还要 写: test1 my_st; 才能用 my_st.x 和 my_st.y DECLARE_MESSAGE_MAP() 说明: 用户程序中的每个CCmdTarget派生类必须提供消息映射以处理消息。在类定义的末尾使用DECLARE_MESSAGE_MAP宏。接着,在定义类成员函数的.CPP文件中,使用BEGIN_MESSAGE_MAP 宏,每个用户消息处理函数的宏项下面的列表以及END_MESSAGE_MAP宏。 注释: 如果在DECLARE_MESSAGE_MAP之后定义任何一个成员,那么必须为他们指定一个新存取类型(公共的,私有的,保护的)。 我(转载)觉得他描述得欠妥,我的理解是: 只要有:只要是CCmdTarget(用于所有能够消息映射的基类)派生类,必有消息映射以处理消息,则在类的说明文件的尾部有DECLARE_MESSAGE_MAP宏,在类的定义文件中有BEGIN_MESSAGE_MAP宏和END_MESSAGE_MAP宏以处理用户消息。 其中BEGIN_MESSAGE_MAP(参数1,参数2) ,参数1为该类的类名,参数2为该类基类的类名。 其中ON_MESSAGE(参数1,参数2),参数1为响应的消息,参数2为该消息对应的处理的函数名。 RC与DC的介绍与使用 OpenGL的绘图方式与Windows一般的绘图方式是不同的,主要区别如下: 1、Windows采用的是GDI在设备描述表DC上进行绘图。 2、OpenGL采用的是OpenGL相关的函数在渲染描述表RC上进行绘图。 3、OpenGL使用的是特殊的像素格式。 在Windows中使用GDI绘图时必须指定在哪个设备环境DC中绘制,同同样的在使用OpenGL函数时也必须指定一个所谓的渲染环境。正如DC要存储GDI的绘制环境信息如笔,刷和字体等,RC也必须保存OpenGL所需的渲染信息如像素格式等。 渲染环境主要由以下六个wgl函数来管理: 1、HGLRC wglCreateContext( HDC hdc ) 该函数用来创建一个OpenGL可用的渲染环境。hdc必须是一个合法的支持至少16位色的屏幕设备描述表DC或内存设备描述表的句柄。该函数在调用前,设备描述表必须设置好适当的像素格式。成功创建渲染描述表之后,hdc可以释放或删除。函数返回NULL值表示失败,否则返回值为渲染上下文的句柄。 2、BOOL wglDeleteContex( HGLRC hglrc ) 该函数删除一个RC,一般应用程序在删除RC之前,应使它成为非现行RC,不过,删除一个现行RC也是可以得。此时,OpenGL系统冲掉等待的绘图命令并使之成为非现行RC,后删除之。注意:删除一个属于别的线程的RC时会导致失败。 3、HGLRC wglGetCurrentcontext( void ) 该函数返回线程的现行RC,如果线程无现行RC则返回NULL 4、HDC wglGetCurrentDC( void ) 该函数返回与线程现行RC关联的DC,如果线程无现行RC则返回NULL。 5、BOOL wglMakeCurrent( HDC hdc, HGLRC hglrc ) 该函数把hdc和hglrc关联起来,并使hglrc成为调用线程的现行RC,如果传给hglrc的值为NULL,则函数解除关联,并置线程的现行RC为非现行RC,测试忽略hdc参数。 注意:传给该函数的hdc可以不是调用wglCreateContext时使用的值,但是,他们所关联的设备必须相同并且拥有相同的像素格式。一个RC可以由几个现行RC,这是针对调用线程而言的。一个线程在拥有现行RC进行绘图时,别的现场将无法同时绘图。一个线程一次只能拥有一个现行的RC,但是可以拥有多个RC; 一个RC也可以由多个线程共享,但是他每次只能在一个线程中试现行RC。在使用RC时,不应该释放或者删除与之关联的DC。如果应用程序在整个生命期内保持一个现行RC,则应用程序也一直占有一个DC资源。Windows系统只有有限的DC资源。 管理RC与DC两种方法 1、在WM_CREATE消息响应时创建RC,创建后立即释放DC;当WM_PAINT消息到来时,程序再获取DC句柄,并与RC关联起来,绘图完成后,立即解除RC与DC得关联并释放DC;当WM_DESTROY消息到来时,程序只需简单的删除RC。 2、RC在程序开始时创建并使之成为现行RC。它将保持为现行RC直至程序结束。相应的,GETDC在程序开始调用,RELEASEDC在程序结束时才调用。此中方法的好处是在相应WM_PAINT消息时,无需调用十分耗时的wglMakeCurrent函数。一般它要消耗几千个时钟周期。 LPCREATESTRUCT LPCREATESTRUCT是一个指向结构CREATESTRUCT的指针.以下是该结构的信息: The CREATESTRUCT structure defines the initialization parameters passed to the window procedure of an application. typedef struct tagCREATESTRUCT { // cs LPVOID lpCreateParams; HINSTANCE hInstance; HMENU hMenu; HWND hwndParent; int cy; int cx; int y; int x; LONG style; LPCTSTR lpszName; LPCTSTR lpszClass; DWORD dwExStyle; } CREATESTRUCT; cy Specifies the height of the new window, in pixels. cx Specifies the width of the new window, in pixels. y Specifies the y-coordinate of the upper left corner of the new window. If the new window is a child window, coordinates are relative to the parent window. Otherwise, the coordinates are relative to the screen origin. x Specifies the x-coordinate of the upper left corner of the new window. If the new window is a child window, coordinates are relative to the parent window. Otherwise, the coordinates are relative to the screen origin. style Specifies the style for the new window. lpszName Pointer to a null-terminated string that specifies the name of the new window. lpszClass Pointer to a null-terminated string that specifies the class name of the new window. dwExStyle Specifies the extended style for the new window. Remarks Windows NT: You should access the data represented by the lpCreateParams member using a pointer that has been declared using the UNALIGNED type, because the pointer may not be DWORD aligned. This is demonstrated in the following example: typedef struct tagMyData { // Define creation data here. } MYDATA; typedef struct tagMyDlgData { SHORT cbExtra; MYDATA myData; } MYDLGDATA, UNALIGNED *PMYDLGDATA; PMYDLGDATA pMyDlgdata = (PMYDLGDATA) (((LPCREATESTRUCT) lParam)->lpCreateParams); WM_CREATE The WM_CREATE message is sent when an application requests that a window be created by calling the CreateWindowEx or CreateWindow function. The window procedure of the new window receives this message after the window is created, but before the window becomes visible. The message is sent before the CreateWindowEx or CreateWindow function returns. lpcs = (LPCREATESTRUCT) lParam; // structure with creation data Parameters lParam Value of lParam. Pointer to a CREATESTRUCT structure that contains information about the window being created. The members of CREATESTRUCT are identical to the parameters of the CreateWindowEx function. Return Values If an application processes this message, it should return 0 to continue creation of the window. If the application returns –1, the window is destroyed and the CreateWindowEx or CreateWindow function returns a NULL handle. hInstance = ((LPCREATESTRUCT) lParam)->hInstance ; 这句代码就是实例化前面一个hInstance. CREATESTRUCT这个结构体里的成员和CreateWindow里的参数是一样的。 这个结构主要是用来修改应用界面的外观的。 在创建窗口时,通常是响应窗口的WM_CREATE消息来创建子窗口和控件,而CreateWindow需要给出hInstance参数,你这行代码就是从CREATESTRUCT结构中取出hInstance参数。 vc6转vc2005时肯定会出不少错误 很多消息的宏都变了。参数和返回值不同了。 error C2440: “static_cast”: 无法从“void (__cdecl *)(UINT,CPoint)”转换为“void (__thiscall CWnd::* )(UINT,CPoint)” 看起来你写的是 void (__thiscall CWnd::* )(UINT,CPoint) 但实际是 void (__cdecl *)(UINT,CPoint) 不能转换为 void (__thiscall CWnd::* )(UINT,CPoint) 所以出现错误。 一般要查看该CPP文件的消息映射宏位置, 的BEGIN_MESSAGE_MAP(CXXX, CDialog) //{{AFX_MSG_MAP(CXXX) ON_WM_SYSCOMMAND() //}}AFX_MSG_MAP END_MESSAGE_MAP() 和该CPP文件的相关头文件,看看vc2005对 afx_msg 那些行的要求。 // Implementation protected: // Generated message map functions //{{AFX_MSG(CXXX) afx_msg void OnSysCommand(UINT nID, LPARAM lParam); virtual BOOL OnInitDialog(); //}}AFX_MSG DECLARE_MESSAGE_MAP() 错误 1 error C2440: “static_cast”: 无法从“void (__thiscall openGL::* )(LPCREATESTRUCT)”转换为“int (__thiscall CWnd::* )(LPCREATESTRUCT)” d:\my documents\visual studio 2008\projects\opengltest\opengltest\opengl.cpp 24 __thiscall __cdecl和__stdcall 函数调用约定有多种,这里简单说一下: 1、__stdcall调用约定相当于16位动态库中经常使用的PASCAL调用约定。在32位的 VC++5.0中PASCAL调用约定不再被支持(实际上它已被定义为__stdcall。除了__pascal 外,__fortran和__syscall也不被支持),取而代之的是__stdcall调用约定。两者实 质上是一致的,即函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参 数的内存栈,但不同的是函数名的修饰部分(关于函数名的修饰部分在后面将详细说 明)。 _stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的 压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀, 在函数名后加上"@"和参数的字节数。 在SDK中输出API函数的时候,经常会利用WINAPI对函数进行约定,WINAPI在WIN32中, 它被定义为__stdcall 2、C调用约定(即用__cdecl关键字说明)按从右至左的顺序压参数入栈,由调用者把 参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数 的函数只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。 _cdecl是C和C,,程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码, 所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方 式。VC将函数编译后会在函数名前面加上下划线前缀。是MFC缺省调用约定。 3、__fastcall调用约定是“人”如其名,它的主要特点就是快,因为它是通过寄存器 来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下 的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函 数名修饰约定方面,它和前两者均不同。 _fastcall方式的函数采用寄存器传递参数,VC将函数编译后会在函数名前面加上"@"前 缀,在函数名后加上"@"和参数的字节数。 4、thiscall仅仅应用于“C++”成员函数。this指针存放于CX寄存器,参数从右到左 压。thiscall不是关键词,因此不能被程序员指定。 5、naked call采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来 保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。 naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同 使用。 关键字 __stdcall、__cdecl和__fastcall可以直接加在要输出的函数前,也可以在编 译环境的Setting...\C/C++ \Code Generation项选择。当加在输出函数前的关键字与 编译环境中的选择不同时,直接加在输出函数前的关键字有效。它们对应的命令行参数 分别为/Gz、/Gd和/Gr。缺省状态为/Gd,即__cdecl。 __thiscall是关于类的一种调用方式,它与其他调用方式的最大区别是: __thiscall对每个函数都增加了一个类指针参数 class aa { void bb(int cc); }; 实际上bb的函数原形是void bb(aa &this, int cc); 这就是__thiscall的调用方式 函数调用方式 我们知道在进行函数调用时,有几种调用方法,主要分为C式,Pascal式.在C和C++中 C式调用是缺省的,类的成员函数缺省调用为_stdcall。二者是有区别的,下面我们用 实例说明一下: 1. __cdecl :C和C++缺省调用方式 例子: void Input( int &m,int &n);/*相当于void __cdecl Input(int &m,int &n);*/ 以下是相应的汇编代码: 00401068 lea eax,[ebp-8] ;取[ebp-8]地址(ebp-8),存到eax 0040106B push eax ;然后压栈 0040106C lea ecx,[ebp-4] ;取[ebp-4]地址(ebp-4),存到ecx 0040106F push ecx ;然后压栈 00401070 call @ILT+5(Input) (0040100a);然后调用Input函数 00401075 add esp,8 ;恢复栈 从以上调用Input函数的过程可以看出:在调用此函数之前,首先压栈ebp-8,然 后压栈ebp-4,然后调用函数Input,最后Input函数调用结束后,利用esp+8恢复栈。由 此可见,在C语言调用中默认的函数修饰_cdecl,由主调用函数进行参数压栈并且恢复 堆栈。 下面看一下:地址ebp-8和ebp-4是什么, 在VC的VIEW下选debug windows,然后选Registers,显示寄存器变量值,然后在 选debug windows下面的Memory,输入ebp-8的值和ebp-4的值(或直接输入ebp-8和-4), 看一下这两个地址实际存储的是什么值,实际上是变量 n 的地址(ebp-8),m的地址 (ebp-4),由此可以看出:在主调用函数中进行实参的压栈并且顺序是从右到左。另外, 由于实参是相应的变量的引用,也证明实际上引用传递的是变量的地址(类似指针)。 总结:在C或C++语言调用中默认的函数修饰_cdecl,由主调用函数进行参数压栈并且 恢复堆栈,实参的压栈顺序是从右到左,最后由主调函数进行堆栈恢复。由于主调用 函数管理堆栈,所以可以实现变参函数。另外,命名修饰方法是在函数前加一个下划 线(_). 2. WINAPI (实际上就是PASCAL,CALLBACK,_stdcall) 例子: void WINAPI Input( int &m,int &n); 看一下相应调用的汇编代码: 00401068 lea eax,[ebp-8] 0040106B push eax 0040106C lea ecx,[ebp-4] 0040106F push ecx 00401070 call @ILT+5(Input) (0040100a) 从以上调用Input函数的过程可以看出:在调用此函数之前,首先压栈ebp-8,然 后压栈ebp-4,然后调用函数Input,在调用函数Input之后,没有相应的堆栈恢复工作 (为其它的函数调用,所以我没有列出) 下面再列出Input函数本身的汇编代码:(实际此函数不大,但做汇编例子还是大了些, 大家可以只看前和后,中间代码与此例子无关) 39: void WINAPI Input( int &m,int &n) 40: { 00401110 push ebp 00401111 mov ebp,esp 00401113 sub esp,48h 00401116 push ebx 00401117 push esi 00401118 push edi 00401119 lea edi,[ebp-48h] 0040111C mov ecx,12h 00401121 mov eax,0CCCCCCCCh 00401126 rep stos dword ptr [edi] 41: int s,i; 42: 43: while(1) 00401128 mov eax,1 0040112D test eax,eax 0040112F je Input+0C1h (004011d1) 44: { 45: printf("\nPlease input the first number m:"); 00401135 push offset string "\nPlease input the first number m"... (004260b8) 0040113A call printf (00401530) 0040113F add esp,4 46: scanf("%d",&m); 00401142 mov ecx,dword ptr [ebp+8] 00401145 push ecx 00401146 push offset string "%d" (004260b4) 0040114B call scanf (004015f0) 00401150 add esp,8 47: 48: if ( m= s ) 004011B3 mov eax,dword ptr [ebp+8] 004011B6 mov ecx,dword ptr [eax] 004011B8 cmp ecx,dword ptr [ebp-4] 004011BB jl Input+0AFh (004011bf) 57: break; 004011BD jmp Input+0C1h (004011d1) 58: else 59: printf(" m < n*(n+1)/2,Please input again!\n"); 004011BF push offset string " m < n*(n+1)/2,Please input agai"... (00426060) 004011C4 call printf (00401530) 004011C9 add esp,4 60: } 004011CC jmp Input+18h (00401128) 61: 62: } 004011D1 pop edi 004011D2 pop esi 004011D3 pop ebx 004011D4 add esp,48h 004011D7 cmp ebp,esp 004011D9 call __chkesp (004015b0) 004011DE mov esp,ebp 004011E0 pop ebp 004011E1 ret 8 最后,我们看到在函数末尾部分,有ret 8,明显是恢复堆栈,由于在32位C++中, 变量地址为4个字节(int也为4个字节),所以弹栈两个地址即8个字节。 由此可以看出:在主调用函数中负责压栈,在被调用函数中负责恢复堆栈。因此不能 实现变参函数,因为被调函数不能事先知道弹栈数量,但在主调函数中是可以做到的, 因为参数数量由主调函数确定。 下面再看一下,ebp-8和ebp-4这两个地址实际存储的是什么值,ebp-8地址存储的是n 的值,ebp -4存储的是m的值。说明也是从右到左压栈,进行参数传递。 总结:在主调用函数中负责压栈,在被调用函数中负责弹出堆栈中的参数,并且 负责恢复堆栈。因此不能实现变参函数,参数传递是从右到左。另外,命名修饰方法 是在函数前加一个下划线(_),在函数名后有符号(@),在@后面紧跟参数列表中的参数 所占字节数(10进制),如:void Input(int &m,int &n),被修饰成:_Input@8 对于大多数api函数以及窗口消息处理函数皆用 CALLBACK ,所以调用前,主调函数会 先压栈,然后api函数自己恢复堆栈。 如: push edx push edi push eax push ebx call getdlgitemtexta 在VISUAL C++ 2008上配置OPENGL开发环境 这篇文章写的太有用了~我想配VC2008的OpenGL,添加了5个文件后仍然找不到glaux.h,很是着急啊,直到这篇文章解救了我。 ~~~非常感谢~ ----------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------ 应该在VISUAL C++ 2005/VC 6.0/VC7.0同样都适用 首先感谢网上写VC6.0配置OPENGL开发环境的作者,我是先在你那里学习了如何配置,只是做了一点小的延伸,主要内容还是你的。 第一步:下载OpenGL的GLUT库 Windows环境下的GLUT下载地址:(苹果机不需要安装,自带) 第二步:OpenGL库和配置文件 OpenGL库配置用到的文件分为下面三类: ? 动态链接库文件(.dll) glaux.dll, glu32.dll, glut32.dll, OPENGL32.DLL, glut.dll。 ? 头文件(.h) GL.H, GLAUX.H, GLU.H, glut.h。 ? 库文件(.lib) GLAUX.LIB、Glu32.lib、glut32.lib、Opengl32.lib,glut.lib。 其中opengl32.dll, glaux.dll,glu32.dll是安装显卡驱动自带,应该每个系统里面都有,如果没有重新安装显卡驱动。 其中glut32.dll, glut.dll, glut.h, glut32.lib, glut.lib 是在刚才那个地址下载的,打开压缩包后会有5个文件 下面就是区别了,VC++ 2008不带GL.H, GLAUX.h, glu.h, glaux.lib, glu32.lib, opengl32.lib这些文件要在网上下载或者在VC6.0里面拷贝出来, 如果想要全套的文件,给我发邮件我给你发xudongcui@gmail.com 第三步:Windows下配置OpenGL 把glut32.dll, glut.dll拷贝到C:\WINDOWS\system32目录下,system32目录下应该已经有 opengl32.dll, glu32.dll了。 把GL.H, GLAUX.h, glu.h, glut.h 拷贝到 C:\Program Files\Microsoft Visual Studio 9.0\VC\inclu de\gl 把 GLAUX.LIB、Glu32.lib、glut32.lib、Opengl32.lib,glut.lib拷贝到 C:\Program Files\Microsoft Visual Studio 9.0\VC\lib 第四步:建立VC++2008 打开VC++2008,选择新建工程,Win32,Win32控制台应用程序。删除默认的所有代码,用如下代码替换。 #include "stdafx.h" #include #include #include #include #include void background(void) { //设置背景颜色为黑色 glClearColor(0.0,0.0,0.0,0.0); } void myDisplay(void) { //buffer设置为颜色可写 glClear(GL_COLOR_BUFFER_BIT); //开始画三角形 glBegin(GL_TRIANGLES); //设置为光滑明暗模式 glShadeModel(GL_SMOOTH); //设置第一个顶点为红色 glColor3f(1.0,0.0,0.0); //设置第一个顶点的坐标为(-1.0,-1.0) glVertex2f(-1.0,-1.0); //设置第二个顶点为绿色 glColor3f(0.0,1.0,0.0); //设置第二个顶点的坐标为(0.0,-1.0) glVertex2f(0.0,-1.0); //设置第三个顶点为蓝色 glColor3f(0.0,0.0,1.0); //设置第三个顶点的坐标为(-0.5,1.0) glVertex2f(-0.5,1.0); //三角形结束 glEnd(); //强制OpenGL函数在有限时间内运行 glFlush(); } void myReshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); //设置视口 glMatrixMode(GL_PROJECTION); //指明当前矩阵为GL_PROJECTION glLoadIdentity(); //将当前矩阵置换为单位阵 if(w <= h) gluOrtho2D(-1.0,1.5,-1.5,1.5*(GLfloat)h/(GLfloat)w); //定义二维正视投影矩阵 else gluOrtho2D(-1.0,1.5*(GLfloat)w/(GLfloat)h,-1.5,1.5); glMatrixMode(GL_MODELVIEW); //指明当前矩阵为GL_MODELVIEW } int main(int argc, char* argv[]) { // 初始化 glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowSize(400,400); glutInitWindowPosition(200,200); //创建窗口 glutCreateWindow("Triangle"); //绘制与显示 background(); glutReshapeFunc(myReshape); glutDisplayFunc(myDisplay); glutMainLoop(); return(0); } CMyView* m_pNewView= new CMyView(); Class myView { public: int m_idView; } void CDynViewsDlg::AddView(VIEW_TYPE viewType) { int idNewView=0; CMyView* m_pNewView= new CMyView(); m_pNewView->m_idView = idNewView; //声明时初始化才能调用构造函数从而使m_idView有值 } 控件ID与声明对象map地址 UINT typedef unsigned int UINT; 表明是否按下各种键的状态标志。UINT类型在WINDOWS API中有定义,它对应于32位无符号整数。 1.在C, C++中不存UINT在这个关键字 UINT类型是unsigned int派生出来的 int是带符号的,表示范围是:-2147483648 到2147483648 uint是不带符号整形,表示范围是0到4294967295(2^32-1),即第一个数字不表示符号 VC++配置OpenGL 在VisualC++6.0中建立一个单文档应用程序框架后,首先添加头文件,打开StdAfx.h文件,加入下列语句: 程序1.3.2 添加头文件 #include“GL\gl.h #include“GL\glu.h #include“GL\glaux.h 运行该程序和运行一般VC 程序一样,只是在连接的时候同样要加入三个静态库—opengl32. lib、glu32. lib 和glaux.lib。具体方法是:在VC ++ 中,用鼠标点取VisualC++6.0中“工程Project”菜单,选取“设置”命令,打开“ Project Settings”对话框,选择“连接”,找到VC 的安装目录,如C: \ Program Files \Microsoft Visual Studio \ VC98 ,选取lib 目录,从中选取opengl32.lib、glu32. lib 和glaux. lib 库文件,在“对象/库模块”中输入“opengl32.lib、glu32.lib、glaux.hb’’,中间用空格隔开,单击OK,加入到应用程序项目中。 (2)建立GDI窗口 任何一个Windows应用程序都必须创建Windows应用窗口,OpenGL程序也如此。在VC ++ 中,当创 建一个项目是会自动产生一个窗口,因此我们不需要再重新定义窗口,而只需在所需的窗口内绘图即可。但若要使用OpenGL 在窗口内绘图,则必须对定义窗口的一些函数进行修改。 OpenGL 窗口必须具有WS_CLIPCHILDREN (创建父窗口使用的Windows 风格,用于重绘时裁剪子窗口所覆盖的区域)或WS_CLIPSIBLINGS(创建子窗口使用的Windows 风格,用于重绘时裁剪其它子窗口所覆盖的区域) 属性。此外,窗口类属性不能包括CS_PARENTDC。具体程序实现如下: 程序1.3.3 建立GDI窗口 B00LCMyView::PreCreateWindow(CREATESTRUCT&cs) { cs.style}=WS_CLIPSIBLINGS}WS_CLIPCHILDREN: return CView::ProCreatewindow(cs); } (3) 窗口初始化 要是窗口支持OpenGL 绘图,必须对窗口进行初始化。 1) 定义象素结构(PIXELFORMAT) 由于OpenGL 窗口要求有自己的象素格式,即只有那些从OpenGL 窗口客户区域获得的Device Contexts (设备描述表,简称DC ,) 才允许在窗口内绘图。函数ChoosePixelFormat 使用有DC 支持的与所给定的PIXELFORMATDESCRIPTOR(象素格式描述) 最相匹配的象素格式,从而可以满足用户所需求的象素格式;函数SetPixelFormat 将DC 中的象素格式置为由参数iPixelFormat 所指定的象素格式。 OpenGL 可采用双缓存技术,所谓双缓存技术是指OpenGL为了获得逼真的动画效果,需要先在内存中生成下一幅图像,然后把已经生成的图像从内存中显示到屏幕上的一种技术。 于是看起来所有的画面都是连续的。在定义象素格式时,可以将PIXELFORMATDESCRIPTOR 结构中的dwFlags 位置为PFD—DOUBLEBUFFER。值得注意的是,在绘制物体结束时,一定要加上SwapBuffers (wglGetCurrentDC() ) 语句,即交换前台和后台缓存。 2) 定义RGB 调色板(PALETTE) OpenGL 应用了一些专门的函数来指定三维模型的颜色,可以选择两种颜色模式:RGBA 模式和颜色索引模式。在RGBA 模式下,所有的颜色定义全用R、G、B 三个值来表示,有时也加上Alpha 值(与透明度有关) ;在颜色索引模式下,每一个象素的颜色使用颜色索引表中的某个颜色索引值表示,而这个索引值指向了相应的R、G、B 值。 3) 创建RC 在VC ++ 中,绘图是离不开DC 的,它是一个定义了Graphic Objects (图形对象) 和Graphic Modes (图形模式) 的数据结构。这里的Graphic Objects 是指用于绘图的pen (画笔) 、brush(绘刷) 、bitmap (位图) 、palette (调色板) 、region (区域) 等一系列资源。若想在指定的窗口内绘图必须找到它的句柄(handle) 。 而在OpenGL 编程中,需要使用Rendering Contexts (绘制描述表,简称RC ,下同) 。RC 并不等同于DC ,它适合于在由hdc(用来产生RC 的DC 的句柄) 指定的设备上画图。绘图时,要先定义DC 的象素格式,然后创建RC,并把它作为当前线程的RC,最后调用OpenGL 函数。当用RC 完成绘图后,必须将它释放。用wglCreateContext 函数来创建一个OpenGL 的RC。用wglMakeCurrent 函数是给定的OpenGL RC 成为当前调用线程的RC ,线程后来的OpenGL 调用均画在有hdc 指定的设备上。 程序1.3.4 设置像素格式,创建绘制描述表 int CMyView::onCreate(LPCREATESTRUCT 1pCreatestruet) { if(CView::onCreate(IPCreatestruet)= = -l) return-l; PIXELFORMATDESCRIPTOR pfd= {sizeof(PIXELF0RMATDESCRIPTOR), 1, //设置版本为1 PFD_DRAW一TO一WINDOW,PFD_DOUBLEBUFFER, PFD_SUPPORT_OPENGL,PFD_DRAW_TO_BITMAP, PFD_SUPPORT_GDI,PFD_STEREO_DONTCARE, //设置像素属性 PFD_TYPE_RGBA, //采用RGBA颜色模式 32, //32位颜色缓冲 8,16,8,8,8,0, //颜色位 0, 0, 64, //64位累加缓存 16,16,16,0, //各颜色累加位 32, //32位深度缓冲 8, //8位模板缓存 0, //主层 PFD_MAIN_PLANE,0, 0,0,0 }; CDC*pdc=GetDC(): //获取当前DC ASSERT(pdc->m_hDC); &Pfd); //返回最匹配的像素格式 intpixelFormat=ChoosePixelFormat(Pdc->m_hDC,BOOL success=SetPixelFormat(Pdc->m_hDC,PixelFormat,&Pfd); //设置像素格式 m_hRc=wglCreateContext(pdc->meshne): //创建一个RC wglMakeCurrent(NULL,NULL); //使当前Re为空,为释放DC做准备 ReleaseDC(pdc): //释放oe return 0; } if(! m_pNewView->Create(NULL, NULL, WS_VISIBLE | WS_CHILD, clientRect,&list1, 1)) { TRACE( "Failed view creation\n" ); } CEdit 对像与opengl对象内存冲突 CDC与HDC的区别 HDC m_hDC : CDC对象使用的输出设备上下文 HDC m_hAttribDC : CDC对象使用的属性设备上下文 二者在CDC对象创建时指向相同的设备上下文。 所需头文件:#include vc 2010-04-29 15:11:39 阅读87 评论0 字号:大中小 订阅 HDC是WINDOWS的一种数据类型,是设备描述句柄。而CDC是MFC里的一个类,它封装了几乎所有的关于HDC的操作。也可以这样说,HDC定义的变量指向一块内存,这内存用来描述一个设备的相关的内容,所以也可以认为HDC定义的是一个指针;而CDC类定义一个对象,这个对象拥有HDC定义的一个设备描述表,同时也包含与HDC相关的操作的函数。这与HPEN和CPen,POINT与CPoint之间的差别是一样的 它们都是DC,HDC就是最原始的 DC 句柄,很多API的第一个参数就是一个HDC类型,比如 HDC hDC = ::GetDC( m_hWnd); ::MoveToEx( hDC, 0,0, NULL ); ::LineTo( hDC, 0, 100, ); ::ReleaseDC( m_hWnd, hDC ); 在MFC中,为了将API封装成一个类来操作,因此多出来了一个CDC。所以在MFC中,都是 CDC dc = GetDC(); dc.MoveTo( 0,0 ); dc.LineTo( 0,100 ); this->ReleaseDC( &dc ); 但这样还不够,因为 CDC还要你自己去释放,所有MFC中又多出来一个CClientDC, 这样你就可以这样了: CClientDC dc(this); dc.MoveTo( 0,0 ); dc.LineTo( 0,100 ); CClientDC的析构函数自己会释放自己。 DC不是什么对象,就是设备上下文的简称。 与CClientDC一样,还有CWindowDC,CPaintDC,只是它们的绘制范围不一样。 但弄到底,都只是HDC的一些封装而已,你可以在CDC类中直接引用 m_hDC,这就是那个原始的HDC句柄了。 HDC,CDC,CClientDC的区别和联系 简而言之,HDC是句柄;CDC是MFC封装的Windows 设备相关的一个类;CClientDC是CDC的衍生类,产生对应于Windows客户区的对象 pDC 是 类指针 HDC 是 windows句柄 通过pDC获得hdc: HDC hdc=pDC->GetSafeHdc(); 通过hdc获得pDC: CDC *pDC=new CDC; pDC->Attach(hdc); HDC是WINDOWS的一种数据类型,是设备描述句柄。 而CDC是MFC里的一个类,它封装了几乎所有的关于 HDC的操作。 也可以这样说,HDC定义的变量指向一块内存,这块 内存用来描述一个设备的相关的内容,所以也可以 认为HDC定义的是一个指针;而CDC类定义一个对象, 这个对象拥有HDC定义的一个设备描述表,同时也包 含与HDC相关的操作的函数。 这与HPEN和CPen,POINT与CPoint之间的差别是一样 的 显然 CDC是类 HDC是句柄 CDC是包了HDC的类 SwapBuffers(m_pDC->GetSafeHdc())==SwapBuffers(m_ pDC) 左边m_pDC是cdc;右边m_pDC是HDC cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN; cs.style后面的竖杠很重要~~~~~~~~~ SetTimer函数的用法 1 )用WM_TIMER来设置定时器 先请看SetTimer这个API函数的原型 UINT_PTR SetTimer( HWND hWnd, // 窗口句柄 UINT_PTR nIDEvent, // 定时器ID,多个定时器时,可以通过该ID判断是哪个定时器 UINT uElapse, // 时间间隔,单位为毫秒 TIMERPROC lpTimerFunc // 回调函数 ); 例如 SetTimer(m_hWnd,1,1000,NULL); //一个1秒触发一次的定时器 在MFC程序中SetTimer被封装在CWnd类中,调用就不用指定窗口句柄了 于是SetTimer函数的原型变为: UINT SetTimer(UINT nIDEvent,UINT nElapse,void(CALLBACK EXPORT *lpfnTimer)(HWND,UINT ,UINT ,DWORD)) 当使用SetTimer函数的时候,就会生成一个计时器。函数中nIDEvent指的是计时器的标识,也就是名字。nElapse指的是时间间隔, 也就是每隔多长时间触发一次事件。第三个参数是一个回调函数,在这个函数里,放入你想要做的事情的代码,你可以将它设定为NULL, 也就是使用系统默认的回调函数,系统默认认的是onTime函数。这个函数怎么生成的呢,你需要在需要计时器的类的生成onTime函数: 在ClassWizard里,选择需要计时器的类,添加WM_TIME消息映射,就自动生成onTime函数了。然后在函数里添加代码,让代码实现功能。 每隔一段时间就会自动执行一次。 SetTimer计时器是系统资源,使用完毕应及时用KillTimer销毁,关于SetTimer的返回值:如果hWnd为NULL,返回值为新建立的timer的ID,如果hWnd非NULL,返回一个非0整数,如果SetTimer调用失败则返回0 ,简言之,SetTimer的返回值用于将来的销毁。 改变计时器的时间间隔 如果想将一个已经存在的计时器设定为不同的时间间隔,可以简单地用不同的时间值再次调用SetTimer。 计时器精确吗, 计时器并不精确。有两个原因: 原因一:Windows计时器是硬件和ROM BIOS架构下之计时器一种相对简单的扩充。回到Windows以前的MS-DOS程序写作环境下,应用程式能够通过拦截者称为timer tick的BIOS中断来实现时钟或计时器。一些为MS-DOS编写的程序自己拦截这个硬件中断以实现时钟和计时器。这些中断每54.915毫秒产生一次,或者大约每秒18.2次。这是原始的IBM PC的微处理器频率值4.772720 MHz被218所除而得出的结果。在Windows 98中,计时器与其下的PC计时器一样具有55毫秒的解析度。在Microsoft Windows NT中,计时器的解析度为10毫秒。Windows应用程式不能以高于这些解析度的频率(在Windows 98下,每秒18.2次,在Windows NT下,每秒大约100次)接收WM_TIMER消息。在SetTimer中指定的时间间隔总是截尾后tick数的整数倍。例如,1000毫秒的间隔除以54.925毫秒,得到18.207个tick,截尾后是18个tick,它实际上是989毫秒。对每个小于55毫秒的间隔,每个tick都会产生一个WM_TIMER消息。 可见,计时器并不能严格按照指定的时间间隔发送WM_TIMER消息,它总要相差那么几毫秒。 即使忽略这几个毫秒的差别,计时器仍然不精确。请看原因二: WM_TIMER消息放在正常的消息队列之中,和其他消息排列在一起,因此,如果在SetTimer中指定间隔为1000毫秒,那么不能保证程序每1000毫秒或者989毫秒就会收到一个WM_TIMER消息。如果其他程序的执行事件超过一秒,在此期间内,您的程式将收不到任何WM_TIMER讯息。事实上, Windows对WM_TIMER消息的处理非常类似于对WM_PAINT消息的处理,这两个消息都是低优先级的,程序只有在消息队列中没有其他消息时才接收它们。 WM_TIMER还在另一方面和WM_PAINT相似:Windows不能持续向消息队列中放入多个WM_TIMER讯息,而是将多余的WM_TIMER消息组合成一个消息。因此,应用程序不会一次收到多个这样的消息,尽管可能在短时间内得到两个WM_TIMER消息。应用程序不能确定这种处理方式所导致的WM_TIMER消息「遗漏」的数目。 可见,WM_TIMER消息并不能及时被应用程序所处理,WM_TIMER在消息队列中的延误可能就不能用毫秒来计算了。 由以上两点,你不能通过在处理WM_TIMER时一秒一秒计数的方法来计时。如果要实现一个时钟程序,可以使用系统的时间函数如GetLocalTime ,而在时钟程序中,计时器的作用是定时调用GetLocalTime获得新的时间并刷新时钟画面,当然这个刷新的间隔要等于或小于1秒。 例: 1) 不用回调函数 SetTimer(1,1000,NULL); 1:计时器的名称; 1000:时间间隔,单位是毫秒; NULL:使用onTime函数。 当不需要计时器的时候调用KillTimer(nIDEvent); 例如:KillTimer(1); 2) 调用回调函数 此方法首先写一个如下格式的回调函数 void CALLBACK TimerProc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime); 然后再用SetTimer(1,100,TimerProc)函数来建一个定时器,第三个参数就是回调函数地址。 或许你会问,如果我要加入两个或者两个以上的 timer怎么办, 继续用SetTimer函数吧,上次的timer的ID是1,这次可以是2,3,4。。。。 SetTimer(2,1000,NULL); SetTimer(3,500,NULL); 嗯,WINDOWS会协调他们的。当然onTimer函数体也要发生变化,要在函数体内添加每一个timer的处理代码: onTimer(nIDEvent) { switch(nIDEvent) { case 1:........; break; case 2:.......; break; case 3:......; break; } } LPCTSTR是什么类型 解释一: LP-长指针 C-Const T-Unicode/ANSI兼容 STR-字符串 解释二: LPCSTR A 32-bit pointer to a constant character string. LPSTR A 32-bit pointer to a character string. LPCTSTR A 32-bit pointer to a constant character string that is portable for Unicode and DBCS. LPTSTR A 32-bit pointer to a character string that is portable for Unicode and DBCS. 解释三: LPCSTR 就是 静态char * 静态8位Windows字符(ANSI)无终结字符串指针 LPCTSTR 就是 静态wchar_t * 如果UNICODE已定义则为LPCWSTR,否则为LPCTSTR L表示long指针, 这是为了兼容Windows 3.1等16位操作系统遗留下来的, 在win32中以及其他的32为操作系统中, long指针和near指针及far修饰符都是为了兼容的作用。没有实际意义。 P表示这是一个指针 C表示是一个常量 T在Win32环境中, 有一个_T宏, 这个宏用来表示你的字符是否使用UNICODE, 如果你的程序定义了UNICODE或者其他相关的宏, 那么这个字符或者字符串将被作为UNICODE字符串,否则就是标准的ANSI字符串。 STR表示这个变量是一个字符串。 所以LPCTSTR就表示一个指向常固定地址的可以根据一些宏定义改变语义的字符串。 同样, LPCSTR就只能是一个ANSI字符串, 在程序中我们大部分时间要使用带T的类型定义。 LPCTSTR == const TCHAR * Invalidate() UpdateAllViews()与UpdateWindow( ) Invalidate()是让程序重画窗口。 UpdateAllViews()是在DOC/VIEW结构中,当一个视图的数据改变后,通知所有视图作相应的改变,和重画毫无关系. Invalidate()是使窗口无效,使系统向其发WM_PAINT消息,使的程序的OnPaint被调用重画客户区。 而UpdateAllViews()是文档与视之间的联系,调用它会使程序与此文档相关的所有视的UpdateView被调用至于是否重画以及怎么画是由各视的UpdateView来决定的。 Invalidate()是Cwnd的成员函数,与DOC-VIEW无关; UpdateAllViews是CDocument的成员函数,具体体现DOC-VIEW的精神。 如果仅重画当前窗口用 this->Invalidate(); 如果通知所有和当前文档相关的窗口重画用GetDocument()->UpdateAllViews()(在View中)或this->UpdateAllViews()(在Doc中)。 Invalidate()函数产生一条WM_PAINT消息,并送入windows消息队列中,是窗口产生重画。 而,UpdateAllViews 并不进入windows消息队列中,直接产生重画 ******************************************* Invalidate函数的总结 InvalidateRect只是增加重绘区域,在下次WM_PAINT的时候才生效 InvalidateRect函数中的参数TRUE表示系统会在你画之前用背景色将所选区域覆盖一次,默认背景色为白色,可以通过设置BRUSH来改变背景色。 Invalidate()之后:(MFC的,顺便了) OnPaint()->OnPrepareDC()->OnDraw() 所以只是刷新在OnPaint()和OnDraw()函数中的绘图语句。其它地方没有影响。 Invalidate标记一个需要重绘的无效区域,并不意味着调用该函数后就立刻进行重绘。类似于PostMessage(WM_PAINT),需要处理到WM_PAINT消息时才真正重绘。以为您Invalidate之后还有其他的语句正在执行,程序没有机会去处理WM_PAINT消息,但当函数执行完毕后,消息处理才得以进行。 Invalidate只是放一个WM_PAINT消息在队列里,不做别的,所以只有当当前函数返回后,进入消息循环,取出WM_PAINT,才执行PAINT,所以不管Invalidate放哪里,都是最后的。 InvalidateRect(hWnd,&rect,TRUE);向hWnd窗体发出WM_PAINT的消息,强制客户区域重绘制, rect是你指定要刷新的区域,此区域外的客户区域不被重绘,这样防止客户区域的一个局部的改动,而导致整个客户区域重绘而导致闪烁,如果最后的参数为TRUE,则还向窗体发送WM_ERASEBKGND消息,使背景重绘,当然在客户区域重绘之前。 UpdateWindow只向窗体发送WM_PAINT消息,在发送之前判断GetUpdateRect(hWnd,NULL,TRUE)看有无可绘制的客户区域,如果没有,则不发送WM_PAINT 如果希望立即刷新无效区域,可以在调用InvalidateRect之后调用UpdateWindow,如果客户区的任一部分无效,则UpdateWindow将导致Windows用WM_PAINT消息调用窗口过程(如果整个客户区有效,则不调用窗口过程)。这一WM_PAINT消息不进入消息队列,直接由WINDOWS调用窗口过程。窗口过程完成刷新以后立刻退出,WINDOWS将控制返回给程序中UpdateWindow调用之后的语句。(windows程序设计第5版 P98) UpdateData()顺便说下,这个函数不是刷新界面用的。 UpdateData();参数为FALSE时,将界面上控件绑定的变量的数据导到控件内,参数为TRUE时,导入方向则相反。 void Invalidate( BOOL bErase = TRUE ); 该函数的作用是使整个窗口客户区无效。窗口的客户区无效意味着需要重绘,例如,如果一个被其它窗口遮住的窗口变成了前台窗口,那么原来被遮住的部分就是无效的,需要重绘。这时Windows会在应用程序的消息队列中放置WM_PAINT消息。MFC为窗口类提供了WM_PAINT的消息处理函数OnPaint,OnPaint负责重绘窗口。视图类有一些例外,在视图类的OnPaint函数中调用了OnDraw函数,实际的重绘工作由OnDraw来完成。参数bErase为TRUE时,重绘区域内的背景将被擦除,否则,背景将保持不变。 它和 UpdateWindow( )区别在于: UpdateWindow( )的作用是使窗口立即重绘。调用Invalidate等函数后窗口不会立即重绘,这是由于WM_PAINT消息的优先级很低,它需要等消息队列中的其它消息发送完后才能被处理。调用UpdateWindow函数可使WM_PAINT被直接发送到目标窗口,从而导致窗口立即重绘。 static_cast < type-id > ( expression ) 用法 该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法: ?用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。 进行上行转换(把派生类的指针或引用转换成基类表示)是安全的; 进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。 ?用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。 ?把空指针转换成目标类型的空指针。 ?把任何类型的表达式转换成void类型。 注意:static_cast不能转换掉expression的const、volitale、或者__unaligned属性。 C++中static_cast和reinterpret_cast的区别 C++primer第五章里写了编译器隐式执行任何类型转换都可由static_cast显示完成;reinterpret_cast通常为操作数的位模式提供较低层的重新解释 1、C++中的static_cast执行非多态的转换,用于代替C中通常的转换操作。因此,被做为隐式类型转换使用。比如: int i; float f = 166.7f; i = static_cast(f); 此时结果,i的值为166。 2、C++中的reinterpret_cast主要是将数据从一种类型的转换为另一种类型。所谓“通常为操作数的位模式提供较低层的重新解释”也就是说将数据以二进制存在形式的重新解释。比如: int i; char *p = "This is a example."; i = reinterpret_cast(p); 此时结果,i与p的值是完全相同的。reinterpret_cast的作用是说将指针p的值以二进制(位模式)的方式被解释为整型,并赋给i,//i 也是指针,整型指针;一个明显的现象是在转换前后没有数位损失。 VS2008与VC6.0的变化 1.MessageBox() VC++6.0:MessageBox("Hello,World!"); VS2008:MessageBox(L"Hello,World!"); 或 MessageBox(TEXT("Hello,World!")); 2.Combo box内容添加方法 VC++6.0:Drop-Down List Box Control 的 Properties 中的 Data 用 Ctrl-Enter 输入 VS2008:更名为 Combo-Box Control ,并在右侧 Data 区域用 semicolons(即';')分隔输入 3.从.net开始就没有classwizard了,全部在属性窗口里了 属性窗口中有,闪电图标及右边的都是,包括事件,消息,虚函数重载,加入变量则是在类标上右击->添加变量…… 4.消息映射 VS2005对消息的检查更为严格,以前在VC6下完全正常运行的消息映射在VS2005下编译不通过 a,ON_MESSAGE(message,OnMyMessage); OnMyMessage返回值必须为LRESULT,其形式为:afx_msg LRESULT OnMyMessage(WPARAM, LPARAM); 如果不符合,则有错误提示: error C2440: “static_cast”: 无法从“void (__thiscall CPppView::* )(WPARAM,LPARAM)”转换为“LRESULT (__thiscall CWnd::* )(WPARAM,LPARAM)” error C2440: “static_cast”: 无法从“void (__thiscall CPppView::* )(void)”转换为“LRESULT (__thiscall CWnd::* )(WPARAM,LPARAM)” b,在VS2005中,OnMyMessage返回值必须为BOOL,且含有一个 UINT 参数指出了命令ID,其形式为:afx_msg BOOL OnMyMessage(UINT);如果不符合,则有错误提示. 如在VS6中,OnMyMessage2的定义为afx_msg BOOL OnViewZoomBar()时亦可正常编译通过,但在VS2005下,有错误提示: error C2440: “static_cast”: 无法从“BOOL (__thiscall CMainFrame::* )(void)”转换为“BOOL (__thiscall CCmdTarget::* )(UINT)” error C2440: “static_cast”: 无法从“BOOL (__thiscall CMainFrame::* )(void)”转换为“BOOL (__thiscall CCmdTarget::* )(UINT)” 5.字符处理 在c中广泛使用的strcpy,strcat,strstr等等推荐使用更为安全strcpy_s,strcat_s,strstr_s等来代替. 6.数学函数检查 VS2005中,数学函数的参数检查更为严格,如pow(2, 45)会引起一个错误提示如下: error C2668: “pow”: 对重载函数的调用不明确 d:\program files\microsoft visual studio 8\vc\include\math.h(575): 可能是“long double pow(long double,int)” d:\program files\microsoft visual studio 8\vc\include\math.h(527): 或“float pow(float,int)” d:\program files\microsoft visual studio 8\vc\include\math.h(489): 或“double pow(double,int)” 试图匹配参数列表“(int, int)”时 正确的使用为pow(2.0, 45) 7.更加符合C++标准 如在VS6中,在FOR循环中的循环变量的定义的作用域延伸到循环体外,VS2005则修正了这样的bug。 VC6: for(int i=0;i<100;i++)f2(); for(i = 1;i<10;i++)f1(); //i已经定义 而有VS2005中,第二句的i必须重新定义 Mfc通过ado连接数据库 #import "c:\program files\common files\system\ado\msado15.dll" \ no_namespace \ rename ("EOF", "adoEOF") SqlServer的mdf文件必须附加到数据库中才能进行编辑 // 初始化COM,创建ADO连接等操作 if (!AfxOleInit()) { AfxMessageBox("OLE/COM初始化失败"); return FALSE; } HRESULT hr; try { hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象 //hr = m_pConnection.CreateInstance(__uuidof(Connection)); if(SUCCEEDED(hr)) { //hr = m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=test1.mdb","","",adModeUnknown);///连接 hr = m_pConnection->Open("Provider=SQLOLEDB;server=(local);UID=sa;PWD=123;database=test1","","",a dModeUnknown); } } catch(_com_error e)///捕捉异常 { CString errormessage; errormessage.Format("连接数据库失败!\r错误信息:%s",e.ErrorMessage()); AfxMessageBox(errormessage);///显示错误信息 } _variant_t _variant_t和_bstr_t这两个类分别封装并管理VARIANT和BSTR这两种数据类型, VARIANT和BSTR这两种类型是COM中使用的数据类型。 为了C++中的变量应用到ADO编程中,只能进行数据类型的转换。 通过_variant_t和_bstr_t这两个类,就可以方便的把C++类型变量转换成COM中的变量了 Sql语句错误导致数据异常 MyLinkMdb.exe 中的 0x7c812afb 处未处理的异常: Microsoft C++ 异常: 内存位置 0x0012eeb0 处的 _com_error。 _T _T("")是一个宏,他的作用是让你的程序支持Unicode编码 因为Windows使用两种字符集ANSI和UNICODE, 前者就是通常使用的单字节方式, 但这种方式处理象中文这样的双字节字符不方便, 容易出现半个汉字的情况。 而后者是双字节方式,方便处理双字节字符。 Windows NT的所有与字符有关的函数都提供两种方式的版本,而Windows 9x只支持ANSI方式。 如果你编译一个程序为ANSI方式, _T实际不起任何作用。 而如果编译一个程序为UNICODE方式,则编译器会把"Hello"字符串以UNICODE方式保存。_T和_L的区别在于,_L不管你是以什么方式编译,一律以UNICODE方式保存。 LPSTR:32bit指针指向一个字符串,每个字符占1字节 LPCSTR:32-bit指针指向一个常字符串,每个字符占1字节 LPCTSTR:32-bit指针指向一个常字符串,每字符可能占1字节或2字节,取决于Unicode是否定义 LPTSTR:32-bit指针每字符可能占1字节或2字节,取决于Unicode是否定义 L是表示字符串资源为Unicode的。 比如 wchar_t Str[] = L"Hello World!"; 这个就是双子节存储字符了。 _T是一个适配的宏, 当 #ifdef _UNICODE的时候 _T就是L 没有#ifdef _UNICODE的时候 _T就是ANSI的。 比如 LPTSTR lpStr = new TCHAR[32]; TCHAR* szBuf = _T("Hello"); 以上两句使得无论是在UNICODE编译条件下都是正确编译的。 而且MS推荐你使用相匹配的字符串函数。 比如处理LPTSTR或者LPCTSTR 的时候,不要用strlen ,而是要用_tcslen 否则在UNICODE的编译条件下,strlen不能处理 wchar_t*的字符串。 T是非常有意思的一个符号(TCHAR、LPCTSTR、LPTSTR、_T()、_TEXT()...),它表示使用一种中间类型,既不明确表示使用 MBCS,也不明确表示使用 UNICODE。那到底使用哪种字符集?编译的时候才决定 Eof和Bof的用法 2009-04-06 01:58 P.M. 1.使用ADO连接数据库进行查询的时候,数据库将查询结果返回查询端,在查询端的内存里面就会有一个列表,这个列表存放的就是查询的结果。这个内存中的列表就是数据集。在你的程序里面,rs就是表示这个数据集。bof表示rs当前的指针是指在了数据集的前面,比如数据集里面有编号为1,2,3,4的4条,但是rs指向的编号是-1,这样就会符合了bof的情况。同理, rs的指针指向5,而第五条记录并不存在,这样就会产生了eof的情况。 如果同时产生了bof和aof的情况,就是说rs的指针既在上界之外也在下届之外,这样只有一种情况,就是rs所代表的数据集空是的,这样rs的指针无论是指到什么地方都是同时具有bof和eof的属性。 情况估计是sql语句查询出来的值是空的,因此数据集也是空的,在读取rs数据集里面的值的时候就会报这个错误。 使用 if rs.bof and rs.eof then 这个判断可以判别rs里面是否有没有数据集,如果符合这个条件,说明数据集是空的,在程序中就要绕开对rs里字段的访问。 2. BOF和EOF是分别指向记录集的起始地址和结束地址。你确定这两个指针有指向吗,这两个指针要同时为“真”吧。还有如果你确认以上是正确的,那么你是否已经打开相应的记录集呢, 还有就是选择一个已有用户名来调试。真相会出来的。 3.在你查询的表中没有数据,可能是SQL的毛病也可能是真的没有数据。只要加上一个出错处理就可以了。 on error resuem next rss.movefirst if err=3021 then response.write "无数据~" end if Ado封装类 // AdoData.cpp: implementation of the CAdoData class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h " #include "PersonManager.h " #include "AdoData.h " #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CAdoData::CAdoData() { ::CoInitialize(NULL); mcnn.CreateInstance(__uuidof(Connection)); mcnn-> CursorLocation=adUseClient; mrst.CreateInstance((__uuidof(Recordset))); } CAdoData::~CAdoData() { if (mrst-> GetState() == adStateOpen) mrst-> Close(); if (mcnn-> GetState() == adStateOpen) mcnn-> Close(); mrst=NULL; mcnn=NULL; ::CoUninitialize(); } //连接ADO数据源 long CAdoData::AdoConnect( CString vstrDatabase, bool vblnIsSQLServer, CString vstrServer, CString vstrUser, CString vstrPassWord ) { try { if ( vblnIsSQLServer ) { mcnn-> Open(_bstr_t( "Provider=SQLOLEDB.1;Persist Security Info=False;User ID= " + vstrUser + ";Password = " + vstrPassWord + ";Initial Catalog= " + vstrDatabase + ";Data Source= " + vstrServer), " ", " ",-1); } else { mcnn-> Open(_bstr_t( "Provider=Microsoft.Jet.OLEDB.4.0;Data Source= " + vstrDatabase + ";Pers ist Security Info=False;Jet OLEDB:Database PassWord = " + vstrPassWord), " ", " ",-1); } mcnn-> Open( str, " ", " ", -1); if ( mcnn-> GetState() == adStateOpen ){ return -1; } else { return 0; } } catch (...) { return 0; } } //打开记录集 _RecordsetPtr &CAdoData::GetRecordSet( CString vstrSQL ) { try { if ( mcnn-> GetState() == adStateOpen) { mrst-> Open(_bstr_t( vstrSQL ), mcnn.GetInterfacePtr(), adOpenDynamic, adLockOptimistic, adC mdText); } return mrst; } catch (...) { return mrst; } } //执行SQL语句 long CAdoData::AdoQuery( CString vstrQuerySQL ) { try { VARIANT varCount; if ( mcnn-> GetState() != adStateOpen) return 0; mcnn-> Execute( _bstr_t( vstrQuerySQL ), &varCount, adCmdUnknown ); return (long)varCount.vt; } catch (...) { return 0; } } atol 功 能: 把字符串转换成长整型数相关函数: atof,atoi,strtod,strtol,strtoul 表头文件: #include 定义函数: long atol(const char *nptr); 函数说明: atol()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负符号才开始做转换,而再遇到非数字或字符串结束时('\0')才结束转换,并将结果返回。 返回值:返回转换后的长整型数。如果传入的字符串为空,或者字符串包含的内容非阿拉伯数字序列,则函数返回默认值0。 附加说明: atol()与使用strtol(nptr,(char**)NULL,10);结果相同。
/
本文档为【mfc_openGL学习笔记】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索