VC杂记

获得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()并返回,所以pThreadpApp这两个指针是一样的.

再来看AfxGetApp()的实现代码:

_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()

{ return afxCurrentWinApp; }

看下afxCurrentWinApp的实现(AFXWIN.H):

#define afxCurrentWinApp AfxGetModuleState()->m_pCurrentWinApp

从第3步以及本步的以上描述可以得出:实际上pThreadpApp这两个指针实际上就是this指针,this指针指向实际应用程序,也就是theApp.

6. InitInstance()函数:

再看第5步的AfxWinMain的实现,其中的pThread->InitInstance为我们自己程序的xxxApp(其实也可以说是theApp)的方法.

7. 注册类的函数AfxEndDeferRegisterClass(WINCORE.CPP).

8. MFC应用程序实际上有两个窗口,其中一个是CMainFrame类的对象代表的应用程序框架窗口,该类有一个PreCreateWindow,它在窗口产生之前被调用.CmainFram只是仅仅调用了父类CFrameWndPreCreateWindow(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);

}

效果如下,画的线未影响文字部分

clip_image002

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(类似DelphiTabSheet):

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:

“用该成员变量去存储你的线程主窗口对象。当和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,则此窗口的所有者就是桌面。

CWndCreate方法用于创建一个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);

 

 

posted on 2013-10-08 14:52  easy33  阅读(469)  评论(0编辑  收藏  举报

导航