获得Combobox的状态:向ComboBox发送CB_GETDROPPEDSTATE消息.
格式化字串:char buff[10] ; sprintf(buff,”1+1=%d”,1+1); Sprintf会返回格式化的字符串的长度。
C区别大小写。
窗口大部份情况都要处理WM_PAINT,WM_PAINT处理几乎总是从BeginPaint开始,以EndPaint结束。
获取字符串长度:strlen wcslen(unicode长度);
退出程序:DestroyWindow
文件拖放相关API:DragAcceptFiles,DragQueryFile以及消息WM_DROPFILES
设置定时器:SetTimer删除定时器:KillTimer
LOWORD:返回含低16位;HIWORD:返回高16位,类似的还有LOBYTE,HIBYTE
Cout,cerr与<<一起完成输出与标准错误检出。Cin与>>完成输入操作.endl表示换行。使用他们需要#include <iostream.h>。例如:cout<<”a”< 当子类创建时,会先调用基类的构造函数,然后再调用子类函数。在Delphi中如果不显示使用inherited则不会调用基类的构造函数。另外C++的继承和Delphi也不样,C++可用Public,Protected,private指定继承级别。 Class AA { AA(int i); ...... } Class BB:AA { BB(int i); ...... } //这里会先执行父类AA的构造函数,再执行子类BB的构造函数 BB::BB(int i):AA(int i) { …… } 纯虚函数,在最后加上=0;如virtual void aa()=0;,拥有纯虚函数的类称为抽象类。 避免重复定义类 #inndef xxxx #define xxxx 类包含的内容 #endif 在WM_PAINT最前面用ValidateRect(hwnd,NULL)可使显示区域有效且不会重绘,因为显示区域无效才会重绘 GetTextMetrics取得字体大小,GetSystemMetrics函式以取使用者介面上各类视觉元件大小的资讯 Stdafx:包含了一些必需的头文件,是MFC编程的必需文件,它还会调用windows.h AppWizard生成的典型源文件: Project.cpp MainFrm.capp:控制程序的主窗口 projectView.cpp projectDoc.cpp stdafx.cpp project.rc:包含资源文件 resource.h:用来定义宏 m_hWnd:MFC里窗口类的句柄 IsDlgButtonChecked判断CheckBox是否按下 UpdateData(bSaveAndValidate)如果bSaveAndValidate=false则初始化对话框,如果bSaveAndValidate=true则重新获取对话框的数据,默认为True GetCurSel:获取ComboBox,ListBox的当前选择项目,如果返回CB_ERR则没有项目被选中 CWnd::SetDlgItemText:Sets the caption or text of a control owned by a window or dialog box. CWnd::GetDlgItemText:Call this member function to retrieve the title or text associated with a control in a dialog box. void CheckRadioButton( int nIDFirstButton, int nIDLastButton, int nIDCheckButton ); Selects (adds a check mark to) a given radio button in a group and clears (removes a check mark from) all other radio buttons in the group. CComboBox::GetDroppedState:判断CComboBox是否处于下拉状态 如果要使一个static Text响应消息,必须设置一个唯一ID,并勾选属性里面的notify Vector:相当于一个容器,是一个能够存放任意类型的动态数组,能够增加和压缩数据。 Vector Test.pushback(66);//在vector的最后放入66 Test.pushbak(88); //在vector的最后放入88 MFC程序入口点WinMain DefWindowProc:对未被处理的消息提供默认响应。 去除一个属性:当前属性结合与上需要去除属性取反后的值,如 styles & ~style1 关闭程序流程: 点击右上角的叉叉->产生WM_CLOSE消息->在WM_CLOSE里可以判断是否需要关闭,如果需要关闭则调用DestroyWindow()(作用是销毁Windows窗体)->当窗体销毁后产生一个WM_DESTROY消息->在WM_DESTROY里调用PostQuitMessage(0) ->产生WM_QUIT消息(GetMessage()如果获取到的消息是WM_QUIT就会返回0,这样就退出消息循环)->退出消息循环,程序结束 当收到WM_DESTROY消息后,必须调用PostQuitMessage,否则消息循环不会停止,程序也永远不会结束。PostQuitMessage会产生一个WM_QUIT消息,当应用程序收到WM_QUIT消息后就退出循环了。 DefWindowProc:缺省窗口处理过程,不需要自己处理的消息都由这个过程处理 m_前缀代表是一个类的成员变量 标准输入输出流(#include iostream.h): cin>>xxxx cout< cerr< 在输出时可以使用endl,相当于C语言的’\n’, 表示换行 如果以一个类未提供构造函数,则C++会提供一个默认的构造函数,这个构造函数没有参数,只负责创建对象,而不做任何的初始化工作. 只要定义了一个构造函数,C++就不再提供默认的构造函数,如果还想要没有参数的构造函数,则必须自己定义. this是一个隐含的指针,它指向对象本身,代表了对象的地址. 句柄 :HWND,HICON,HCURSOR,HBRUSH等等. Windows程序是一种基于事件驱动的程序,主要是基于消息. 在Win32程序中,WinMain函数的hPrevInstance总是NULL. SendMessage直接将消息发到窗口,消息处理完成后才返回. PostMessage将消息发到应用程序的消息队列里,并立即返回. 类成员在默认情况下是私有的. 当以MyClass obj ;声明一个对象时,会默认调用无参数构造函数. 在声明一个对象时,如MyClass obj时,对象就已经创建.如果需要使用带参数的构建函数,则在声明对象时应该这样 : MyClass obj(x,y) 子类声明 : Class fish : public animal { ...... } 分配内存 :pName = new char[20] ; 释放内存 :delete[] pName ; 只有返回类型不同是不能构成函数重载的. 在函数重载时,要注意带有默认值参数的情况. 子类的构造函数会先调用父类的构造函数. 子类的析构函数则相反,子类先析构,父类后析构. 当子类调用父类有不同参数的构造函数时,需要这样 : Fish(void) :animal(100,200) { ...... } 在父类没有和子类参数一样的构造函数时,必须这样处理. C++支持多重继承 : Class b :public class c,public class d { ...... } B继承于c和d . 初始化时按照基类表说明顺序来进行的.析构函数则是按照说明顺序的相反方向进行的. 子类调用父类函数(如果子类没有调用父类的函数,则不会执行父类的虚拟函数) : class fish:public animal { public: virtual void eate(void) { animal::eate(); cout<<"bigfish eate"< } }; 声明一个纯虚函数: Virtual void eate(void) = 0; 凡是含有纯虚函数的类成为抽象类,这种类不能实例化,只是作为基类为派生类服务.在派生类必须全部实现基类的纯虚函数,否则派生类也变成了抽象类. 当基类和子类出现两个参数和名称完全一样的函数,并且基类函数没有使用virtual标示符,则基类的的函数将被隐藏. 当基类和子类出现两个参数不一样,但名称完全一样的函数时,不管基类是否使用了virtual标示符,基类的函数都将被隐藏. C分配类存的函数: Malloc(),calloc(),realloc() 使用free释放分配的内存 C++分配内存: Int *p = new int;//单个对象 Int *q = new int[100];数组 释放内存: Delete p; Delete[] q; 声明一个引用: int a = 5; int &b = a;//初始化引用,代表b和a使用同一内存. 如果b = 3,则a也等于3. 引用一旦初始化后,再也不能代表别的内存. 一般情况下都不用引用,而使用指针. 每个MFC都只有1个派生于应用程序类(CWinApp)的theApp全局变量,用来唯一标识应用程序的实例,标示了应用程序本身. Afx前缀代表应用程序框架(Application Framework),以Afx前缀开头的函数都是全局函数.比如AfxMessageBox。 MFC程序的全局变量都是放在Globals分支下. 在加载WinMain之前,全局对象/变量就已经初始化OK了. Memset:填充内存为某一ASCII值,和Delphi的FillChar类似. MFC中后缀名为Ex的函数都是扩展函数. 寻找WinMain: 1. APPMODUL.CPP _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); } 2. 每个MFC应用程序有且只有一个继承于CWinApp的theApp对象,代表此应用程序. 3. 因为基类的构造函数会调用父类的构造函数,所以请看CwinApp的构造函数(APPCORE.CPP), 其中有一句pModuleState->m_pCurrentWinApp = this 4. 接下来再回来看第1步的_tWinMain函数中的AfxWinMain. 5. 在WINMAIN.CPP里找到AfxWinMain的实现代码.注意此函数定义里的几句代码: { …… CWinThread* pThread = AfxGetThread(); CWinApp* pApp = AfxGetApp(); …… if (pApp != NULL && !pApp->InitApplication())//完成MFC内部管理工作 …… if (!pThread->InitInstance())//实际上调用的是我们自己的应用程序对象的//InitInstance, …… nReturnCode = pThread->Run(); } 经查看AfxGetThread实现代码(THRDCORE.APP)可知, AfxGetThread函数最终调用了AfxGetApp()并返回,所以pThread和pApp这两个指针是一样的. 再来看AfxGetApp()的实现代码: _AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp() { return afxCurrentWinApp; } 看下afxCurrentWinApp的实现(AFXWIN.H): #define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp 从第3步以及本步的以上描述可以得出:实际上pThread和pApp这两个指针实际上就是this指针,this指针指向实际应用程序,也就是theApp. 6. InitInstance()函数: 再看第5步的AfxWinMain的实现,其中的pThread->InitInstance为我们自己程序的xxxApp(其实也可以说是theApp)的方法. 7. 注册类的函数AfxEndDeferRegisterClass(WINCORE.CPP). 8. MFC应用程序实际上有两个窗口,其中一个是CMainFrame类的对象代表的应用程序框架窗口,该类有一个PreCreateWindow,它在窗口产生之前被调用.CmainFram只是仅仅调用了父类CFrameWnd的PreCreateWindow(WINFRM.CPP),在父类CframeWnd的PreCreateWindow函数里又调用了AfxEndDeferRegisterClass方法注册类(第7步).所以可以看出, PreCreateWindow可以再创建窗体之前可以改变它的注册类的各项参数,从而也改变了窗体的样式等属性. 9. 查看CWnd::CreateEx函数,此函数用于初始化窗口注册类,并调用而来第8步的PreCreateWindow(所以说PreCreateWindow在创建窗体之前留给了程序员一条用于修改窗体属性的通道),最后使用了CreateWindowEx来创建窗体. 10. 又是谁调用CreateEx呢,它就是CFrameWnd::Create(WINFRM.CPP). 11. 再往上刨刨,看看CFrameWnd::Create又是谁调用的,原来是CFrameWnd::LoadFrame. Create -> CreateEx -> PreCreateWindow CWnd::SetIcon:为窗口设置一个图标 以::开始的方法是全局函数. 定义类成员变量时,一般用m_前缀开头. 创建一个按钮: m_btn1.Create("ViewBtn1",WS_CHILD | WS_VISIBLE,CRect(0,0,300,300),this,0); GetParent()获得父窗口. 消息映射有三个地方相关: 1. 头文件,类似于: //{{AFX_MSG(CDrawView) afx_msg void OnLButtonDown(UINT nFlags, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP() 2. 源文件有两处,类似于: 第一处: BEGIN_MESSAGE_MAP(CDrawView, CView) //{{AFX_MSG_MAP(CDrawView) ON_WM_LBUTTONDOWN()//它是一个宏,绑定了消息和其响应方法 //}}AFX_MSG_MAP …… END_MESSAGE_MAP() 第二处: void CDrawView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CView::OnLButtonDown(nFlags, point); } 消息->窗口句柄和对象指针一一对应的对照表->根据消息中的句柄找到对象->把消息传给应用程序框架类->调用WindowProc CWnd的WindowProc会调用一个OnWndMsg函数,此函数负责对消息路由分派(一一对消息判断,然后处理). OnWndMsg会根据上面所述的消息映射方法,到子类的头文件的DECLARE_MESSAGE_MAP()之上, //{{AFX_MSG(CDrawView)和//}}AFX_MSG之间查找是否有相应的消息响应函数原型声明,再到子类的源文件的BEGIN_MESSAGE_MAP(CDrawView, CView)和END_MESSAGE_MAP()之间查看是否有相应的消息映射宏. 如果找到了消息响应函数,就调用消息响应函数处理消息.如果子类没有找到消息响应函数,就交给父类处理. CclientDC:客户区域DC,在构造或释放时自动调用GetDC和ReleaseDC. CwindowDC:客户访问整个窗口区域.包括标题栏菜单. SetROP2:设置绘图模式 获得一个透明背景的画刷CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH)) 创建并显示一个插入符: CreateSolidCaret(20,100); ShowCaret(); SetCaretPos:设置插入符的位置 获得字体的度量信息: 使用CDC的GetTextExtent来获得字符串的宽度和高度 路径层(Path bracket): 使用CDC的BeginPath和EndPath来创建及销毁路径层.需要配合Rectangles, Ellipse等使用, Rectangle, Ellipse等画出了当前路径层的范围 当前路径层的作用需要SelectClipPath来确定模式. 裁剪区域(Clipping region):绘图操作局限于在裁剪区域. CDC的SelectClipPath: 为设备上下文选择当前路径作为剪切区,并使用指定模式组合新区域与已存在的剪切区. 例子: pDC->BeginPath();//路径层 CSize size = pDC->GetTextExtent(m_strText);//获得m_strText的宽度 //这里的Rectangle标识除了路径层的范围 pDC->Rectangle(CRect(0,0,size.cx,size.cy));// pDC->EndPath(); //RGN_DIFF:排除路径层内容,也就是下面画的线不会显示在当前路径层 pDC->SelectClipPath(RGN_DIFF); //画线,这里就可以看出效果了 for (int i=0;i<300;i+=10) { pDC->MoveTo(0,i); pDC->LineTo(300,i); pDC->MoveTo(i,0); pDC->LineTo(i,300); } 效果如下,画的线未影响文字部分 Cfont:构造后还要初始化才能使用.比如用CreateFont等. 如果要使用这个字体,还必须选入设备描述符. CeditView和CrichEditView这两个类用来实现文字处理. OnDraw:每单重绘时都会调用OnDraw(个人觉得应该是在WM_PAINT里面调用的) CCmdTarget类及其派生类可以接受命令消息(WM_COMMAND),通告消息,但不能接收标准消息 CWnd类及其派生类可以接收标准消息(WM_XXXX),又因Cwnd派生于CCmdTarget,所以也可以接收命令消息,通告消息 注: 标准消息:除WM_COMMAND外,所有以WM_开头的消息 命令消息:来自菜单,加速键,工具栏的消息,以WM_COMMAND的形式呈现. 通告消息:是由控件产生的消息,例如按钮的单击,列表框的选择等,通告消息也是以WM_COMMAND形式呈现. 获得程序的主菜单:Cwnd的GetMenu(); 获得子菜单:Cmenu的GetSubMenu; 标记菜单: //勾选(标记)菜单,根据索引 this->GetMenu()->GetSubMenu(4)->CheckMenuItem(0,MF_BYPOSITION | MF_CHECKED); //勾选(标记)菜单,根据ID号 this->GetMenu()->GetSubMenu(4)->CheckMenuItem(IDM_TEST3,MF_BYCOMMAND | MF_CHECKED); //每个子菜单只能有一个缺省菜单 //设置缺省菜单,根据索引 this->GetMenu()->GetSubMenu(4)->SetDefaultItem(2,TRUE); //设置缺省菜单,根据ID号 this->GetMenu()->GetSubMenu(4)->SetDefaultItem(IDM_TEST5,FALSE); //设置菜单显示图形 int xMenu = GetSystemMetrics(SM_CXMENUCHECK); int yMenu = GetSystemMetrics(SM_CYMENUCHECK); CString str; str.Format("菜单图标的大小为:长:%d宽%d",xMenu,yMenu); //图标的大小不能超过xMenu,yMenu MessageBox(str); CBitmap *pBitmap = new CBitmap; pBitmap->LoadBitmap(IDB_MENU); this->GetMenu()->GetSubMenu(4)->SetMenuItemBitmaps(3,MF_BYPOSITION,pBitmap,pBitmap); //手动控制菜单是否可用 m_bAutoMenuEnable = FALSE; //将"打开"设置为不可用 this->GetMenu()->GetSubMenu(0)->EnableMenuItem(1,MF_BYPOSITION | MF_DISABLED | MF_GRAYED); //手动设置菜单 this->SetMenu(NULL);//这里菜单会消息 //然后再重新设置新菜单 CMenu menu; menu.LoadMenu(IDR_MAINFRAME); //需要重新设置菜单的属性 menu.GetSubMenu(0)->EnableMenuItem(1,MF_BYPOSITION | MF_DISABLED | MF_GRAYED); this->SetMenu(&menu); menu.Detach();//必须断开与HMENU的联系,否则会出错 如果要手动更改菜单的启用或禁用或变灰状态,需要在框架构建函数里设置m_bAutoMenuEnable为False. UPDATE_COMMAND_UI消息用于改变菜单项的状态,通过映射的方法可更改菜单项的状态(只适用于子菜单). 把Toolbar工具栏的某项的ID号设置得和菜单一样,他们就关联到一起了. 多个菜单项可以使用同一个处理函数(注意消息映射). 弹出菜单: CMenu menu; menu.LoadMenu(IDR_MENUPOP); CMenu *popMenu = menu.GetSubMenu(0); ClientToScreen(&point);//窗口坐标转屏幕坐标,因为TrackPopupMenu需要使用屏幕坐标 popMenu->TrackPopupMenu(TPM_LEFTALIGN| TPM_RIGHTBUTTON,point.x,point.y,this); 重绘菜单: 框架类DrawMenuBar();动态编辑菜单后,需要重绘菜单反应效果. 动态添加一个菜单: m_menu.CreatePopupMenu(); GetParent()->GetMenu()->AppendMenu(MF_POPUP,(UINT)m_menu.m_hMenu,"PhoneBook"); GetParent()->DrawMenuBar(); 可以用CWnd的虚函数OnCommand拦截菜单消息. 对于动态添加的菜单的响应可以集中在OnCommand里处理 GetActiveView():获取CView 模态对话框: 打开:DoModal(); 关闭:EndDialog(); 例: CDialog1 dialog ; dialog.DoModal(); 非模态对话框: 打开:Create ShowWindow CTestDlg *pDlg = new CTestDlg(); pDlg->Create(IDD_DIALOG1,this); pDlg->ShowWindow(SW_SHOW); 如果在非模态对话框中实现OK按钮,必须覆盖OnOK成员函数,并在其中调用DestoryWindow。不能调用基类成员函数,那将会调用EndDialog,使对话框虽然存在但不可视: void CTestDlg::OnOK() { DestroyWindow();//非模态调用,并注释基类成员函数(如下) //CDialog::OnOK();//模态调用 } 定义一个指针,分配到堆上,生命周期和整个应用程序的生命周期一样. CRect.IsRectNull():4个坐标都为0 CRect.IsRectEmpty():矩形大小为0,坐标为非0 GetClientRect和GetWindowRect的区别: GetWindowRect() 得到的是在屏幕坐标系下的RECT;(即以屏幕左上角为原点) GetClientRect() 得到的是在客户区坐标系下的RECT; (即以所在窗口左上角为原点) 先调用GetWindowRect后再调用ScreenToClient,这个时候得到的rect和直接使用GetClientRect得到的值是相等的。 WS_EX_TOPMOST:顶层窗口 BringWindowToTop:把一个窗口放置到Z次序的顶部. SetForeground:设置前台窗口(相对于同一个应用程序) SetWindowPos:设置窗口大小,位置等 WM_INITDIALOG:Dialog及其子控件创建完成,将要显示前产生此消息 指定的窗口数据是在缓存中保存的,因此在调用SetWindowLong之后再调用SetWindowPos函数才能使SetWindowLong函数所作的改变生效。 改变一个Button的WM_MOUSEMOVE消息的响应(假设这个Button名字为btn1): 1. 新建一个CButton的子类,假设为CMyButton 2. 根据需要,重新实现CMyButton的WM_MOUSEMOVE消息响应函数 3. btn1关联一个类型为CButton的控件变量m_btn1, 4. 现在btn1的WM_MOUSEMOVE就由CMyButton来响应了 使用PropertyPage(类似Delphi的TabSheet): 1. 在资源里添加N个IDD_PROPPAGE_XXXX(Dialog子项) 2. 为对应的IDD_PROPPAGE_XXXX资源添加类(CPropertyPage的子类) 3. 声明这些类的对象 4. 创建一个CPropertySheet对象. 5. CPropertySheet对象使用AddPage把第3步这些对象添加到CPropertySheet对象里 6. 如果需要向导模式,则CPropertySheet对象使用SetWizardMode()方法. 7. CPropertySheet对象使用DoModal模式显示或者Create普通显示 8. 通过覆盖第2步这些CPropertyPage子类的虚拟方法virtual BOOL CProp1::OnSetActive(),可以控制显示哪些按钮,如只显示”下一步”可以在OnSetActive方法里这样: ((CPropertySheet *)this->GetParent())->SetWizardButtons(PSWIZB_NEXT); 关于RadioButton的分组: 将第一个RadioButton的Group勾选上,别的同组RadioButton不用勾选.为第一个RadioButton关联一个int类型变量,这个变量的值指示了本组哪一个RadioButton本选中,-1:没有RadioButton被选中,0:第一个RadioButton被选中,1:第二个被选中,以此类推. 下一组的第一个RadioButton的Group勾上,就作为新的组了. Dialog的初始化可以放到WM_INITDIALOG消息响应函数OnInitDialog()里 改变MFC窗口的标题 //改变窗口的标题 //需先把FWS_ADDTOTITLE去掉 cs.style &= ~FWS_ADDTOTITLE; cs.lpszName = "窗口外观定制"; 以下为说明的都在PreCreateWindow里实现: 可以用GetWindowLong,SetWindowLong修改窗口风格 可以通过创建一个新WNDCLASS修改窗口类风格 可以通过AfxRegisterWndClass注册一个新窗口类来修改风格 可以用SetClassLong改变窗口类风格,可在多处地方调用 判断一个窗口是否可见: IsWindowVisible() 重新设置CFrameWnd对象的控件条的位置 ShowControlBar:显示或隐藏一个ControlBar对象 在View里设置StatusBar的文本: 1.((CMainFrame *)GetParent())->m_wndStatusBar.SetWindowText(str); 2.((CMainFrame *)GetParent())->SetMessageText(str); 3.((CMainFrame *)GetParent())->GetMessageBar()->SetWindowText(str); 4.GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str); int idx = m_wndStatusBar.CommandToIndex(IDS_TIMER);//根据字符串资源获得状态栏某项的序号 m_wndStatusBar.SetPaneInfo(idx,IDS_TIMER,SBPS_NORMAL100); //重新设置状态栏某项的宽度 m_wndStatusBar.SetPaneText(idx,str,TRUE);//在状态栏的某一格显示文字 Dll导出函数方法: 1. 在头文件里声明:extern "C" int __declspec(dllexport)add(int x, int y); 2. 添加一个*.def文件,文件内容如下: LIBRARY dllTest Dll的静态调用: 1.将dllTest1.lib拷贝到callDllTest2工程目录 2.将dllTest1.dll拷贝到callDllTest2的Debug目录 3.将dllTest1的h头文件拷贝到工程目录(本项目略过) 4.使用语句导入dllTest1,如下: #pragma comment(lib,"dllTest1.lib") extern "C" int __declspec(dllimport) add(int x,int y); Dll的动态调用: #include Dll的入口函数: //DLL的入口函数 BOOL APIENTRY DllMain(HANDLE hMoudle, DWORD ul_reason_for_call, LPVOID lpReserved) { switch(ul_reason_for_call){ case DLL_PROCESS_ATTACH: printf("DLL_PROCESS_ATTACH\n"); break; case DLL_PROCESS_DETACH: printf("DLL_PROCESS_DETACH\n"); break; case DLL_THREAD_ATTACH: printf("DLL_THREAD_ATTACH\n"); break; case DLL_THREAD_DETACH: printf("DLL_THREAD_DETACH\n"); break; } return TRUE; } 如果通过VC++编写的DLL欲被其他语言编写的程序调用,应将 函数的调用方式声明为__stdcall方式 C/C++缺省的调用方式却为__cdecl Windows编程中常见的几种函数类型声明宏都是与__stdcall和__cdecl有关的(节选自windef.h): #define CALLBACK __stdcall //这就是传说中的回调函数 一般在Dll文件里这样声明: int __stdcall add(int x,int y){ return x + y; } 并定义def文件,输出函数,def文件内容如下:(经测试,如果Delphi需要调用VC写的Dll必须用def文件导出函数) LIBRARY dllMain EXPORTS add @ 1 如果要在Dll中导出变量,在def文件里:(DATA标志很重要) dllGlobalVar DATA 使用dll的变量: #include 如果C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern "C" { } string转 char *:string.c_str();同样适用于string转LPCSTR memset:填充缓冲 注意互斥对象在拥有Mutex时的计数。请求次数和释放次数要相等,别的线程才能使用这个互斥对象。 当一个线程终止时,即便拥有Mutex没有释放,操作系统也会自动释放线程拥有的Mutex。 在做HID的DLL时,由于死锁造成了不能退出线程。在线程里调用ReadBuff回调函数,如果这时ReadBuff回调函数里调用Close,由于Close会等待线程结束,而线程在等待ReadBuff返回,于是造成了死锁。 Delphi调用vc写的dll时,需要注意调用顺序,一般统一为stdcall CObject给子类提供了3个重要的特性: 1. 串行化支持 2. 运行时类信息支持(RTCI,和RTTI不一样) 3. 诊断和调试支持 Afx函数:全局函数: AfxGetApp:返回指向应用程序对象的指针 AfxGetMainWnd:返回指向应用程序主窗口的指针 AfxGetInstanceHandle:获得实例句柄 常用Afx函数见MFC Windows程序设计13页 消息映射: 在类的最后加上DECLARE_MESSAGE_MAP(),如果在DECLARE_MESSAGE_MAP()后又定义了成员,需要重新指明访问类型:public,protected等 然后在在.cpp文件里BEGIN_MESSAGE_MAP(…)和里END_MESSAGE_MAP()里注明映射,如: BEGIN_MESSAGE_MAP(CMaindWindows,CFrameWnd) ON_WM_PAINT() END_MESSAGE_MAP() m_pMainWnd: “用该成员变量去存储你的线程主窗口对象。当和m_pMainWnd 相关的窗口被关闭后,MFC会自动终止你的线程。如果该线程是应用程序主线程,程序也将会被终止。如果该数据成员为NULL,应用程序CWinApp对象的主窗口将用来决定什么时候去终止线程。m_pMainWnd是一个CWnd*类型的public变量(CWinApp类里定义)。 MFC应用程序的核心就是基于CWinApp类的应用程序对象。CWinApp提供了消息循环来检索消息并将消息调度给应用程序窗口,它还包括可以被覆盖的,用来自定义应用程序行为的主要虚函数。一旦头文件包括Afxwin.h,就可以将CWinApp以及其他MFC类引入应用程序中。 CWinApp对象的InitInstace虚方法在程序开始运行后,窗口创建前被调用 除非InitInstace创建一个窗口,否则应用程序是不会有窗口的,这正是为什么MFC应用程序必须从CWinApp派生出一个类并覆盖CWinApp::InitInstace的原因。一般应用程序的初始化工作都可以放在InitInstace方法里。如果InitInstace返回false将关闭程序 一般情况下,在InitInstance里调用ShowWindow时,传递m_nCmdShow(执行这个程序时由外部指定),而不是SW_值。 当你从CWinApp继承应用程序类的时候,应重载InitInstance成员函数以创建应用程序的主窗口对象。 如果在InitInstance里分配了资源,可以在ExitInstance里释放这些资源,他们都是在CWinApp的虚拟函数,在CwinApp子类里可被覆盖. 其他CWinApp可被覆盖的函数有:OnIdle,Run,PreTranslateMessage. 应用程序空闲时调用OnIdle,可以覆盖Run来自定义消息循环,如果想在消息被调度前执行一些专门的预处理,则可以覆盖PreTranslateMessage. 如果MFC没有提供某个消息的映射,可以自定义消息映射: ON_MESSAGE(WM_SetText,OnSetText); …… afx_msg LRESUT OnSetText(WPARAM wPram,LPARAM lParam); 如果要调用常规API,需要在API函数前使用 :: 符号。 MessageBox(L"hello, world"); MessageBox(_T"hello, world"); L"hello, world" 这个字符串保存的是wchar的 _T"hello, world" 如果工程定义了_UNICDE则这个字符串是按wchar方式保存的,如果没有定义则是按char方式保存的 如果想使应用程序不关心字符集,除了用_T外,还需要注意: 1. 使用TCHAR而不是char 2. 使用TCHAR*或者更佳的LPSTR和LPCSTR 3. 使用Tchar.h里面的字符串处理函数 在AfxWinMain运行前,应用程序对象(CWinApp)必须在内存中存在。 SetWindowLong:改变窗口特征 MoveWindow:移动窗口 DestroyWindow:退出窗口 SetWindowText:改变窗口标题栏的名称 LoadMenu :载入一个菜单 AfxGetInstanceHandle:获得应用程序的实例 AfxRegisterWndClass (0):一个非常一般的窗口类 销毁一个窗口: pWnd->DestroyWindow();//删除Windows窗口 delete pWnd;//销毁pWnd AfxGetApp( )->LoadCursor (xx):装载一个光标 GetDesktopWindow:获得桌面窗口 WindowFromPoint():通过坐标获取窗口 覆盖PreCreateWindow方法可在创建窗体前更改窗体样式 在用到RECT的地方,可以直接用CRect类替换 在创建窗体时,如果pParentWnd为NULL,则此窗口的所有者就是桌面。 CWnd的Create方法用于创建一个Windows窗体 CDC类封装了Windows设备环境。 CPaintCDC派生于CDC,用于绘制屏幕,它只在WM_PAINT消息里使用。 CPaintCDC的构造函数调用BeginPaint,析构函数调用EndPaint。 在创建了CPaintCDC对象后,OnPaint将构造一个代表矩形的CRect对象,此时可以调用CWnd::GetClientRect以使用窗口的客户区的坐标来初始化这个矩形。 StringCbCopy strcpy, wcscpy, _tcscpy lstrcpy StrCpy StringCbCopy函数原型如下: HRESULT StringCbCopy( __out LPTSTR pszDest, //目标字符串缓冲区 __in size_t cbDest, //目标缓冲区大小(字节),这个值必须考虑pszSrc加上空停止符’/0’的大小; //最大运行的字节数是STRSAFE_MAX_CCH * sizeof(TCHAR) __in LPCTSTR pszSrc //源字符串缓冲区,必须以’/0’结尾 ); 函数返回&#20540;如下(强烈建议应用SUCCEEDED和FAILED宏来测试返回&#20540;): S_OK //一切OK STRSAFE_E_INVALID_PARAMETER //目标缓冲区中&#20540;的大小要么是0,要么大于最大容许&#20540; STRSAFE_E_INSUFFICIENT_BUFFER //目标缓冲区大小不敷,数据被截断; //当容许数据截断时,这不算是错误 Carray:数组类 其中定义了一些专用数组类: CByteArray,CWordArray,CUIntArray,CDWordArray,CStringArray,CObArray,CPtrArray 使用数组前,需引用afxtempl.h CArray CArray m_Array.SetSize(10,10): SetSize函数设定数组的大小,该函数有两个参数,第一个参数设定数组的大小;第二个参数设定数组增长时内存分配的大小,缺省值是-1,使用缺省值可以保证内存分配得更加合理 您可以随时使用SetSize函数设定数组的大小,如果第一个参数值小于数组已有成员数量,多于第一个参数值的成员将被截去并释放相应内存 在使用CArray数组前,最好先使用SetSize确定其大小并申请存储空间。如果不这样做,向数组中增加元素时,需要不断地移动和拷贝元素造成运行的低效率和内存碎块 GetWindowsDC:可以在窗体任一位置画图 CDC的子类: CPaintDC:响应WM_PAINT消息 CClientDC:客户区域 CWindowDC:客户区域+非客户区域,并不常用.一般借助OnNcPaint处理程序捕获WM_NCPAINT消息 CMetaFileDC 当使用new创建设备描述符表时,需要亲自释放,如: CPaintDC *pDC = new CPaintDC(thils); … Delete pDC; 获得真个屏幕的设备描述表 CClientDC dc(NULL);(或者CWindowDC dc(NULL);) 背景透明:dc.SetBKMode(TRANSPARENT) 获得设备信息: 如:获得屏幕的的宽,以像素点数目计算: CClientDC dc(this); Int cx = dc.GetDeviceCaps(HORZRES);//宽度 Int cy = dc.GetDeviceCaps(VERTRES);//高度 Polyline:需要5个点来画一个矩形,其中第一个点作为矩形的起点 PolylineTo:只需要4个点,因为第一个点是使用设备描述符的当前位置(如先使用MoveTo确定起点), PolylineTo返回最后一个点的位置 CPen: 如果要改变画线方式,则需创建一个画笔(CPen),并由CDC::SelectObject选入设备描述符. 创建画笔有三种方式: 1. 直接使用构造函数 2. 使用CPen::CreatePen 3. 使用Cpen::CreatePenIndirect 画笔的3个特性:样式,宽度,颜色 MFC程序执行流程: 1. 全局对象(CWinApp的子类)theApp 2. CWinApp构造函数 3. theApp的构造函数 4. AfxWinMain 5. 窗体PreCreateWindow()(给了窗口创建前修改窗口的机会) 6. 应用程序InitInstance()(窗口类注册,窗口创建,窗口显示等工作) 7. 应用程序Run()(消息循环,处理消息) WM_CTLCOLOR: OnCtlColor响应WM_CTLCOLOR. pWnd->GetDlgCtrlID():获得窗口的ID 改变对话框上按钮的颜色: 1. 新建一个CButton的子类. 2. 覆盖DrawItem方法 3. 在DrawItem里修改颜色等….. 4. 建立一个和这个按钮关联的成员变量(类型为新建的类) 5. 在图形界面勾选按钮的Style为Owner draw 自定义背景 BOOL CBitmapDCView::OnEraseBkgnd(CDC* pDC) { // TODO: Add your message handler code here and/or call default CBitmap bitmap; bitmap.LoadBitmap(IDM_BACKGROUND);//加载一幅位图 CDC dcCompatible; dcCompatible.CreateCompatibleDC(pDC);//创建兼容DC dcCompatible.SelectObject(&bitmap);//将位图选入兼容dc CRect rect; this->GetClientRect(&rect); //拷贝位图到当前dc pDC->BitBlt(0,0,rect.Width(),rect.Height(),&dcCompatible,0,0,SRCCOPY); return TRUE; //return CView::OnEraseBkgnd(pDC); } CPtrArray: 设备坐标:以像素为单位 逻辑坐标:单位可以是像素,毫米,英寸等. OnInitialUpdate:窗口创建之后第一个被调用的函数,在第一次调用OnDraw之前调用OnInitialUpdate SetScrollSizes:设置窗口滚动大小 指向常量的指针: char ch [5] = “abcd”; //const在char的前面或后面是一样的,所以等同于 char const * pStr = ch,一般都是把const放//在char前面.他们都表示指针指向的对象是常量 const char * pStr = ch; *pStr = ‘w’ //error pStr = “xyz” //ok 指针常量 char ch[5] = “abcd”; char * const pStr=ch; //指针本身是常量,不能修改 pStr = “xyz”; //error //但是指针指向的内容是可以修改的 *pStr = ‘x’; //ok 文件写入: FILE * file = fopen("1.txt","w"); char buffer[10]; for (int i=0;i<10;i++) { buffer[i] = i + 0x30; } fwrite(buffer,sizeof(char),10,file); //fseek(file,0,SEEK_SET);//移动到文件的开始处 //fwrite("abcde",sizeof(char), lstrlen("abcde"),file);//这里会覆盖之前写的内容 //fflush(file);//将缓冲的内容写入磁盘文件,这样就不用每次都使用fclose关闭文件了 fclose(file); 文件读取: FILE * file = fopen("1.txt","r"); char buffer[10]; fread(buffer,sizeof(char),10,file); fclose(file); fseek:移动文件的指针 ftell:获取文件的当前位置 rewind:将文件指针移动到文件开始处 itoa:整形转为字符串 atoi:字符串转整形 ofstream:写文件 ifstream:读文件 需要引用fstream.h CreateFile,FileRead,FileWrite:API函数,可读写文件 MFC的Cfile:MFC的一个文件类(推荐),用于操作文件. CFile::modeNoTruncate:在CFile构建函数的第二个参数使用CFile::modeCreate | CFile::modeNoTruncate,可不用删除以前的文件. 初始化一个字符数组(优点:数组大小可为一个变量) Char * pBuff; pBuff = new char[100]; CDocument::SetTitle: 设置文档的标题 也可以通过修改IDR_MAINFRAME字符串资源的第二项来设置文档的标题(默认为空) 获得IDR_MAINFRAME里的字符串: CDocTemplate::GetDocString Doc文档的Serialize函数用于处理输入输出文件. 使一个类具有串行持久性的步骤: 1. 继承于Cobject 2. 覆盖Serialize成员函数(实现首先要调用基类的Serialize) 3. 在类申明的地方(头文件)定义 DECLARE_SERIAL(CGraph) 4. 定义一个不带参数的构造函数 5. 在类实现定义 IMPLEMENT_SERIAL(CGraph,CObject,1) 6. 然后在文档类(doc)的Serialize函数里保存或读取类 在Doc文档类里获得视类View: POSITION pos = GetFirstViewPosition(); if (pos != NULL) { CView * view = GetNextView(pos); …… } 在View类里获的Doc类的指针: 直接调用view类的m_pDocument CobArray:支持串行化,在文档类的Serialize函数里直接调用CobArray对象的Serialize函数,并把ar传递给CobArray对象的Serialize.,当然CobArray存储的对象必须具有串行化持久性 DeleteContents:在新建或者打开文档时会调用这个函数,用户需覆盖这个虚函数,从而执行清除数据工作 IP地址由4个8位点分数字表示 Port端口由一个16位数字表示,1024以下为系统保留使用. Socket服务器程序流程: 1 创建套接字(socket) 2 将套接字绑定到一个本地地址和端口上(bind) 3 将套接字设为监听模式,准备接收客户请求(listen) 4 等待客户请求到来.当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept) 5 用返回的套接字和客户端进行通信(send/recv) 6 返回,等待另一个客户请求 7 关闭套接字 Socket客户端程序流程 1 创建套接字(socket) 2 向服务器发出连接请求(connect) 3 和服务器端进行通信(send/recv) 4 关闭套接字 UDP服务器程序流程 1 创建套接字(socket) 2 将套接字绑定到一个本地地址和端口上(bind) 3 等待接收数据(recvfrom) 4 关闭套接字 UDP客户端程序流程 1 创建套接字(socket) 2 向服务器发送数据(sendto) 3 关闭套接字 ALT+F8:调整代码的格式 inet_addr:将一个十进制点分格式的字符串转换为一个in_addr能用的u_long类型. inet_ntoa:而此函数和inet_addr正好相反,它将一个in_addr类型转换为十进制点分格式的字符串.( in_addr需为网络字节顺序) htonl: 将一个u_long转换为TCP/IP网络字节顺序(大端模式,低位放高字节) htons: 将一个u_short转换为TCP/IP网络字节顺序(大端模式,低位放高字节) gets: 从stdio流中读取字符串,直至接受到换行符或EOF时停止,并将读取的结果存放在buffer指针所指向的字符数组中。换行符不作为读取串的内容,读取的换行符被转换为null值,并由此来结束字符串。 和cin效果一样 n 程序是计算机指令的集合,它以文件的形式存储在磁盘上。 n 进程:通常被定义为一个正在运行的程序的实例,是一个程序在其自身的地址空间中的一次执行活动。 n 进程是资源申请、调度和独立运行的单位,因此,它使用系统中的运行资源;而程序不能申请系统资源,不能被系统调度,也不能作为独立运行的单位,因此,它不占用系统的运行资源。 n 进程由两个部分组成: 1、操作系统用来管理进程的内核对象。内核对象也是系统用来存放关于进程的统计信息的地方。 2、地址空间。它包含所有可执行模块或DLL模块的代码和数据。它还包含动态内存分配的空间。如线程堆栈和堆分配空间。 n 进程是不活泼的。进程从来不执行任何东西,它只是线程的容器。若要使进程完成某项操作,它必须拥有一个在它的环境中运行的线程,此线程负责执行包含在进程的地址空间中的代码。 n 单个进程可能包含若干个线程,这些线程都“同时” 执行进程地址空间中的代码。 n 每个进程至少拥有一个线程,来执行进程的地址空间中的代码。当创建一个进程时,操作系统会自动创建这个进程的第一个线程,称为主线程。此后,该线程可以创建其他的线程。 AfxSocketInit: 初始化socket,调用WSAStartup,并保证在程序结束前调用WSAClear. 在InitInstance里调用这个函数.(须引用afxsock.h) 一个线程的回调函数可以设为类的内部函数,这个函数必须是静态的. 当线程创建后,需马上使用CloseHandle关闭句柄,减少线程引用计数 当CreateEvent的第二个参数为FALSE, WaitForSingleObject执行后事件为变为无信号,但不会自动设置事件为有信号,必须使用SetEvent设置事件为有信号. WSAAsyncSelect:将一个网络事件和一个消息关联.一般用于异步操作.WSASelectEvent结合WSAWaitForMultipleEvents也可以异步操作. 如果出现了不应该编译不过得情况,可以尝试删除DEBUG文件夹 可以使用WSAAsyncSelect来异步处理SOCKET数据: 1. 使用WSAAsyncSelect,将一个网络事件和一个自定义消息相关联 2. 将自定义消息映射到一个函数 3. 在消息响应函数里根据事件的类型,分别处理. Wparam指定发生事件的socket, lparam低半子指定事件类型(比如FD_READ,FD_WRITE,FD_CLOSE之类的), lparam高半子指示了错误代码. gethostbyname: 根据主机名获得IP地址 gethostbyaddr:根据IP地址获得主机名 进程间通信: 1. 剪贴板 2. 匿名管道 3. 命名管道 4. 邮槽 获得鼠标按键的数目: GetSystemMetrics(SM_CMOUSEBUTTONS) CalcWindowRect:根据客户区域大小,计算出需要的窗口大小.必须在窗口创建之后使用. CDC:: DeflateRect: 通过将CRect的边向其中心移动来缩小它 CRect::PtInRect: 判断某点是否位于RECT内 MB_SYSTEMMODAL: 如果MessageBox使用了MB_SYSTEMMODAL标志,则对话框在所有窗口的最上面,即便切换到了其他程序. AfxGetApp()->LoadStandardCursor(IDC_ARROW):获得windows预定义的光标 AfxGetApp()->LoadStandardIcon(IDI_WINLOGO):获得windows预定义的图标 AfxRegisterWndClass: 注册一个窗口类.其中的背景颜色可以用(HBRUSH)(COLOR_3DFACE + 1)这种方式获得windows预定义的颜色,这里的颜色就是COLOR_3DFACE,注意要+1; GetSysColor: 通过windows预定义颜色的序号获得RGB表示的颜色. CreateEx: 如果要使窗口不能缩放,需要从dwStyle中移除WS_THICKFRAME 非客户区鼠标消息:类似于客户区鼠标消息,只是加了一个NC,如WM_NCLBUTTONDOWN,对应的处理函数为OnNcLButtonDown, 处理函数可以捕获鼠标是否在标题栏,关闭按钮,菜单栏,最大化按钮,边框等位置. 如果类要从CWnd派生,需要覆写PostNcDestroy函数,并在函数实现里删除自身delete this. WM_NCHITTEST: 窗口在接收到一个客户区或非客户区鼠标消息前,先接收到光标的屏幕坐标和WM_NCHITTEST消息,基于这点,可以在处理鼠标消息前做更多的工作. TrackMouseEvent: 可以捕获鼠标的离开和一定时间静止未动.在使用前需要引用winuser.h,并引入函数: extern "C" WINUSERAPI BOOL WINAPI TrackMouseEvent(LPTRACKMOUSEEVENT lpEventTrack); 在响应了WM_MOUSELEAVE和WM_MOUSEHOVER消息后必须重新调用TrackMouseEvent. SystemParametersinfo: 获得系统参数. SetCapture: 捕获鼠标.捕获鼠标后即使光标移出了窗口,仍然可以接收鼠标消息.,一般在XbuttonDown里SetCapture,xButtonUp里ReleaseCapture; dc.SetROP2(R2_NOT):一次擦除背景色,两次还原背景色 在移动鼠标时,windows通过重画光标的背景把光标从旧位置上清除,然后给窗口发送包含命中测试代码的WM_SETCURSOR消息,对此消息系统的默认响应是调用::SetCursor. 改变光标的两种方法: 1. 在注册WNDCLASS时 2. 使用SetCursor来响应WM_SETCURSOR SetTextAlign:设置文本对齐方式. 显示与隐藏光标: ShowCursor(TRUE),ShowCursor(FALSE) 获得光标的位置: GetCursorPos, GetMessagePos, 他们返回的是屏幕坐标,可以用ScreenToClient转换为客户区坐标. 将光标固定在一个区域: ClipCursor,释放时ClipCursor(NULL); WM_SETFOCUS: 获得焦点 WM_KILLFOCUS: 失去焦点 wmd.SetFocus(): 设置焦点 CWnd::GetFocus(): 获得焦点 一般按键消息的顺序: 1. WM_KEYDOWN 2. WM_CHAR 3. WM_KEYUP ALT和F10是”系统键”,他们只会产生WM_SYSKEYDOWN和WM_SYSKEYUP.在ALT按着的同时,别的键被按下,也会产生WM_SYSKEYDOWN和WM_SYSKEYUP消息. GetKeyState: 检查某个键的状态,如果是按下返回负值,否则返回非负值.当用来检查Num Look,Caps Lock,Scroll Lock是否处于激活状态可以这样:::GetKeyState(VK_NUMLOCK) & 0X01,返回非零值说明按键激活. GetKeyState只能在键盘消息处理程序里使用.如果要在别的地方获得按键状态,要使用GetAsyncKeyState. 在VS2012里,devguid.h定义了常用的GUID。 调用dll的两种方法: 1.连接lib (#pragma comment(lib,"xxx.lib")),包含对应的.h头文件(#include "xxx.H").lib文件在编译dll文件时自动生成 2.使用LoadLibrary加载dll文件,使用GetProcAddress获得函数.使用FreeLibrary释放由LoadLibrary加载的dll文件
CONTAINING_RECORD(address, type, field):根据成员的地址得到结构的地址,如: BOOL
GetTextMetrics(
HDC hdc, // handle to device context
LPTEXTMETRIC lptm // pointer to text metrics structure
);
EXPORTS
add @ 1
#include
typedef int(*lpAddFun)(int, int); //宏定义函数指针类型
int main(int argc, char *argv[])
{
HINSTANCE hDll; //DLL句柄
lpAddFun addFun; //函数指针
hDll = LoadLibrary("..\\Debug\\dllTest.dll");
if (hDll != NULL)
{
addFun = (lpAddFun)GetProcAddress(hDll, "add");
if (addFun != NULL)
{
int result = addFun(2, 3);
printf("%d", result);
}
FreeLibrary(hDll);
}
return 0;
}
#define WINAPI __stdcall //这就是传说中的WINAPI
#define WINAPIV __cdecl
#define APIENTRY WINAPI //DllMain的入口就在这里
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
#pragma comment(lib,"dllTest.lib")
extern int _declspec(dllimport) dllGlobalVar; //用_declspec(dllimport)导入
int main(int argc, char *argv[])
{
printf("%d ", dllGlobalVar);
dllGlobalVar = 1; //这里就可以直接使用, 无须进行强制指针转换
printf("%d ", dllGlobalVar);
return 0;
}
struct CIOCPBuffer //per-i/o
{
WSAOVERLAPPED ol;
SOCKET sClient; //AcceptEx接受的客户方套接字
char *buff; //I/O缓冲区(使用的)大小
int nLen;
ULONG nSequenceNumber; //此I/O序列号
int nOperation; //操作类型
#define OP_ACCEPT 1
#define OP_WRITE 2
#define OP_READ 3
CIOCPBuffer *pNext;
};
CIOCPBuffer * pBuffer = CONTAINING_RECORD(lpol,CIOCPBuffer,ol);