获得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”<<endl<<”b”<<endl.
当子类创建时,会先调用基类的构造函数,然后再调用子类函数。在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<int> test;//声明一个存放int类型的容器
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<<xxx
cerr<<xxx
在输出时可以使用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"<<endl;
}
};
声明一个纯虚函数:
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:设置插入符的位置
获得字体的度量信息:
BOOL
GetTextMetrics(
HDC hdc, // handle to device context
LPTEXTMETRIC lptm // pointer to text metrics structure
);
使用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
EXPORTS
add @ 1
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 <stdio.h>
#include <windows.h>
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;
}
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 //这就是传说中的回调函数
#define WINAPI __stdcall //这就是传说中的WINAPI
#define WINAPIV __cdecl
#define APIENTRY WINAPI //DllMain的入口就在这里
#define APIPRIVATE __stdcall
#define PASCAL __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 <stdio.h>
#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;
}
如果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 相关的窗口被关闭后,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 <CPoint,CPoint&> m_Array:传递参数时传递的是指针,推荐使用
CArray <CPoint,CPoint> m_Array:传递参数时传递的是对象.
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):根据成员的地址得到结构的地址,如:
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);