mfc基础教程(学习笔记)

目录

mfc基础教程

第一章:CDC的使用

第二章:文本编程

第三章:菜单

第四章:对话框

第五章:对话框(二)

第六章:样式

第七章:创建兼容DC

第八章:图形的保存与重绘

第九章:文件

第十章:文档与串行化

第十一章:网络编程

第十二章:多线程与聊天室程序的创建

第十三章:线程同步与异步套接字编程

第十四章:进程间通信

第十五章:ActiveX控件

第十六章:动态链接库

第十七章:HOOK和数据库访问


mfc基础教程

编写教程的方法:

  • 始终以第一个实现的案例为基础。

  • 添加扩展功能使用小圆点表示。

  • 添加的功能需要标注添加的位置所在。

第一章:CDC的使用

    void CMfcStudyView::OnLButtonDown(UINT nFlags, CPoint point)
    {
        beginPoint = point;
        CView::OnLButtonDown(nFlags, point);
    }
    
    
    void CMfcStudyView::OnLButtonUp(UINT nFlags, CPoint point)
    {
        CClientDC dc(this);
        dc.MoveTo(beginPoint);
        dc.LineTo(point);
        CView::OnLButtonUp(nFlags, point);
    }
  • 构造DC与框架窗口相关

    CClientDC dc(GetParent());//10
​
- 构造CWindowDC
    CWindowDC dc(this);//10
    CWindowDC dc(GetDesktopWindow());//获取windows桌面窗口
  • 使用CPen

    CClientDC dc(this);
    CPen pen;
    pen.CreatePen(PS_SOLID,1,RGB(255,0,0));
    CPen *oldPen = dc.SelectObject(&pen);
    dc.MoveTo(beginPoint);
    dc.LineTo(point);
    dc.SelectObject(pen);
  • 使用CBrush

    CClientDC dc(this);
    CBrush brush;
    brush.CreateSolidBrush(RGB(255,0,0));
    dc.FillRect(CRect(beginPoint,point),&brush);
  • 创建位图画刷

    CClientDC dc(this);
    CBitmap bitmap;
    bitmap.LoadBitmap(IDB_BITMAP1);
    CBrush brush(&bitmap);
    dc.FillRect(CRect(beginPoint,point),&brush);
  • 创建透明画刷

    CClientDC dc(this);
    CBrush *brush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));//将HBRUSH转化为CBrush对象
    CBrush *oldBrush = dc.SelectObject(brush);
    dc.Rectangle(CRect(beginPoint,point));
    dc.SelectObject(oldBrush);
  • 设置绘图模式

    CClientDC dc(this);
    CBrush brush;
    brush.CreateSolidBrush(RGB(255,0,0));
    dc.SetROP2(R2_NOT);//像素取反
    CBrush* oldBrush = dc.SelectObject(&brush);
    dc.Rectangle(CRect(beginPoint,point));
    dc.SelectObject(oldBrush);

第二章:文本编程

创建插入符

int CMfcStudyView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CView::OnCreate(lpCreateStruct) == -1)
        return -1;
    TEXTMETRIC tm;
    CClientDC dc(this);
    dc.GetTextMetrics(&tm);
    CreateSolidCaret(tm.tmAveCharWidth/8,tm.tmHeight+tm.tmExternalLeading);
    ShowCaret();
    return 0;
}
  • 创位图插入符

	m_icon.LoadBitmap(IDB_BITMAP2);
	CreateCaret(&m_icon);
  • 绘制字符串

	CString str = L"Hello world";
	pDC->TextOut(50,50,str);
  • 使用字符串资源

str.LoadString(IDS_STRINGHELLO);
  • 使用裁剪区

	CString str = L"你好时间";
	CSize size = pDC->GetTextExtent(str);//获取指定字符串的大小
	pDC->TextOut(100,100,str);

	pDC->BeginPath();
	pDC->Rectangle(100,100,100+size.cx,100+size.cy);
	pDC->EndPath();
	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);
	}
  • 绘制平滑显示

void CMfcStudyView::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	static int width = 0;
	CClientDC dc(this);
	dc.SetTextColor(RGB(255,0,0));
	CString str = L"Hello word";
	CSize size = dc.GetTextExtent(str);
	if(width>size.cx){
		width = 0;
	}
	width++;
	dc.DrawText(str,CRect(100,100,100+width,100+size.cy),DT_LEFT);
	CView::OnTimer(nIDEvent);
}

第三章:菜单

  • 添加一个菜单项,添加命令处理函数

void CMfcStudyView::OnTest()
{
	AfxMessageBox(L"click Test");
}
  • 选择按钮

	GetMenu()->GetSubMenu(0)->CheckMenuItem(0,MF_BYPOSITION|MF_CHECKED);
	GetMenu()->GetSubMenu(0)->CheckMenuItem(ID_FILE_NEW,MF_BYCOMMAND|MF_CHECKED);
  • 设置默认按钮

	GetMenu()->GetSubMenu(0)->SetDefaultItem(1,TRUE);
	GetMenu()->GetSubMenu(0)->SetDefaultItem(ID_FILE_OPEN);
  • 设置按钮的点击图标

	CString str;
	str.Format(L"x=%d,y=%d",GetSystemMetrics(SM_CXMENUCHECK),
		GetSystemMetrics(SM_CYMENUCHECK));
	//MessageBox(str);获取点击按钮的图标大小
	bitmap.LoadBitmap(IDB_BITMAP2);
	GetMenu()->GetSubMenu(3)->SetMenuItemBitmaps(1,MF_BYPOSITION,&bitmap,NULL);
  • 自定义Menu

	SetMenu(NULL);
	menu.LoadMenu(IDR_MAINFRAME);
	SetMenu(&menu);
	menu.Detach();//让menu与系统脱离联系
  • 命令更新

void CAboutDlg::OnUpdateFileClose(CCmdUI *pCmdUI)
{
	pCmdUI->Enable();
}
  • 定义右键弹出菜单功能

void CL1View::OnRButtonUp(UINT nFlags, CPoint point)
{
	CMenu menu;
	menu.LoadMenu(IDR_MENU1);
	ClientToScreen(&point);
	CMenu* pop = menu.GetSubMenu(0);
	pop->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,point.x,point.y,GetParent());

	CView::OnRButtonUp(nFlags, point);
}
  • 动态添加、删除、插入菜单

	CMenu menu;
	menu.CreatePopupMenu();
	//GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,L"WinSun");添加弹出菜单
	GetMenu()->InsertMenu(1,MF_BYPOSITION|MF_POPUP,(UINT)menu.m_hMenu,L"WinSun");//插入菜单
	menu.AppendMenu(MF_STRING,111,L"Hello");
	menu.AppendMenu(MF_STRING,112,L"World");
	menu.AppendMenu(MF_STRING,113,L"Welcome");//添加菜单项
	//GetMenu()->GetSubMenu(0)->AppendMenu(MF_STRING,114,L"Welcome");
	//GetMenu()->GetSubMenu(0)->InsertMenu(1,MF_BYPOSITION,115,L"Bobo");
	GetMenu()->DeleteMenu(1,MF_BYPOSITION);//删除菜单
	GetMenu()->GetSubMenu(3)->DeleteMenu(1,MF_BYPOSITION);//删除菜单项
	menu.Detach();
  • 自动添加电话本程序

第一步:定义按键响应函数

void CL3View::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	static UINT beginID = 111;
	if(0x0d==nChar){
		if(beginID==111){
			menu.CreatePopupMenu();
			GetParent()->GetMenu()->AppendMenu(MF_POPUP,(UINT)menu.m_hMenu,L"电话");
			GetParent()->DrawMenuBar();
		}
		menu.AppendMenu(MF_STRING,beginID,phone);
		//GetParent()->GetMenu()->GetSubMenu(4)->AppendMenu(MF_STRING,beginID,phone);并不清楚为什么此函数不能执行成功
		beginID++;
		phoneArray.Add(phone);
		phone.Empty();
	}else{
		phone+=(WCHAR)nChar;
	}
	Invalidate();
	CView::OnChar(nChar, nRepCnt, nFlags);
}
第二步:获取按键享有权

m_bAutoMenuEnable = FALSE;

第三步:重写OnCommand函数
BOOL CMainFrame::OnCommand(WPARAM wParam, LPARAM lParam)
{
	CString str;
	str.Format(L"%d,%d",wParam,lParam);
	MessageBox(str);
	return CFrameWnd::OnCommand(wParam, lParam);
}

第四章:对话框

  • 使用对话框

	//dlg.DoModal();//产生一个模态对话框
	dlg.Create(IDD_DIALOG1,this);
	dlg.ShowWindow(SW_SHOW);//定义非模态对话框
  • 修改对话框中的静态文本

第一步:修改静态文本的ID。
第二步:修改Notify为true,向父窗口发送消息。


void Dlg1::OnStnClickedEdit1()
{
	static bool isSecond=false;
	if(isSecond==false){
		isSecond=true;
		GetDlgItem(IDC_EDIT1)->SetWindowText(L"点击按钮");
	}else{
		GetDlgItem(IDC_EDIT1)->SetWindowText(L"Static");
		isSecond=false;
	}
}
  • 动态生成按钮

	static bool isBtnCreated = false;
	if(isBtnCreated==false){
		btn.Create(L"Hello",BS_PUSHBUTTON|WS_VISIBLE|WS_CHILD,CRect(0,0,100,100),this,123);
		isBtnCreated = true;
	}else{
		btn.DestroyWindow();
		isBtnCreated = false;
	}
	return TRUE;
  • 使用对话框设计一个计算器

方法1:获取控件,设置控件
	WCHAR ch1[10],ch2[10],ch3[10];
	GetDlgItem(IDC_EDIT1)->GetWindowText(ch1,sizeof(ch1));
	GetDlgItem(IDC_EDIT2)->GetWindowText(ch2,sizeof(ch2));
	int num1 = _wtoi(ch1);
	int num2 = _wtoi(ch2);
	int num3 = num1+num2;
	_itow(num3,ch3,sizeof(ch3));
	GetDlgItem(IDC_EDIT3)->SetWindowText(ch3);
方法2:获取控件文本
	WCHAR ch1[10],ch2[10],ch3[10];
	GetDlgItemText(IDC_EDIT1,ch1,sizeof(ch1));
	GetDlgItemText(IDC_EDIT2,ch2,sizeof(ch2));
	int num1 = _wtoi(ch1);
	int num2 = _wtoi(ch2);
	int num3 = num1+num2;
	_itow(num3,ch3,sizeof(ch3));
	SetDlgItemText(IDC_EDIT3,ch3);
方法3:获取控件数据
	int num1,num2,num3;
	num1 = GetDlgItemInt(IDC_EDIT1);
	num2 = GetDlgItemInt(IDC_EDIT2);
	num3 = num1+num2;
	SetDlgItemInt(IDC_EDIT3,num3);
方法4:DDX数据关联
注意使用UpdateData函数,当传入为true,为获取数据,当传入为false时,为写入数据,默认的为true。
方法5:DDX控件关联
	int num1,num2,num3;
	WCHAR ch1[10],ch2[10],ch3[10];
	m_edit1.GetWindowText(ch1,sizeof(ch1));
	m_edit1.GetWindowText(ch2,sizeof(ch2));
	num1 = _wtoi(ch1);
	num2 = _wtoi(ch2);
	num3 = num1 + num2;
	_itow(num3,ch3,sizeof(ch3));
	m_edit3.SetWindowText(ch3);
方法6:从父窗口发送消息(这里的10表示的是10进制)
	WCHAR ch1[10],ch2[10],ch3[10];
	::SendMessage(GetDlgItem(IDC_EDIT1)->m_hWnd,WM_GETTEXT,10,(LPARAM)ch1);
	::SendMessage(GetDlgItem(IDC_EDIT2)->m_hWnd,WM_GETTEXT,10,(LPARAM)ch2);
	int num1 = _wtoi(ch1);
	int num2 = _wtoi(ch2);
	int num3 = num1 + num2;
	_itow(num3,ch3,sizeof(ch3));
	::SendMessage(GetDlgItem(IDC_EDIT3)->m_hWnd,WM_SETTEXT,10,(LPARAM)ch3);
方法7:从对话框发送消息
	WCHAR ch1[10],ch2[10],ch3[10];
	SendDlgItemMessage(IDC_EDIT1,WM_GETTEXT,10,(LPARAM)ch1);
	SendDlgItemMessage(IDC_EDIT2,WM_GETTEXT,10,(LPARAM)ch2);
	int num1 = _wtoi(ch1);
	int num2 = _wtoi(ch2);
	int num3 = num1 + num2;
	_itow(num3,ch3,sizeof(ch3));
	SendDlgItemMessage(IDC_EDIT3,WM_SETTEXT,10,(LPARAM)ch3);
  • 伸缩对话框的实现

void Dlg2::OnBnClickedStretch()
{
	static bool isStretch = false;
	if(isStretch == false){
		isStretch = true;
		SetWindowPos(NULL,0,0,m_windowRect.Width(),m_smallHeight,SWP_NOMOVE|SWP_NOZORDER);
	}else{
		isStretch = false;
		SetWindowPos(NULL,0,0,m_windowRect.Width(),m_windowRect.Height(),SWP_NOMOVE|SWP_NOZORDER);
	}
}

BOOL Dlg2::OnInitDialog()
{
	CRect m_smallRect;
	CDialogEx::OnInitDialog();
	isStrtch = false;
	GetWindowRect(&m_windowRect);
	GetDlgItem(IDC_SEPARATOR)->GetWindowRect(&m_smallRect);
	m_smallHeight = m_smallRect.bottom - m_windowRect.top;
	return TRUE;
}
  • 使用回车键切换输入焦点

第一步:将对话框的Multiline改为true,同意接受多行文本
第二步:重载OnOK函数
void Dlg2::OnOK()
{
	//CDialogEx::OnOK();
}
第三步:书写文本框的窗口处理函数
LRESULT CALLBACK EditProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	if(uMsg == WM_CHAR && LOWORD(wParam) == 0x0d){
		::SetFocus(GetNextWindow(hwnd,GW_HWNDNEXT));
		return 1;
	}else{
		return prevProc(hwnd,uMsg,wParam,lParam);
	}
}
第四步:在初始化对话框的时候修改文本框的窗口处理函数
BOOL Dlg2::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	prevProc = (WNDPROC)SetWindowLong(GetDlgItem(IDC_EDIT1)->m_hWnd,GWL_WNDPROC,(LONG)EditProc);
	return TRUE;
}
  • 关于焦点转移的其他方法

    1. 参照GetWindow方法

    2. 参照GetNextDlgItem方法

    3. 重写OnOK函数,修改默认窗口处理函数,使用GetFocus函数获取焦点,再调用GetNextWindow获取下一个窗口,然后使用SetFocus方法设置焦点。

第五章:对话框(二)

  • 逃跑按钮的实现

第一步:
先定义一个类,继承自CButton类。
第二步:
将对话框中的两个按钮绑定到成员变量中,记住,成员变量的类型为新创建的按钮类。
第三步:
在新的类中定义WM_ONMOUSEMOVE消息的事件响应函数。
void CWeixinBtn::OnMouseMove(UINT nFlags, CPoint point)
{
	ShowWindow(SW_HIDE);
	m_btn->ShowWindow(SW_SHOW);//其中m_btn为另一个按钮的地址
	CButton::OnMouseMove(nFlags, point);
}
  • 属性表单

第一步:定义属性表单
新建属性表单资源
第二步:为属性表单添加控件
1. 需要注意的是属性下来框需要拉大一些。
2. 为每一个属性页定义一个类,其继承CPropertyPage类
3. 定义属性表单类,其继承CPropertySheet类,在其构造函数中添加属性页资源
CPropSheet::CPropSheet(UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
	:CPropertySheet(nIDCaption, pParentWnd, iSelectPage)
{
	AddPage(&m_prop1);
	AddPage(&m_prop2);
	AddPage(&m_prop3);
}

CPropSheet::CPropSheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
	:CPropertySheet(pszCaption, pParentWnd, iSelectPage)
{
	AddPage(&m_prop1);
	AddPage(&m_prop2);
	AddPage(&m_prop3);
}
第三步:显示属性表单
	CPropSheet sheet(L"属性表单");
	sheet.DoModal();
  • 向导

1. 创建向导和创建属性表单一样,但是在其DoModel前调用:
	sheet.SetWizardMode();
2. 修改属性表单中的按钮——分别在每一个属性页类中重载OnSetActive方法
BOOL CProp1::OnSetActive()
{
	((CPropertySheet*)GetParent())->SetWizardButtons(PSWIZB_NEXT);
	return CPropertyPage::OnSetActive();
}
  • 向导判断用户是否选择(如果没有选择就禁止进入下一个页面)

  1. 判断用户是否选择单选按钮

1. 对于单选按钮,需要将其第一个单选框的radiobox的group属性设置为true
2. 在类向导中找到radiobox,将其关联为int类型的成员变量
3. 当用户点击Next的时候,需要判断单选按钮是否被选择,因此需要重载OnWizardNext函数:
LRESULT CProp1::OnWizardNext()
{
	UpdateData();
	if(m_occupation==-1){
		MessageBox(L"请选择你的职业!");
		return -1;
	}
	return CPropertyPage::OnWizardNext();
}
  • 添加列表框的选项

	((CListBox*)GetDlgItem(IDC_LIST1))->AddString(L"北京");
	((CListBox*)GetDlgItem(IDC_LIST1))->AddString(L"天津");
	((CListBox*)GetDlgItem(IDC_LIST1))->AddString(L"上海");
  • 组合框添加选项

	((CComboBox*)GetDlgItem(IDC_COMBO1))->AddString(L"1000月以下");
	((CComboBox*)GetDlgItem(IDC_COMBO1))->AddString(L"1000-2000元");
	((CComboBox*)GetDlgItem(IDC_COMBO1))->AddString(L"2000-3000元");
	((CComboBox*)GetDlgItem(IDC_COMBO1))->AddString(L"3000元以上");
	((CComboBox*)GetDlgItem(IDC_COMBO1))->SetCurSel(0);
  • 将用户的选择输出到父窗口中

1. 在表单类中重写OnWizardFinish方法,将向导中的数据写入成员变量中。
2. 在父窗口中判断DoModal的返回值,是否为ID_WIZFINISH,如果为真,获取CPropSheet中各个属性页中保存的属性值。

第六章:样式

  • 修改窗口的外观

1. 在创建窗口之前改变外观

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
	if( !CFrameWnd::PreCreateWindow(cs) )
		return FALSE;
	cs.cx = 300;
	cs.cy = 200;
	cs.style &=~FWS_ADDTOTITLE;
	//cs.style = WS_OVERLAPPEDWINDOW;
	cs.lpszName = L"http://www.baidu.com";
	return TRUE;
}
2. 在创建窗口之后改变外观


	SetWindowLong(m_hWnd,GWL_STYLE,WS_OVERLAPPEDWINDOW);
	SetWindowLong(m_hWnd,GWL_STYLE,GetWindowLong(m_hWnd,GWL_STYLE)&~WS_MAXIMIZEBOX);
  • 修改窗口类的外观(图标、光标、背景)

1. 方法一:重写一个窗口类,用自己定义的窗口类生成程序

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
	if( !CFrameWnd::PreCreateWindow(cs) )
		return FALSE;
	WNDCLASS wndcls;
	wndcls.cbClsExtra = 0;
	wndcls.cbWndExtra = 0;
	wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wndcls.hCursor = LoadCursor(NULL,IDC_HELP);
	wndcls.hIcon = LoadCursor(NULL,IDI_ERROR);
	wndcls.hInstance = AfxGetInstanceHandle();
	wndcls.lpfnWndProc = ::DefWindowProcW;
	wndcls.lpszClassName = L"index";
	wndcls.lpszMenuName = NULL;
	wndcls.style = CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&wndcls);
	cs.lpszClass = L"index";
	return TRUE;
}
同样在View类中使用窗口类
BOOL CL7View::PreCreateWindow(CREATESTRUCT& cs)
{
	cs.lpszClass =L"index";
	return CView::PreCreateWindow(cs);
}
2. 使用AfxRegisterWndClass直接修改窗口样式

cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW,0,0,LoadIcon(NULL,IDI_WARNING));
3. 在窗口生成之后修改窗口的样式

SetClassLong(m_hWnd,GCL_HBRBACKGROUND,(LONG)GetStockObject(BLACK_BRUSH));
  • 工具栏编程

1. 创建工具栏资源
2. 显示工具栏
	
if (!m_newToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_RIGHT | CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
		!m_newToolBar.LoadToolBar(IDR_TOOLBAR1))
	{
		TRACE0("未能创建工具栏\n");
		return -1;      // 未能创建
	}
	m_newToolBar.EnableDocking(CBRS_ALIGN_ANY);
	DockControlBar(&m_newToolBar);
3. 控制工具栏的显示
	
    if(m_newToolBar.IsWindowVisible()){
		m_newToolBar.ShowWindow(SW_HIDE);
	}else{
		m_newToolBar.ShowWindow(SW_SHOW);
	}
	RecalcLayout();//重新计算
	DockControlBar(&m_newToolBar);//停靠工具栏
3.1 控制栏的显示


ShowControlBar(&m_newToolBar,!m_newToolBar.IsWindowVisible(),FALSE);
  • 在状态栏中显示系统时间

1. 先定义字符串资源——时间和进度条
2. 在状态指示器中添加新添加的资源

static UINT indicators[] =
{
	ID_SEPARATOR,           // 状态行指示器
	ID_INDICATOR_TIMER,
	ID_INDICATOR_PROGRESS,
	ID_INDICATOR_CAPS,
	ID_INDICATOR_NUM,
	ID_INDICATOR_SCRL,
};
3. 设置定时器,并在定时器时间相应函数中修改时间显示

void CMainFrame::OnTimer(UINT_PTR nIDEvent)
{
	CTime t = CTime::GetCurrentTime();
	CString str = t.Format(L"%H:%M:%S");
	CClientDC dc(this);
	CSize size = dc.GetTextExtent(str);
	m_wndStatusBar.SetPaneInfo(1,ID_INDICATOR_TIMER,SBPS_NORMAL,size.cx);
	m_wndStatusBar.SetPaneText(1,str);
	CFrameWnd::OnTimer(nIDEvent);
}
  • 在状态栏中显示进度条

1. 使用自定义消息相应函数处理进度栏资源未产生的问题

1.1 定义消息类型
\\#define  UM_PROGRESS WM_USER+1
1.2 定义消息相应函数
	afx_msg LRESULT OnProgress(WPARAM wParam,LPARAM lParam);
	DECLARE_MESSAGE_MAP()
1.3 定义消息绑定
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
     	ON_MESSAGE(UM_PROGRESS,OnProgress)
END_MESSAGE_MAP()
1.4 消息相应函数内容
LRESULT CMainFrame::OnProgress(WPARAM wParam,LPARAM lParam)
{
	CRect rect;
	m_wndStatusBar.GetItemRect(2,&rect);
	m_progress.Create(WS_CHILD|WS_VISIBLE,rect,&m_wndStatusBar,123);
	m_progress.SetPos(50);
	return 0;
}
1.5 发送消息(CMainFrame::OnCreate函数中最后书写)
	PostMessage(UM_PROGRESS);
2. 使用重绘窗口显示

void CMainFrame::OnPaint()
{
	CPaintDC dc(this); // device context for painting
	CRect rect;
	m_wndStatusBar.GetItemRect(2,&rect);
	if(!m_progress.m_hWnd)
		m_progress.Create(WS_CHILD|WS_VISIBLE|PBS_SMOOTH,rect,&m_wndStatusBar,123);
	else
		m_progress.MoveWindow(rect);
	m_progress.SetPos(50);
}

void CMainFrame::OnTimer(UINT_PTR nIDEvent)
{
	m_progress.StepIt();//每秒钟向前走一格
}
  • 将当前的鼠标位置位置显示在状态栏中

void CL7View::OnMouseMove(UINT nFlags, CPoint point)
{
	CString str;
	str.Format(L"x=%d,y=%d",point.x,point.y);
	//((CMainFrame*)GetParent())->m_wndStatusBar.SetWindowText(str);
	//((CMainFrame*)GetParent())->SetMessageText(str);
	//((CMainFrame*)GetParent())->GetMessageBar()->SetWindowText(str);
	GetParent()->GetDescendantWindow(AFX_IDW_STATUS_BAR)->SetWindowText(str);
	CView::OnMouseMove(nFlags, point);
}

第七章:创建兼容DC

  • 使用工具栏绘制相应的图形

1. 添加菜单
2. 书写消息处理函数

void CL1View::OnLButtonDown(UINT nFlags, CPoint point)
{
	m_oriPoint = point;
	CView::OnLButtonDown(nFlags, point);
}

void CL1View::OnLButtonUp(UINT nFlags, CPoint point)
{
	CClientDC dc(this);
	CPen pen(PS_SOLID,1,RGB(255,0,0));
	dc.SelectObject(&pen);
	CBrush *brush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
	dc.SelectObject(brush);
	switch(m_type){
	case 1:
		dc.SetPixel(point,RGB(255,0,0));
		break;
	case 2:
		dc.MoveTo(m_oriPoint);
		dc.LineTo(point);
		break;
	case 3:
		dc.Rectangle(CRect(m_oriPoint,point));
		break;
	case 4:
		dc.Ellipse(CRect(m_oriPoint,point));
		break;
	}
	CView::OnLButtonUp(nFlags, point);
}
  • 使用设置对话框

void CL1View::OnSetting()
{
	CSettingDlg dlg;
	dlg.m_linewidth = m_linewidth;
	if(IDOK==dlg.DoModal()){
		m_linewidth = dlg.m_linewidth;
	}
}
  • 使用颜色对话框

void CL1View::OnColorsetting()
{
	CColorDialog dlg;
	dlg.m_cc.rgbResult = m_color;
	dlg.m_cc.Flags |= CC_RGBINIT;
	if(IDOK == dlg.DoModal()){
		m_color = dlg.m_cc.rgbResult;
	}
}
  • 使用字体对话框

void CL1View::OnFontsetting()
{
	CFontDialog dlg;
	if(IDOK == dlg.DoModal()){
		if(m_font.m_hObject)
			m_font.DeleteObject();//删除CGDI对象和WINDOWS GDI对象之间的关联
		m_font.CreateFontIndirect(dlg.m_cf.lpLogFont);
		m_strFontName = dlg.m_cf.lpLogFont->lfFaceName;
		Invalidate();
	}
}
  • 为线型选择显示实例

void CSettingDlg::OnPaint()
{
	UpdateData();
	CPaintDC dc(this);
	CPen pen(m_radio,m_linewidth,RGB(255,0,0));
	dc.SelectObject(&pen);
	CRect rect;
	GetDlgItem(IDC_SAMPLE)->GetWindowRect(&rect);//获取子窗口控件的位置
	ScreenToClient(&rect);//获得位置是屏幕坐标,必须转化为客户区坐标
	dc.MoveTo(rect.left+20,rect.top+rect.Height()/2);
	dc.LineTo(rect.right-20,rect.top+rect.Height()/2);
}
  • 修改对话框控件样式

HBRUSH CSettingDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
	//修改组框的字体颜色和背景颜色、背景模式
	HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
	if(pWnd->GetDlgCtrlID()==IDC_LINESTYLE){
		pDC->SetTextColor(RGB(255,0,0));
		pDC->SetBkMode(TRANSPARENT);
		return m_brush;
	}
	//修改输入框中的字体颜色和背景颜色
	if(pWnd->GetDlgCtrlID()==IDC_LINEWIDTH){
		pDC->SetTextColor(RGB(255,0,0));
		//pDC->SetBkMode(TRANSPARENT);
		pDC->SetBkColor(RGB(0,0,255));
		return m_brush;
	}
	//修改对话框中的字体
	if(pWnd->GetDlgCtrlID()==IDC_TEST){
		pDC->SelectObject(&m_font);
	}
	return hbr;
}
  • 修改对话框中按钮的样式

1. 新建CTestBtn类,继承CButton,并重写DrawItem函数

void CTestBtn::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	UINT uStyle = DFCS_BUTTONPUSH;
	ASSERT(lpDrawItemStruct->CtlType == ODT_BUTTON);
	if (lpDrawItemStruct->itemState & ODS_SELECTED)
		uStyle |= DFCS_PUSHED;
	::DrawFrameControl(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, 
		DFC_BUTTON, uStyle);
	CString strText;
	GetWindowText(strText);
	COLORREF crOldColor = ::SetTextColor(lpDrawItemStruct->hDC, RGB(255,0,0));
	::DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(), 
		&lpDrawItemStruct->rcItem, DT_SINGLELINE|DT_VCENTER|DT_CENTER);
	::SetTextColor(lpDrawItemStruct->hDC, crOldColor);
}
2. 修改按钮的样式Owner Draw为True
3. 将自己的按钮和重写的按钮关联成员变量

一些其他自定义按钮控件推荐使用CButtonST类。

  • 在窗口中贴图

1. 创建位图
2. 创建兼容DC
3. 将位图选到兼容DC中
4. 将兼容DC中的位图贴到当前DC中

BOOL CL1View::OnEraseBkgnd(CDC* pDC)
{
	//在OnEraseBkgnd中写此函数比在OnDraw中运行更快,原因是当执行OnDraw函数时,必须先擦除背景,然后再绘制位图,但是在此函数中没有擦除背景
	CBitmap bitmap;
	bitmap.LoadBitmap(IDB_BITMAP1);

	BITMAP bmp;
	bitmap.GetBitmap(&bmp);
	CDC dcCompatible;
	dcCompatible.CreateCompatibleDC(pDC);
	dcCompatible.SelectObject(&bitmap);
	CRect rect;
	GetClientRect(&rect);
	//pDC->BitBlt(0,0,rect.Width(),rect.Height(),&dcCompatible,0,0,SRCCOPY);
	pDC->StretchBlt(0,0,rect.Width(),rect.Height(),&dcCompatible,
		0,0,bmp.bmWidth,bmp.bmHeight,SRCCOPY);
	return TRUE;
	//return CView::OnEraseBkgnd(pDC);
}

第八章:图形的保存与重绘

  • 使用容器存储绘图信息

1. 定义存储绘图信息的类

class CGraph
{
public:
	CGraph(void);
	CGraph(UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd);
	~CGraph(void);
public:
	CPoint m_ptOrigin;
	CPoint m_ptEnd;
	UINT m_nDrawType;
};
2. 在鼠标弹起的时候保存绘图信息
	
CGraph *graph=new CGraph(m_type,m_oriPoint,point);
	m_ptrArray.Add(graph);
3. 在OnDraw函数取出绘图信息,并开始绘制
	

for(int i=0;i<m_ptrArray.GetSize();++i){
		CPoint begin=((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin;
		CPoint end = ((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd;
		switch(((CGraph*)m_ptrArray.GetAt(i))->m_nDrawType){
		case 1:
			pDC->SetPixel(begin,m_color);
			break;
		case 2:
			pDC->MoveTo(begin);
			pDC->LineTo(end);
			break;
		case 3:
			pDC->Rectangle(CRect(begin,end));
			break;
		case 4:
			pDC->Ellipse(CRect(begin,end));
			break;
		}
	}
  • 创建滚动窗口

1. 将所有的CView修改成CSrcollView
2. 重写OnInitialUpdate,初始化滚动窗口的大小

void CL1View::OnInitialUpdate()
{
	CScrollView::OnInitialUpdate();
	SetScrollSizes(MM_TEXT,CSize(800,600));
}
3. 在鼠标弹起的地方,也就是需要记录鼠标位置的地方将设备坐标转化为逻辑坐标

	OnPrepareDC(&dc);
	dc.DPtoLP(&m_oriPoint);
	dc.DPtoLP(&point);
4. 此时的绘制就会显示到正确的位置上来

  • 使用CMetaFileDC存储绘制过程

1. 定义变量,并初始化

	m_dcMetaFile.Create();
2. 讲绘制在CDC中的命令保存在绘制在MedaFile中

CBrush *brush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
	m_dcMetaFile.SelectObject(brush);
...
	case 1:
		m_dcMetaFile.SetPixel(begin,m_color);
3. 在OnDraw中执行具体绘制

	HMETAFILE hmetaFile;
	hmetaFile = m_dcMetaFile.Close();
	pDC->PlayMetaFile(hmetaFile);
	m_dcMetaFile.Create();
	m_dcMetaFile.PlayMetaFile(hmetaFile);//在源文件中执行绘制命令等同于将原来的绘制命令保存在新创建的MetaFile中
	DeleteMetaFile(hmetaFile);
4. 保存和打开MetaFile文件

void CL1View::OnFileSave()
{
	HMETAFILE hmetaFile;
	hmetaFile = m_dcMetaFile.Close();
	CopyMetaFile(hmetaFile,L"meta.wmf");
	m_dcMetaFile.Create();
	DeleteObject(hmetaFile);
}

void CL1View::OnFileOpen()
{
	HMETAFILE hmetaFile;
	hmetaFile = GetMetaFile(L"meta.wmf");
	m_dcMetaFile.PlayMetaFile(hmetaFile);
	DeleteMetaFile(hmetaFile);
	Invalidate();
}
  • 使用兼容DC

1. 定义兼容DC成员变量

	CDC m_dcCompatible;
2. 创建兼容DC

	if(!m_dcCompatible.m_hDC){
		m_dcCompatible.CreateCompatibleDC(&dc);
		CRect rect;
		GetClientRect(&rect);
		CBitmap bitmap;
		bitmap.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height());
		m_dcCompatible.SelectObject(&bitmap);
		m_dcCompatible.BitBlt(0,0,rect.Width(),rect.Height(),&dc,0,0,SRCCOPY);
	}
...
		m_dcCompatible.SetPixel(begin,m_color);
3. 将兼容DC拷贝到设备描述表中


	CRect rect;
	GetClientRect(&rect);
	pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompatible,0,0,SRCCOPY);

第九章:文件

  • 指针常量和指向常量的指针

  1. 指针常量:char* const ptr指针不可以改变,但是指向的内容可以改变。

  2. 指向常量的指针:const char* ptr指向的内容不可以修改,但是指针可以改变。

  • C语言中的写入文件操作

	//写入文件
	FILE *pFile = fopen("1.txt","w");
	fwrite("http://www.baidu.com",1,strlen("http://www.baidu.com"),pFile);
	//fseek(pFile,0,SEEK_SET);
	//fwrite("ftp:",1,strlen("ftp:"),pFile);
	//fclose(pFile);
	fflush(pFile);//将缓冲区的内容写入文件
  • C语言中读取文件操作

	FILE*pFile = fopen("1.txt","r");
	char ch[100];
	memset(ch,0,100);
	fread(ch,1,100,pFile);
	//MessageBox(ch);
	fclose(pFile);
  • C语言中判断文件长度

	char *pBuf;
	fseek(pFile,0,SEEK_END);
	int len = ftell(pFile);
	pBuf = new char[len+1];
	rewind(pFile);//将文件指针重新移动到文件的开始处
	fread(pBuf,1,len,pFile);
	pBuf[len]=0;
	//MessageBox(ch);
  • 换行回车问题

1. 写入文件

	FILE *pFile = fopen("2.txt","w");
	char ch[3];
	ch[0]='a';
	ch[1]=10;
	ch[2]='b';
	fwrite(ch,1,3,pFile);
	fclose(pFile);
2. 读取文件

	FILE *pFile = fopen("2.txt","r");
	char ch[100];
	fread(ch,1,3,pFile);
	ch[3] = 0;
	//MessageBox(ch);
	fclose(pFile);
注意,在windows系统中,使用文本方式写入文件\n时会自动加上\r,因此写入的是4个字节,但是在读的时候并不会影响读取字节的个数。
使用二进制形式读取和写入文件只需要将b改成rb即可。

  • 向文本文件中写写入98341

	//将数字表示为ASCII表示
	char ch[5];
	ch[0]=9+48;
	ch[1]=9+48;
	ch[2]=9+48;
	ch[3]=9+48;
	ch[4]=9+48;
	//itoa(i,ch,10);
  • c++写入文件

	ofstream ofs("4.txt");
	ofs.write("http://www.baidu.com",strlen("http://www.baidu.com"));
	ofs.close();
  • c++读取文件

	ifstream ifs("4.txt");
	char ch[100];
	memset(ch,0,100);
	ifs.read(ch,100);
	ifs.close();
	MessageBox(ch);
  • win32 API写入文件

	HANDLE hFile;
	hFile = CreateFile(L"5.txt",GENERIC_WRITE,0,NULL,CREATE_NEW,FILE_ATTRIBUTE_NORMAL,NULL);
	DWORD dwWrites;
	WriteFile(hFile,"你好世界!",strlen("你好世界!"),&dwWrites,NULL);
	CloseHandle(hFile);
  • win32 API读取文件

	HANDLE hFile;
	hFile = CreateFile(L"5.txt",GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
	char ch[100];
	DWORD dwReads;
	ReadFile(hFile,ch,100,&dwReads,NULL);
	ch[dwReads]=0;
	CloseHandle(hFile);
  • MFC中提供的对文件写操作

	CFile file(L"6.txt",CFile::modeWrite|CFile::modeCreate);
	file.Write("你好世界",strlen("你好世界"));
	file.Close();
  • MFC中提供的对文件的读操作

	CFile file(L"6.txt",CFile::modeRead);
	char *pBuf;
	DWORD dwFileLen;
	dwFileLen = file.GetLength();
	pBuf = new char[dwFileLen+1];
	pBuf[dwFileLen]=0;
	file.Read(pBuf,dwFileLen);
	file.Close();
  • 增加打开文件对话框和保存为文件对话框

1. 文件保存对话框

	CFileDialog fileDlg(FALSE);
	fileDlg.m_ofn.lpstrTitle = L"我的文件对话框";
	fileDlg.m_ofn.lpstrFilter = L"Text Files(*.txt)\0*.txt\All Files(*.*)\0*.*\0\0";
	fileDlg.m_ofn.lpstrDefExt = L"txt";
	if(IDOK == fileDlg.DoModal()){
		CFile file(fileDlg.GetPathName(),CFile::modeCreate|CFile::modeWrite);
		file.Write("www.baidu.com",strlen("www.baidu.com"));
		file.Close();
	}
2. 打开文件对话框

	CFileDialog fileDlg(TRUE);
	fileDlg.m_ofn.lpstrTitle = L"我的文件对话框";
	fileDlg.m_ofn.lpstrFilter = L"Text Files(*.txt)\0*.txt\0All Files(*.*)\0*.*\0\0";
	if(IDOK == fileDlg.DoModal()){
		CFile file(fileDlg.GetPathName(),CFile::modeRead);
		char *pBuf;
		DWORD dwFileLen;
		dwFileLen = file.GetLength();
		pBuf = new char[dwFileLen+1];
		pBuf[dwFileLen]=0;
		file.Read(pBuf,dwFileLen);
		file.Close();
		MessageBoxA(GetSafeHwnd(),pBuf,"Message",MB_OK);
	}
  • 读写注册文件

	::WriteProfileStringW(L"www.baidu.com",L"admin",L"zhangsan");
	CString str;
	::GetProfileStringW(L"www.baidu.com",L"admin",L"lisi",str.GetBuffer(100),100);
	MessageBox(str);
注意:这里使用的是全局函数,其中WinApp中包含一个WriteProfileString,它能够根据操作系统的不同自己选择写入注册表还是写入注册文件中[HKEY_CURRENT_USER\Software\]。
  • 写注册表

1. 写入字符串类型的值

	//创建注册表项
	HKEY hKey;
	RegCreateKey(HKEY_LOCAL_MACHINE,L"Software\\http://www.baidu.com\\admin",&hKey);
	//设置注册表项
	RegSetValue(hKey,NULL,REG_SZ,L"zhangsan",lstrlen(L"zhangsan"));
	RegCloseKey(hKey);
2. 写入整型对的值


	DWORD dwAge = 30;
	RegSetValueEx(hKey,L"age",0,REG_DWORD,(CONST BYTE*)&dwAge,4);//向注册表中写入整形数据
  • 读注册表

1. 读取字符串类型的值

	LONG lValue;
	RegQueryValue(HKEY_LOCAL_MACHINE,L"Software\\http://www.baidu.com\\admin",NULL,&lValue);\\获取数据长度
	WCHAR *pBuf = new WCHAR[lValue];
	RegQueryValue(HKEY_LOCAL_MACHINE,L"Software\\http://www.baidu.com\\admin",pBuf,&lValue);\\获取数据
	MessageBox(pBuf);
2. 读取整型的值

	RegOpenKey(HKEY_LOCAL_MACHINE,L"Software\\http://www.baidu.com\\admin",&hKey);
	DWORD dwType;
	DWORD dwValue;
	DWORD dwAge;
	RegQueryValueEx(hKey,L"age",0,&dwType,(LPBYTE)&dwAge,&dwValue);
	CString str;
	str.Format(L"age = %d",dwAge);
	MessageBox(str);

其他有关的函数例如RegDeleteKey删除注册表可以查看相关文档,也可以在MSDN中查看“Register Function”查找相关注册表的函数。

第十章:文档与串行化

  • 使用CArchive类串行化输入输出基本数据类型的数据

void CL2View::OnFilewrite()
{
	CFile file(L"1.txt",CFile::modeCreate|CFile::modeWrite);
	CArchive ar(&file,CArchive::store);
	int i = 4;
	char ch = 'a';
	float f = 1.3f;
	CString str(L"www.baidu.com");
	ar<<i<<ch<<f<<str;
}

void CL2View::OnFileread()
{
	CFile file(L"1.txt",CFile::modeRead);
	CArchive ar(&file,CArchive::load);
	int i;
	char ch;
	float f;
	CString str;
	ar>>i>>ch>>f>>str;
}
  • IDR_MAINFRAME结构 IDR_MAINFRAME包含七个字符串结构,每个字符串结构使用\n

  1. 主窗口上标题栏的字符串

  2. 缺省文档的名称

  3. 文档类型的名称,在新建文件时使用

  4. 文档过滤器,在打开文件时使用

  5. 文档扩展名,在打开文件时使用

  6. 有关注册表的信息

  7. 有关注册表的信息

  • 自定义可序列化的类

1. 先定义CGraph类

class CGraph:public CObject
{
	DECLARE_SERIAL(CGraph)\\注意
public:
	CGraph(void);
	CGraph(UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd);
	virtual ~CGraph(void);
	CPoint m_ptOrigin;
	CPoint m_ptEnd;
	UINT m_nDrawType;
	void Serialize(CArchive& ar);
	void Draw(CDC*pDc);
};
2. 实现CGraph类

IMPLEMENT_SERIAL(CGraph,CObject,1)\\注意
void CGraph::Serialize(CArchive& ar)
{
	if(ar.IsStoring()){
		ar<<m_nDrawType<<m_ptOrigin<<m_ptEnd;
	}else{
		ar>>m_nDrawType>>m_ptOrigin>>m_ptEnd;
	}
}

void CGraph::Draw(CDC*pDc)
{
	CBrush *pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));
	CBrush *pOldBrush = pDc->SelectObject(pBrush);
	switch(m_nDrawType){
	case 1:
		pDc->SetPixel(m_ptEnd,RGB(0,0,0));
		break;
	case 2:
		pDc->MoveTo(m_ptOrigin);
		pDc->LineTo(m_ptEnd);
		break;
	case 3:
		pDc->Rectangle(CRect(m_ptOrigin,m_ptEnd));
		break;
	case 4:
		pDc->Ellipse(CRect(m_ptOrigin,m_ptEnd));
		break;
	}
}
3. 在CL2Doc完善Serialize方法

void CL2Doc::Serialize(CArchive& ar)
{
	POSITION pos = GetFirstViewPosition();
	CL2View *pView = (CL2View*)GetNextView(pos);
	if (ar.IsStoring())
	{
		int nCount = pView->m_obArray.GetSize();
		ar<<nCount;
		for(int i=0;i<nCount;++i){
			ar<<pView->m_obArray.GetAt(i);
		}
	}
	else
	{
		int nCount;
		CGraph *pGraph;
		ar>>nCount;
		for(int i=0;i<nCount;++i){
			ar>>pGraph;
			pView->m_obArray.Add(pGraph);
		}
	}
}
4. 在View类中完善绘图的基本功能


void CL2View::OnDraw(CDC* pDC)
{
	CL2Doc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;
	int nCount;
	nCount = m_obArray.GetSize();
	for(int i=0;i<nCount;++i){
		((CGraph*)m_obArray.GetAt(i))->Draw(pDC);
	}
}

void CL2View::OnLButtonUp(UINT nFlags, CPoint point)
{
	m_endPoint = point;
	CGraph *pGragh = new CGraph(m_type,m_oriPoint,m_endPoint);
	m_obArray.Add(pGragh);
	Invalidate();
	CView::OnLButtonUp(nFlags, point);
}
  • 程序的改进

1. 由于CObjArray类也支持串行化,所以,可以在if (ar.IsStoring())判断之外调用此函数:

	pView->m_obArray.Serialize(ar);
2. 在文档类中定义m_cbarray成员变量,使用的时候仅仅需要使用getDocument即可。

3. 释放旧的对象

void CL2Doc::DeleteContents()
{
	int nCount;
	nCount = m_obArray.GetSize();
	for(int i=0;i<nCount;++i){
		delete m_obArray.GetAt(i);
	}
	m_obArray.RemoveAll();
	CDocument::DeleteContents();
}

第十一章:网络编程

  • 面向连接的服务 注意需要在“连接器\输入\附加依赖项”中添加Ws2_32.lib

1. 服务器端程序

#include <WinSock2.h>
#include <stdio.h>

int main(){
	//加载1.1版本
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		return 0;
	}
	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ) {
			WSACleanup( );
			return 0; 
	}
	SOCKET sockClient = socket(AF_INET,SOCK_STREAM,0);

	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);
	connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

	char recvBuf[100];
	recv(sockClient,recvBuf,100,0);
	printf("%s\n",recvBuf);
	send(sockClient,"This is zhangsan",strlen("This is zhangsan")+1,0);

	closesocket(sockClient);
	WSACleanup();
	return 0;
}
2. 客户端程序

#include <WinSock2.h>
#include <stdio.h>

int main(){
	//加载1.1版本
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		return 0;
	}
	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ) {
			WSACleanup( );
			return 0; 
	}

	SOCKET sockSrv = socket(AF_INET,SOCK_STREAM,0);
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);

	bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));

	listen(sockSrv,5);
	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);
	while(1){
		SOCKET sockConn = accept(sockSrv,(SOCKADDR*)&addrClient,&len);
		char sendBuf[100];
		sprintf(sendBuf,"Welcome %s to www.baidu.com",inet_ntoa(addrClient.sin_addr));
		send(sockConn,sendBuf,strlen(sendBuf)+1,0);
		char recvBuf[100];
		recv(sockConn,recvBuf,100,0);
		printf("%s\n",recvBuf);
		closesocket(sockConn);
	}
	return 0;
}
  • 面向无连接的服务

1. 客户端

#include <WinSock2.h>
#include <stdio.h>

int main(){
	//加载1.1版本
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		return 0;
	}
	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ) {
			WSACleanup( );
			return 0; 
	}

	SOCKET sockClient = socket(AF_INET,SOCK_DGRAM,0);
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);

	sendto(sockClient,"Hello",strlen("Hello")+1,0,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
	closesocket(sockClient);
	WSACleanup();
	return 0;
}
2. 服务器


#include <WinSock2.h>
#include <stdio.h>

int main(){
	//加载1.1版本
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		return 0;
	}
	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ) {
			WSACleanup( );
			return 0; 
	}
	SOCKET sockSrv = socket(AF_INET,SOCK_DGRAM,0);
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);

	bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);
	char recvBuf[100];

	recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);
	printf("%s\n",recvBuf);
	closesocket(sockSrv);
	WSACleanup();
	return 0;
}
  • 给予UDP的字符界面聊天程序

1. 服务器

#include <WinSock2.h>
#include <stdio.h>

int main(){
	//加载1.1版本
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		return 0;
	}
	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ) {
			WSACleanup( );
			return 0; 
	}
	
	SOCKET sockSrv = socket(AF_INET,SOCK_DGRAM,0);
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);

	bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
	char recvBuf[100];
	char sendBuf[100];
	char tempBuf[200];
	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);

	while(1){
		recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);
		if('q'==recvBuf[0]){
			sendto(sockSrv,"q",strlen("q")+1,0,(SOCKADDR*)&addrClient,len);
			printf("Chat end!\n");
			break;
		}
		sprintf(tempBuf,"%s say : %s",inet_ntoa(addrClient.sin_addr),recvBuf);
		printf("%s\n",tempBuf);
		printf("Please input data:\n");
		gets(sendBuf);
		sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrClient,len);
	}
	closesocket(sockSrv);
	return 0;
}
2. 客户端


#include <WinSock2.h>
#include <stdio.h>

int main(){
	//加载1.1版本
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD( 1, 1 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		return 0;
	}
	if ( LOBYTE( wsaData.wVersion ) != 1 ||
		HIBYTE( wsaData.wVersion ) != 1 ) {
			WSACleanup( );
			return 0; 
	}

	SOCKET sockClient = socket(AF_INET,SOCK_DGRAM,0);
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);
	char recvBuf[100];
	char sendBuf[100];
	char tempBuf[200];
	int len = sizeof(SOCKADDR);
	while(1){
		printf("Please input data:\n");
		gets(sendBuf);
		sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrSrv,len);
		recvfrom(sockClient,recvBuf,100,0,(SOCKADDR*)&addrSrv,&len);
		if('q'==recvBuf[0]){
			sendto(sockClient,"q",strlen("q")+1,0,(SOCKADDR*)&addrSrv,len);
			printf("Chat end!\n");
			break;
		}
		sprintf(tempBuf,"%s say : %s",inet_ntoa(addrSrv.sin_addr),recvBuf);
		printf("%s\n",tempBuf);
	}
	closesocket(sockClient);
	WSACleanup();

	return 0;
}

第十二章:多线程与聊天室程序的创建

  • 创建线程

#include <Windows.h>
#include <iostream>
using namespace std;

DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
	cout<<"Thread 1 is running"<<endl;
	return 0;
}

int main()
{
	HANDLE hThread;
	hThread = CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
	CloseHandle(hThread);//关闭句柄并没有终止线程
	cout<<"main thread is running"<<endl;
	Sleep(10);
	return 0;
}
  • 生产者消费者模式

int tickets = 100;
HANDLE hMutex;//互斥对象
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
	while(TRUE)
	{
		WaitForSingleObject(hMutex,INFINITE);//等待互斥对象
		if(tickets>0)
			cout<<"Thread1 sell ticket : "<<tickets--<<endl;
		else
			break;
		ReleaseMutex(hMutex);//释放互斥对象
	}
	return 0;
}

int main()
{
	HANDLE hThread1,hThread2;
	hThread1 = CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
	hThread2 = CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	hMutex = CreateMutex(NULL,FALSE,NULL);//创建一个匿名的互斥对象
	Sleep(4000);
	return 0;
}
  • 引用计数原理

    1. 
	hMutex = CreateMutex(NULL,TRUE,NULL);//引用计数加一
	WaitForSingleObject(hMutex,INFINITE);//引用计数加加一
	ReleaseMutex(hMutex);//引用计数减一,此时引用计数为1
	ReleaseMutex(hMutex);
	2. 如果在使用线程的时候忘记释放hMutex,则操作系统会帮助我们回收
	3. 判断互斥对象是否已经被创建
		hMutex = CreateMutex(NULL,TRUE,L"tickets");
	if(hMutex)
	{
		if(ERROR_ALREADY_EXISTS == GetLastError())
		{
			cout<<"only instance is running"<<endl;
			return 1;
		}
	}
  • 使用多线程编写网络聊天室程序

BOOL CChatApp::InitInstance()
{
	if(!AfxSocketInit())
	{
		AfxMessageBox("加载套接字失败");
		return FALSE;
	}
	...
}
// ChatDlg.h : 头文件
//

#define WM_RECVDATA WM_USER+1
struct RECVPARAM
{
	SOCKET sock;
	HWND hwnd;
};

class CChatDlg : public CDialogEx
{
protected:
	HICON m_hIcon;
	afx_msg LRESULT OnRecvData(WPARAM wParam,LPARAM lParam);
	DECLARE_MESSAGE_MAP()
private:
	SOCKET m_socket;
public:
	BOOL InitSocket(void);
	static DWORD  WINAPI CChatDlg::RecvProc(LPVOID lpParameter);
	afx_msg void OnBnClickedBtnSend();
};

// ChatDlg.cpp : 实现文件
//

BEGIN_MESSAGE_MAP(CChatDlg, CDialogEx)
	ON_MESSAGE(WM_RECVDATA,OnRecvData)
	ON_BN_CLICKED(IDC_BTN_SEND, &CChatDlg::OnBnClickedBtnSend)
END_MESSAGE_MAP()

BOOL CChatDlg::OnInitDialog()
{

	// TODO: 在此添加额外的初始化代码
	InitSocket();
	RECVPARAM *pRecvParam = new RECVPARAM;
	pRecvParam->sock = m_socket;
	pRecvParam->hwnd = m_hWnd;
	HANDLE hThread = CreateThread(NULL,0,RecvProc,(LPVOID)pRecvParam,0,NULL);
	CloseHandle(hThread);
	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

LRESULT CChatDlg::OnRecvData(WPARAM wParam,LPARAM lParam)
{
	CString str = (char*)lParam;
	CString strTemp;
	GetDlgItemText(IDC_EDIT_RECV,strTemp);
	str+="\r\n";
	str+=strTemp;
	SetDlgItemText(IDC_EDIT_RECV,str);
	return 0;
}

BOOL CChatDlg::InitSocket(void)
{
	m_socket = socket(AF_INET,SOCK_DGRAM,0);
	if(INVALID_SOCKET == m_socket)
	{
		MessageBox("套接字创建失败");
		return FALSE;
	}
	SOCKADDR_IN addrSock;
	addrSock.sin_family = AF_INET;
	addrSock.sin_port = htons(6000);
	addrSock.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	int retval;
	retval = bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR));
	if(SOCKET_ERROR == retval)
	{
		closesocket(m_socket);
		MessageBox("绑定失败");
		return FALSE;
	}
	return 0;
}

DWORD  WINAPI CChatDlg::RecvProc(LPVOID lpParameter)
{
	SOCKET sock = ((RECVPARAM*)lpParameter)->sock;
	HWND hwnd = ((RECVPARAM*)lpParameter)->hwnd;
	SOCKADDR_IN addrFrom;
	int len = sizeof(SOCKADDR);
	char recvBuf[200];
	char tempBuf[200];
	int retval;
	while(TRUE)
	{
		retval = recvfrom(sock,recvBuf,200,0,(SOCKADDR*)&addrFrom,&len);
		if(SOCKET_ERROR == retval)
			break;
		sprintf(tempBuf,"%s说:%s",inet_ntoa(addrFrom.sin_addr),recvBuf);
		::PostMessageA(hwnd,WM_RECVDATA,0,(LPARAM)tempBuf);
	}
	return 0;
}

void CChatDlg::OnBnClickedBtnSend()
{
	DWORD dwIP;
	((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);
	SOCKADDR_IN addrTo;
	addrTo.sin_family = AF_INET;
	addrTo.sin_port = htons(6000);
	addrTo.sin_addr.S_un.S_addr = htonl(dwIP);
	CString strSend;
	GetDlgItemText(IDC_EDIT_SEND,strSend);
	sendto(m_socket,strSend,strSend.GetLength()+1,0,(SOCKADDR*)&addrTo,sizeof(SOCKADDR));
	SetDlgItemText(IDC_EDIT_SEND,"");
}

// stdafx.h : 标准系统包含文件的包含文件,
#include <afxsock.h>

//资源文件
将文本框的MultiLine设置为TRUE

第十三章:线程同步与异步套接字编程

  • 使用事件对象处理线程同步问题

int tickets = 100;
HANDLE g_hEvent;//事件对象
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
	while(TRUE)
	{
		WaitForSingleObject(g_hEvent,INFINITE);//请求对象后自动设置为非信号状态,等同于ResetEvent(g_hEvent);
		if(tickets>0)
		{
			Sleep(1);
			cout<<"Thread 1 sell tickets : "<<tickets--<<endl;
		}
		else
			break;
		SetEvent(g_hEvent);//将其设置为有信号状态
	}
	return 0;
}

int main(){
	HANDLE hThread1;
	HANDLE hThread2;
	hThread1 = CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
	hThread2 = CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
	CloseHandle(hThread1);
	CloseHandle(hThread2);

	//g_hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);//创建事件对象
	g_hEvent = CreateEvent(NULL,FALSE,FALSE,L"ticket");//创建事件对象
	if(g_hEvent)
	{
		if(ERROR_ALREADY_EXISTS == GetLastError())
		{
			cout<<"only instance can run !"<<endl;
			return 0;
		}
	}
	SetEvent(g_hEvent);//当人工重置的时间编程有信号状态时,所有的线程都可以同时执行,当自动重置的事件有信号状态,只有一个可以运行,并且同时设置为分信号状态,需要手动设置SetEvent,而人工重置的事件对象不会自动重置为信号或非信号状态,所有的必须手动操作
	Sleep(4000);
	return 0;
}
  • 使用临界区处理线程同步问题

CRITICAL_SECTION g_cs;
DWORD WINAPI Fun1Proc(LPVOID lpParameter)
{
	while(TRUE)
	{
		EnterCriticalSection(&g_cs);
		if(tickets>0)
		{
			Sleep(1);
			cout<<"Thread 1 sell tickets : "<<tickets--<<endl;
		}
		else
			break;
		LeaveCriticalSection(&g_cs);
	}
	return 0;
}

int main(){
	HANDLE hThread1;
	HANDLE hThread2;
	hThread1 = CreateThread(NULL,0,Fun1Proc,NULL,0,NULL);
	hThread2 = CreateThread(NULL,0,Fun2Proc,NULL,0,NULL);
	CloseHandle(hThread1);
	CloseHandle(hThread2);
	InitializeCriticalSection(&g_cs);
	Sleep(4000);
	DeleteCriticalSection(&g_cs);
	return 0;
}
\\使用临界区对象需要注意避免死锁
  • 使用异步套接字编程开发网络聊天室程序

// Chat.cpp : 定义应用程序的类行为。
//

CChatApp::~CChatApp()
{
	WSACleanup();//终止对套接字库的使用
}

BOOL CChatApp::InitInstance()
{
	//加载套接字库
	WORD wVersionRequested;
	WSADATA wsaData;
	int err;

	wVersionRequested = MAKEWORD( 2, 2 );

	err = WSAStartup( wVersionRequested, &wsaData );
	if ( err != 0 ) {
		return FALSE;
	}
	if ( LOBYTE( wsaData.wVersion ) != 2 ||
		HIBYTE( wsaData.wVersion ) != 2 ) {
			WSACleanup( );
			return FALSE; 
	}
}

// ChatDlg.h : 头文件
//

#define  UM_SOCK WM_USER+1
// CChatDlg 对话框
class CChatDlg : public CDialogEx
{
// 构造
public:
	CChatDlg(CWnd* pParent = NULL);	// 标准构造函数
	~CChatDlg();
// 实现
protected:
	afx_msg LRESULT OnSock(WPARAM wParam,LPARAM lParam);
	DECLARE_MESSAGE_MAP()
private:
	SOCKET m_socket;
public:
	BOOL InitSocket(void);
	afx_msg void OnBnClickedBtnSend();
};


// ChatDlg.cpp : 实现文件
//


CChatDlg::CChatDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CChatDlg::IDD, pParent)
{
	m_socket = 0;
}

CChatDlg::~CChatDlg()
{
	if(m_socket)
		closesocket(m_socket);
}

BEGIN_MESSAGE_MAP(CChatDlg, CDialogEx)
	ON_MESSAGE(UM_SOCK,OnSock)
	ON_BN_CLICKED(IDC_BTN_SEND, &CChatDlg::OnBnClickedBtnSend)
END_MESSAGE_MAP()


// CChatDlg 消息处理程序

BOOL CChatDlg::OnInitDialog()
{
	InitSocket();
	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

LRESULT CChatDlg::OnSock(WPARAM wParam,LPARAM lParam)
{
	switch(LOWORD(lParam))
	{
	case FD_READ:
		WSABUF wsabuf;
		wsabuf.buf = new char[200];
		wsabuf.len = 200;
		DWORD dwRead;
		DWORD dwFlag = 0;
		SOCKADDR_IN addrFrom;
		int len = sizeof(SOCKADDR);
		CString str;
		CString strtmp;
		HOSTENT *pHost;
		if(SOCKET_ERROR == WSARecvFrom(m_socket,&wsabuf,1,&dwRead,&dwFlag,(SOCKADDR*)&addrFrom,&len,NULL,NULL))
		{
			MessageBox("接收数据失败");
			return 0;
		}
		pHost = gethostbyaddr((char*)&addrFrom.sin_addr.S_un.S_addr,4,AF_INET);
		//str.Format("%s说:%s",inet_ntoa(addrFrom.sin_addr),wsabuf.buf);
		str.Format("%s说:%s",pHost->h_name,wsabuf.buf);
		str+="\r\n";
		GetDlgItemText(IDC_EDIT_RECV,strtmp);
		str+=strtmp;
		SetDlgItemText(IDC_EDIT_RECV,str);
		break;
	}
	return 0;
}

//初始化Socket
BOOL CChatDlg::InitSocket(void)
{
	m_socket = WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,0);
	if(INVALID_SOCKET == m_socket){
		MessageBox("创建套接字失败");
		return FALSE;
	}
	SOCKADDR_IN addrSock;
	addrSock.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSock.sin_family = AF_INET;
	addrSock.sin_port = htons(6000);
	if(SOCKET_ERROR == bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR)))
	{
		MessageBox("绑定失败");
		return FALSE;
	}
	if(SOCKET_ERROR == WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ)){
		MessageBox("注册网络读取时间失败!");
		return FALSE;
	}
	return TRUE;
}


void CChatDlg::OnBnClickedBtnSend()
{
	DWORD dwIp;
	CString strSend;
	WSABUF wsabuf;
	DWORD dwSend;
	int len;
	CString strHostName;
	SOCKADDR_IN addrTo;
	HOSTENT* pHost;
	//如果用户没有输入主机名,则从地址控件中获取
	if(GetDlgItemText(IDC_EDIT_HOSTNAME,strHostName),strHostName=="")
	{
		((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIp);
		addrTo.sin_addr.S_un.S_addr = htonl(dwIp);
	}
	else
	{
		pHost = gethostbyname(strHostName);
		addrTo.sin_addr.S_un.S_addr = *((DWORD*)pHost->h_addr_list[0]);
	}
	
	addrTo.sin_family = AF_INET;
	addrTo.sin_port = htons(6000);

	GetDlgItemText(IDC_EDIT_SEND,strSend);
	len = strSend.GetLength();
	wsabuf.buf = strSend.GetBuffer(len);
	wsabuf.len = len+1;
	SetDlgItemText(IDC_EDIT_SEND,"");
	if(SOCKET_ERROR == WSASendTo(m_socket,&wsabuf,1,&dwSend,0,(SOCKADDR*)&addrTo,sizeof(SOCKADDR),NULL,NULL))
	{
		MessageBox("数据发送失败");
		return;
	}
}
//头文件
#include <WinSock2.h>
//附加依赖库
Ws2_32.lib

第十四章:进程间通信

  • 使用剪贴板进行进程间通信

void CL1Dlg::OnBnClickedBtnSend()
{
	if(OpenClipboard())
	{
		CString str;
		HANDLE hClip;
		char *pBuf;
		EmptyClipboard();
		GetDlgItemText(IDC_EDIT_SEND,str);
		hClip = GlobalAlloc(GMEM_MOVEABLE,str.GetLength()+1);
		pBuf = (char *)GlobalLock(hClip);
		strcpy(pBuf,str);
		GlobalUnlock(hClip);
		SetClipboardData(CF_TEXT,hClip);
		CloseClipboard();
	}
}


void CL1Dlg::OnBnClickedBtnRecv()
{
	if(OpenClipboard())
	{
		if(IsClipboardFormatAvailable(CF_TEXT))
		{
			HANDLE hClip;
			char *pBuf;
			hClip = GetClipboardData(CF_TEXT);
			pBuf = (char *)GlobalLock(hClip);
			GlobalUnlock(hClip);
			SetDlgItemText(IDC_EDIT_RECV,pBuf);
			CloseClipboard();
		}
	}
}
  • 使用匿名管道进行进程间通信

1. 父进程

private:
	HANDLE hRead;
	HANDLE hWrite;
void CL2View::OnPipeCreate()
{
	SECURITY_ATTRIBUTES sa;
	sa.bInheritHandle = TRUE;
	sa.lpSecurityDescriptor = NULL;
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	if(!CreatePipe(&hRead,&hWrite,&sa,0))
	{
		MessageBox("创建匿名管道失败");
		return ;
	}
	STARTUPINFO sui;
	PROCESS_INFORMATION pi;
	ZeroMemory(&sui,sizeof(STARTUPINFO));
	sui.cb = sizeof(STARTUPINFO);
	sui.dwFlags = STARTF_USESTDHANDLES;
	sui.hStdInput = hRead;
	sui.hStdOutput = hWrite;
	sui.hStdError = GetStdHandle(STD_ERROR_HANDLE);
	if(!CreateProcess(".\\Child.exe",NULL,NULL,NULL,TRUE,0,NULL,NULL,&sui,&pi))
	{
		CloseHandle(hRead);
		CloseHandle(hWrite);
		hRead = NULL;
		hWrite = NULL;
		MessageBox("创建子线程失败");
		return ;
	}
	else
	{
		CloseHandle(pi.hProcess);
		CloseHandle(pi.hThread);
	}
}

void CL2View::OnPipeRead()
{
	char buf[100];
	DWORD dwRead;
	if(!ReadFile(hRead,buf,100,&dwRead,NULL))
	{
		MessageBox("读取数据失败");
		return ;
	}
	MessageBox(buf);
}

void CL2View::OnPipeWrite()
{
	char buf[] = "http://www.baidu.com";
	DWORD dwWrite;
	if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
	{
		MessageBox("写入数据失败");
		return ;
	}
}
2. 子进程



void CChildView::OnPipeRead()
{
	char buf[100];
	DWORD dwRead;
	if(!ReadFile(hRead,buf,100,&dwRead,NULL))
	{
		MessageBox("读取数据失败");
		return ;
	}
	MessageBox(buf);
}

void CChildView::OnPipeWrite()
{
	char buf[] = "匿名管道测试程序";
	DWORD dwWrite;
	if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
	{
		MessageBox("写入数据失败");
		return ;
	}
}

void CChildView::OnInitialUpdate()
{
	CView::OnInitialUpdate();
	hRead = GetStdHandle(STD_INPUT_HANDLE);
	hWrite = GetStdHandle(STD_OUTPUT_HANDLE);
}
  • 使用命名管道进程进行进程间通信

1. 服务器

private:
	HANDLE hPipe;
void CL3View::OnPipeCreate()
{
	hPipe = CreateNamedPipe("\\\\.\\pipe\\MyPipe",PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
		0,1,1024,1024,0,NULL);
	if(INVALID_HANDLE_VALUE == hPipe)
	{
		MessageBox("创建命名管道失败!");
		hPipe=NULL;
		return ;
	}
	HANDLE hEvent;
	hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
	if(!hEvent)
	{
		MessageBox("创建事件对象失败!");
		CloseHandle(hPipe);
		hPipe = NULL;
		return ;
	}
	OVERLAPPED ovlap;
	ZeroMemory(&ovlap,sizeof(OVERLAPPED));
	ovlap.hEvent = hEvent;
	if(!ConnectNamedPipe(hPipe,&ovlap))
	{
		if(ERROR_IO_PENDING != GetLastError())
		{
			MessageBox("等待客户端连接失败!");
			CloseHandle(hPipe);
			CloseHandle(hEvent);
			hPipe = NULL;
			return ;
		}
	}
	if(WAIT_FAILED == WaitForSingleObject(hEvent,INFINITE))
	{
		MessageBox("等待对象失败!");
		CloseHandle(hPipe);
		CloseHandle(hEvent);
		hPipe = NULL;
		return ;
	}
	CloseHandle(hEvent);
}

void CL3View::OnPipeRead()
{
	char buf[100];
	DWORD dwRead;
	if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
	{
		MessageBox("读取数据失败");
		return ;
	}
	MessageBox(buf);
}

void CL3View::OnPipeWrite()
{
	char buf[] = "www.baidu.com";
	DWORD dwWrite;
	if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
	{
		MessageBox("写入数据失败");
		return ;
	}
}
2. 客户端


void CL4View::OnPipeConnect()
{
	if(!WaitNamedPipe("\\\\.\\pipe\\MyPipe",NMPWAIT_WAIT_FOREVER))
	{
		MessageBox("当前没有可利用的命名管道实例!");
		return ;
	}
	hPipe = CreateFile("\\\\.\\pipe\\MyPipe",GENERIC_READ|GENERIC_WRITE,
		0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
	if(!INVALID_HANDLE_VALUE == hPipe)
	{
		MessageBox("打开命名管道失败!");
		hPipe = NULL;
		return ;
	}
}

void CL4View::OnPipeRead()
{
	char buf[100];
	DWORD dwRead;
	if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
	{
		MessageBox("读取数据失败");
		return ;
	}
	MessageBox(buf);
}

void CL4View::OnPipeWrite()
{
	char buf[] = "匿名管道测试程序";
	DWORD dwWrite;
	if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
	{
		MessageBox("写入数据失败");
		return ;
	}
}
  • 使用油槽通信

1. 服务器端程序

void CL1View::OnMailslotRecv()
{
	HANDLE hMailslot;
	hMailslot = CreateMailslot("\\\\.\\mailslot\\MyMailslot",0,MAILSLOT_WAIT_FOREVER,NULL);
	if(INVALID_HANDLE_VALUE == hMailslot)
	{
		MessageBox("创建油槽失败!");
		return ;
	}
	char buf[100];
	DWORD dwRead;
	if(!ReadFile(hMailslot,buf,100,&dwRead,NULL))
	{
		MessageBox("读取数据失败");
		CloseHandle(hMailslot);
		return ;
	}
	MessageBox(buf);
	CloseHandle(hMailslot);
}
2. 客户端程序


void CL2View::OnMailslotSend()
{
	HANDLE hMailslot;
	hMailslot = CreateFile("\\\\.\\mailslot\\MyMailslot",GENERIC_WRITE,
		FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
	if(INVALID_HANDLE_VALUE == hMailslot)
	{
		MessageBox("打开油槽失败!");
		return ;
	}
	char buf[] = "www.baidu.com";
	DWORD dwWrite;
	if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,NULL))
	{
		MessageBox("写入数据失败");
		CloseHandle(hMailslot);
		return ;
	}
	CloseHandle(hMailslot);
}

第十五章:ActiveX控件

  • ActiveX控件的四种属性

  1. Stock:为每个控件提供的标准属性,如字体或颜色。

  2. Ambient:围绕控件的环境属性——已被置入容器的属性。这些属性不能被修改,但是控件可以使用它们调整为自己的属性。

  3. Extended:这些由容器处理的属性,一般包括大小和在屏幕上的位置。

  4. Custom:由控件开发者添加的属性。

  • 定义Clock,并添加前景色和背景色属性

1. 点击_DClock接口->添加变量->添加BackColor和ForeColor
2. 


void CClockCtrl::OnDraw(
			CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
	if (!pdc)
		return;
	CBrush hbrush(TranslateColor(GetBackColor()));
	pdc->FillRect(rcBounds, &hbrush);
	pdc->SetBkMode(TRANSPARENT);
	pdc->SetTextColor(TranslateColor(GetForeColor()));
	CTime time = CTime::GetCurrentTime();
	CString str = time.Format("%H:%M:%S");
	pdc->TextOut(0,0,str);
}
  • 添加颜色选择标签页

// ClockCtrl.cpp : CClockCtrl ActiveX 控件类的实现。
// 属性页

// TODO: 按需要添加更多属性页。请记住增加计数!
BEGIN_PROPPAGEIDS(CClockCtrl, 2)
	PROPPAGEID(CClockPropPage::guid)
	PROPPAGEID(CLSID_CColorPropPage)
END_PROPPAGEIDS(CClockCtrl)
  • 添加自定义属性

1. 点击_DClock接口->添加变量->添加变量Interval,类型Short,添加变量
2. 补充OnIntervalChanged函数

void CClockCtrl::OnIntervalChanged(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	// TODO: 在此添加属性处理程序代码
	if(m_Interval<0||m_Interval>6000)
	{
		m_Interval = 1000;
	}
	else
	{
		m_Interval = m_Interval/1000*1000;
		KillTimer(1);
		SetTimer(1,m_Interval,NULL);
	}
	SetModifiedFlag();
}

void CClockCtrl::OnTimer(UINT_PTR nIDEvent)
{
	//Invalidate();
	InvalidateControl();
	COleControl::OnTimer(nIDEvent);
}
  • 将自定义属性添加到标签页中

1. 在标签页中添加输入框,并关联变量
2. 手动补充关联接口中的“外部属性”变量


void CClockPropPage::DoDataExchange(CDataExchange* pDX)
{
	DDP_Text(pDX, IDC_EDIT_INTERVAL, m_updateInterval,_T("Interval"));
	DDX_Text(pDX, IDC_EDIT_INTERVAL, m_updateInterval);
	DDP_PostProcessing(pDX);
}
  • 为控件添加方法

1. 点击_DClock接口->添加方法
2. 在CClockCtrl中完善方法

void CClockCtrl::Hello(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	MessageBox("Hello world");
}
  • 为控件添加事件

1. 选择CClockCtrl->右键添加事件->添加Click事件。
2. 选择CClockCtrl->右键添加事件->添加NewMinute事件,内部调用FireNewMinute
3. 发出事件:


	CTime time = CTime::GetCurrentTime();
	if(0 == time.GetSecond())
	{
		FireNewMinute();
	}
  • 属性持久化(每次打开原来的工程属性值不会被改变)

void CClockCtrl::DoPropExchange(CPropExchange* pPX)
{
	ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
	COleControl::DoPropExchange(pPX);
	// TODO: 为每个持久的自定义属性调用 PX_ 函数。
	PX_Short(pPX,"Interval",m_Interval,1000);
}
  • 属性表单中的消息同步容器

在OnIntervalChanged修改好计时器的时候调用函数:
		BoundPropertyChanged(0x1);
其中1是	[id(1) ] SHORT Interval中表示的ID号

  • 让控件在设计时不改变时间

	if(AmbientUserMode())
		InvalidateControl();
  • 在VC中使用Avtive控件

  1. 工具->选择工具箱选项,选择注册的控件

  2. 工程->添加ActiveX控件类->选择注册的控件->自动生成控件的头文件和源文件

  3. 手动生成控件

void CL1Dlg::OnBnClickedButton1()
{
	m_clock.Create("Clock",WS_CHILD|WS_VISIBLE,
		CRect(0,0,100,50),this,123);
}

第十六章:动态链接库

  1. 编写动态链接库

1. 创建一个win32动态链接库程序

_declspec(dllexport) int add(int a,int b)
{
	return a+b;
}

_declspec(dllexport) int substract(int a,int b)
{
	return a-b;
}
2. 调用动态链接库

//extern int add(int a,int b);
//extern int substract(int a,int b);
_declspec(dllimport) int add(int a,int b);
_declspec(dllimport) int substract(int a,int b);
void CL2Dlg::OnBnClickedBtnAdd()
{
	CString str;
	str.Format("5+3=%d",add(5,3));
	MessageBox(str);
}


void CL2Dlg::OnBnClickedBtnSub()
{
	CString str;
	str.Format("5-3=%d",substract(5,3));
	MessageBox(str);
}
3. 查看动态链接库的导出函数

dumpbin -exports L1.dll
4. 查看程序的导入函数

dumpbin -imports L2.exe
5. 使用图形化界面显示程序的导入和导出情况——Dependency

  • 为外部调用者提供头文件

L1.h
#ifdef DLL1_API
#else
#define DLL1_API _declspec(dllimport)
#endif
DLL1_API int add(int a,int b);
DLL1_API int substract(int a,int b);

L1.cpp
#define DLL1_API _declspec(dllexport)
#include "L1.h"
DLL1_API int add(int a,int b)
{
	return a+b;
}

DLL1_API int substract(int a,int b)
{
	return a-b;
}

调用者.cpp
#include "L1.h"
//直接调用函数
  • 导出类

class DLL1_API Point
{
public:
	void output(int x,int y);
};

void Point::output(int x,int y)
{
	HWND hwnd = GetForegroundWindow();
	HDC hdc = GetDC(hwnd);
	char buf[20];
	memset(buf,0,20);
	sprintf(buf,"x=%d,y=%d",x,y);
	TextOut(hdc,0,0,buf,strlen(buf));
	ReleaseDC(hwnd,hdc);
}
注意:导出整个类只需要将DLL1_API写在类的前面即可,但是对于私有函数,即使被导出,也无法被调用。也可以只导出部分的成员函数,只需要将DLL1_API写在函数的声明前即可。
如果只导出类中的某些成员函数,但是类仍然可以被构建。
  • 使用C规范避免名字改编造成的问题

1. 头文件


#ifdef DLL1_API
#else
#define DLL1_API extern "C" _declspec(dllimport)
#endif
2. 源文件
#define DLL1_API extern "C" _declspec(dllexport)
  • 使用模块定义文件避免名字改编造成的问题

模块定义文件
LIBRARY L1

EXPORTS
add
substract
c++源文件
int add(int a,int b)
{
	return a+b;
}

int substract(int a,int b)
{
	return a-b;
}
  • 动态引入动态链接库

void CL2Dlg::OnBnClickedBtnAdd()
{
	HINSTANCE hInst;
	hInst = LoadLibrary("L1.dll");
	typedef int (*ADDPROC)(int a,int b);
	//ADDPROC Add = (ADDPROC)GetProcAddress(hInst,"add");
	ADDPROC Add = (ADDPROC)GetProcAddress(hInst,MAKEINTRESOURCE(1));//可以使用导出函数的id获取动态链接库的真实名字,因此不受名字改编的影响,但不推荐
	if(!Add)
	{
		MessageBox("获取函数地址失败!");
		return ;
	}
	CString str;
	str.Format("5+3=%d",Add(5,3));
	MessageBox(str);
	FreeLibrary(hInst);
}

第十七章:HOOK和数据库访问

  1. 创建可执行程序钩子

//鼠标按键消息
LRESULT CALLBACK MouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{
	return 1;//阻止钩子消息
}
HHOOK g_kKeyboard = NULL;
HHOOK g_kMouse = NULL;
HWND g_hWnd = NULL;
//键盘按键消息
LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{

	//if(VK_SPACE == wParam || VK_RETURN == wParam)//拦截回车和空格按键消息
	/*if(VK_F4 == wParam && (1==(lParam>>29 & 1)))//拦截ALT+F4关闭程序的按键消息
		return 1;
	else
		return CallNextHookEx(g_kKeyboard,code,wParam,lParam);*/
	if(VK_F2 == wParam)//自己留下的关闭程序的后门
	{
		::SendMessageA(g_hWnd,WM_CLOSE,0,0);
		UnhookWindowsHookEx(g_kKeyboard);
		UnhookWindowsHookEx(g_kMouse);
	}
	return 1;//阻止钩子消息
}
在初始化代码时创建:


	g_hWnd = m_hWnd;
	g_kMouse = SetWindowsHookEx(WH_MOUSE,MouseProc,NULL,GetCurrentThreadId());
	g_kKeyboard = SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,NULL,GetCurrentThreadId());
  1. 创建动态链接库钩子

1. 动态链接库的创建

#include <Windows.h>

HHOOK g_hMouse = NULL;
HHOOK g_hKeyboard = NULL;
HWND g_hWnd;
//HINSTANCE g_hInst;
//BOOL WINAPI DllMain(  HINSTANCE hinstDLL,  // handle to the DLL module
//	DWORD fdwReason,     // reason for calling function
//	LPVOID lpvReserved   // reserved);
//{
//	g_hInst = hinstDLL;
//}

LRESULT CALLBACK MouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{
	return 1;//阻止钩子消息
}

LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{
	if(VK_F2 == wParam)
	{
		SendMessage(g_hWnd,WM_CLOSE,0,0);
		UnhookWindowsHookEx(g_hMouse);
		UnhookWindowsHookEx(g_hKeyboard);
	}
	return 1;//阻止钩子消息
}
void SetHook(HWND hwnd)
{
	g_hWnd = hwnd;
	g_hMouse = SetWindowsHookEx(WH_MOUSE,MouseProc,GetModuleHandle("l2"),0);
	g_hKeyboard = SetWindowsHookEx(WH_KEYBOARD,KeyboardProc,GetModuleHandle("l2"),0);
}
2.动态链接库的调用


_declspec(dllimport)void SetHook(HWND hwnd);
SetHook(m_hWnd);
  • 将当期窗口设置为最顶层窗口

	int cxScreen,cyScreen;
	cxScreen = GetSystemMetrics(SM_CXSCREEN);
	cyScreen = GetSystemMetrics(SM_CYSCREEN);
	SetWindowPos(&wndTopMost,0,0,cxScreen,cyScreen,SWP_SHOWWINDOW);
  • 共享dll变量(经测试在Windows8.1无法达到预期效果)

1. 方法1,在源文件定义

//为自己的变量创建新的节
#pragma data_seg("MySeg")
HWND g_hWnd = NULL;
#pragma data_seg()
#pragma connect(linker,"/section:MySec:RWS")
2. 在模块定义文件中定义
MySec	READ WRITE SHARED
  • 数据库访问技术 (因为此机器上尚未安装Oricle数据库,所以并不进行学习)

  • 3
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值