第 51 章
ARAM;
// 打开互斥量
hMutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, g_strNcom);
if( hMutex == NULL )
{
AfxMessageBox("open Mutex error...");
return 1;
}
// 失败
if ( WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED )
{
return 1;
}
CByteArray m_Array;
// 读出发送数据
int Count=lpData->length;
m_Array.RemoveAll();
m_Array.SetSize(Count);
for(int i=0;i<Count;i++)
{
m_Array.SetAt(i,lpData->data[i]);
}
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·246·
// 使用串口发送数据
lpData->pComm->SetOutput(COleVariant(m_Array));
// 释放互斥量
ReleaseMutex(hMutex);
return 0;
}
(3)初始化互斥量
为对话框添加初始化互斥量的成员函数InitMutex,代码如下:
bool CSCommDlg::InitMutex()
{
g_hMutex = CreateMutex(NULL, false, g_strNcom);
if( g_hMutex == NULL )
{
AfxMessageBox("创建互斥对象错误");
return false;
}
return true;
}
(4)初始化串口
为对话框添加初始化串口的成员函数InitComm,代码如下:
bool CSCommDlg::InitComm()
{
CCommSettingDlg dlg;
if( dlg.DoModal() == IDOK )
{
m_nIndex = atoi(LPCTSTR(dlg.m_nCommID) );
}
//通信参数设置
// 设置串口号
m_Comm.SetCommPort(m_nIndex);
// 设置数据获取方式
m_Comm.SetInputMode(1);
枫叶文学网www.fywxw.com
第9 章 多线程
·247·
// 设置传输参数
m_Comm.SetSettings("9600,n,8,1");
m_Comm.SetRThreshold(1);
// 指定接收缓冲区大小
m_Comm.SetInBufferSize(1024);
// 清空接收缓冲区
m_Comm.SetInBufferCount(0);
// 设置读取方式
m_Comm.SetInputLen(0);
// 打开串口
if(!m_Comm.GetPortOpen())
{
m_Comm.SetPortOpen(TRUE);
}
// 读取数据
m_Comm.GetInput();
return true;
}
(5)响应OnComm 消息
利用ClassWizard 响应MSComm 控件的OnComm 消息,用来处理接收数据,代码如下:
void CSCommDlg::OnComm()
{
// TODO: Add your control notification handler code here
int nEvent;
VARIANT m_input;
char *str,*str1;
int k,i;
CString str2;
nEvent=m_Comm.GetCommEvent();
switch(nEvent)
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·248·
{
case 2:
// 接收缓冲区的字符数目
k=m_Comm.GetInBufferCount();
if(k>0)
{
m_input=m_Comm.GetInput();
str=(char*)(unsigned char*)m_input.parray->pvData;
}
i=0;
str1=str;
while(i<k)
{
i++;
str1++;
}
*str1=’\0’;
str2=(const char*)str; //清除字符串中的不必要字符
m_RevData = (LPCTSTR)str2;
default:
break;
}
// 显示数据
UpdateData(FALSE);
}
(6)发送数据
响应“发送”按钮的单击事件,用来发送数据,代码如下:
void CSCommDlg::OnSendData()
{
// TODO: Add your control notification handler code here
DWORD id;
UpdateData();
DATA senddata;
senddata.hWnd = this->GetSafeHwnd();
枫叶文学网www.fywxw.com
第9 章 多线程
·249·
senddata.length = m_SendData.GetLength();
strcpy(senddata.data, (LPCTSTR)m_SendData);
senddata.pComm = &m_Comm;
// 创建发送线程
CreateThread(NULL, 0, CommSendProc, &senddata, 0, &id);
}
(7)关闭串口
响应“关闭串口”按钮的单击事件,用来关闭串口,代码如下:
void CSCommDlg::OnCloseCom()
{
// TODO: Add your control notification handler code here
if( m_Comm.GetPortOpen() )
{
// 关闭串口
m_Comm.SetPortOpen(0);
}
}
(8)打开串口
响应“打开串口”按钮的单击事件,用来打开串口,代码如下:
void CSCommDlg::OnOpenCom()
{
// TODO: Add your control notification handler code here
if( !m_Comm.GetPortOpen() )
{
// 打开串口
m_Comm.SetPortOpen(1);
}
}
(9)退出
响应“退出”按钮的单击事件,用来释放资源,退出程序,代码如下:
void CSCommDlg::OnCancel()
{
// TODO: Add extra cleanup here
// 释放互斥量资源
ReleaseMutex(g_hMutex);
// 关闭句柄
CloseHandle(g_hMutex);
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·250·
CDialog::OnCancel();
}
(10)运行结果
运行程序后,首先弹出“选择串口参数”对话框,选择需要的串口,如图9-8 所示。
图9-8 “设置串口参数”对话框
在程序主界面中,单击“发送”按钮,发送输入框中的内容会出现在另一个程序的接收
框中,如图9-9 和图9-10 所示。
为了更加简便,本节仅给出利用多线程串口发送数据的方法,读者若有兴趣可以自行实
现多线程控制多个串口的功能。
图9-9 发送窗口 图9-10 接收窗口
9.4 本章小结
本章主要介绍Windows cāo作系统下多线程的基本概念,如何创建和管理线程,以及线程
的同步问题。通过本章的介绍,读者可以看出,多线程程序设计通常比一般的单线程程序复
杂,在程序设计过程中,一定要考虑清楚各线程的关系,避免出现死锁或不同步的现象。另
外需要注意现在大多数用户使用的是单CPU 计算机,在这种机器上运行多线程程序,有时反
而会降低系统的xìng能。因此,在设计多线程应用程序时,应慎重选择,视具体情况加以处理,
使应用程序获得最佳的xìng能。
枫叶文学网www.fywxw.com
第10 章 动态链接库
动态链接库(Dynamic Link Library)是一个可执行模块,其包含的函数可以由Windows
应用程序调用以执行一些功能,主要为应用程序模块提供服务。本章将全面、系统地阐述在
Visual C++平台下进行Win32 动态链接库的设计和应用。主要包括下面几个方面的内容。
? 动态链接库(DLL)的基本知识。
? DLL 的出入口函数。
? 调用DLL 中的两种方式。
? 开发DLL 的方式。
? DLL 中资源的利用。
? 钩子(Hook)函数的应用方法。
为了使读者充分理解概念,对于动态链接库的开发,本章使用了“界面汉化”的示例来
说明资源在动态链接库中的使用。钩子函数对于大多数读者来说可能是一项较为陌生的技术,
为了加深理解,这里列举了两个关于捕获消息的钩子函数示例,可以帮助读者更好地理解钩
子函数的原理和使用方法。
10.1 动态链接库的基础知识
比较大的应用程序都是由很多模块组成的,这些模块彼此协作,以完成整个软件系统的
工作。其中可能存在一些模块的功能较为通用,在构造其他软件系统时仍会被使用。在构造
软件系统时,如果将所有模块的源代码都静态编译到整个应用程序EXE 文件中,会产生一些
问题。一是增加了应用程序的大小,这样会占用更多的磁盘空间,程序运行时也会消耗较大
的内存空间,造成系统资源的浪费;另外,在编写大的EXE 程序时,每次修改重建时都必须
调整编译所有源代码,不但增加了编译过程的复杂xìng,也不利于阶段xìng的单元测试。
Windows 系统平台上提供了一种完全不同的有效编程和运行环境,可以将独立的程序模
块创建为较小的动态链接库(Dynamic Linkable Library)文件,并可对它们单独进行编译和
测试。在运行时,只有在EXE 程序确实要调用这些DLL 模块的情况下,系统才会将它们装
载到内存空间中。这种方式不仅减少了EXE 文件的大小和对内存空间的需求,而且使这些
DLL 模块可以同时被多个应用程序使用,从而充分利用资源。Microsoft Windows 将一些主要
的系统功能以DLL 模块的形式实现。例如IE 中的一些基本功能就是由DLL 文件实现的,它
可以被其他应用程序调用和集成。一般来说,下面的这几种情况必须用到动态链接库技术。
? 多个应用程序共享代码和数据就是通过共享动态链接库实现的,比如Office 软件的各
个组成部分有相似的外观和功能。
? 在钩子程序过滤系统消息时必须使用动态链接库。
? 设备驱动程序必须是动态链接库。
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·252·
? 如果要在对话框编辑器中使用自己定义的控件,也必须使用动态链接库。
? 动态链接库以一种自然的方式将一个大的应用程序划分为几个小的模块,有利于小组
内部成员的分工与合作。而且,各个模块可以独立升级。如果小组中的一个成员开发
了一组实用示例,他就可以把这些示例放在一个动态链接库中,让小组的其他成员使
用。
? 为了实现应用程序的国际化,往往需要使用动态链接库。使用动态链接库可以将针对
某一国家、语言的信息存放在其中。对于不同的版本,使用不同的动态链接库。在使
用AppWizard 生成应用程序时,可以指定资源文件使用的语言,这就是通过提供不同
的动态链接库实现的。
一般来说,DLL 是一种磁盘文件(通常带有DLL 扩展名),它由全局数据、服务函数和
资源组成,在运行时被系统加载到进程的虚拟空间中,成为调用进程的一部分。如果与其他
DLL 之间没有冲突,该文件通常映shè到进程虚拟空间的同一地址上。DLL 模块中包含各种导
出函数,用于向外界提供服务。Windows 在加载DLL 模块时将进程函数调用与DLL 文件的
导出函数相匹配。
在Win32 环境中,每个进程都复制了自己的读写全局变量。如果想要与其他进程共享内
存,必须使用内存映shè文件或者声明一个共享数据段。DLL 模块需要的堆栈内存都是从运行
进程的堆栈中分配出来的。DLL 现在越来越容易编写。Win32 已经大大简化了其编程模式,
并有许多来自AppWizard 和MFC 类库的支持。使用Visual C++ 6.0 工具可以编写3 种不同类
型的动态链接库。
? Non-MFC DLL:指的是不用MFC 的类
松语文学免费小说阅读_www.16sy.com
// 打开互斥量
hMutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, g_strNcom);
if( hMutex == NULL )
{
AfxMessageBox("open Mutex error...");
return 1;
}
// 失败
if ( WaitForSingleObject(hMutex, INFINITE) == WAIT_FAILED )
{
return 1;
}
CByteArray m_Array;
// 读出发送数据
int Count=lpData->length;
m_Array.RemoveAll();
m_Array.SetSize(Count);
for(int i=0;i<Count;i++)
{
m_Array.SetAt(i,lpData->data[i]);
}
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·246·
// 使用串口发送数据
lpData->pComm->SetOutput(COleVariant(m_Array));
// 释放互斥量
ReleaseMutex(hMutex);
return 0;
}
(3)初始化互斥量
为对话框添加初始化互斥量的成员函数InitMutex,代码如下:
bool CSCommDlg::InitMutex()
{
g_hMutex = CreateMutex(NULL, false, g_strNcom);
if( g_hMutex == NULL )
{
AfxMessageBox("创建互斥对象错误");
return false;
}
return true;
}
(4)初始化串口
为对话框添加初始化串口的成员函数InitComm,代码如下:
bool CSCommDlg::InitComm()
{
CCommSettingDlg dlg;
if( dlg.DoModal() == IDOK )
{
m_nIndex = atoi(LPCTSTR(dlg.m_nCommID) );
}
//通信参数设置
// 设置串口号
m_Comm.SetCommPort(m_nIndex);
// 设置数据获取方式
m_Comm.SetInputMode(1);
枫叶文学网www.fywxw.com
第9 章 多线程
·247·
// 设置传输参数
m_Comm.SetSettings("9600,n,8,1");
m_Comm.SetRThreshold(1);
// 指定接收缓冲区大小
m_Comm.SetInBufferSize(1024);
// 清空接收缓冲区
m_Comm.SetInBufferCount(0);
// 设置读取方式
m_Comm.SetInputLen(0);
// 打开串口
if(!m_Comm.GetPortOpen())
{
m_Comm.SetPortOpen(TRUE);
}
// 读取数据
m_Comm.GetInput();
return true;
}
(5)响应OnComm 消息
利用ClassWizard 响应MSComm 控件的OnComm 消息,用来处理接收数据,代码如下:
void CSCommDlg::OnComm()
{
// TODO: Add your control notification handler code here
int nEvent;
VARIANT m_input;
char *str,*str1;
int k,i;
CString str2;
nEvent=m_Comm.GetCommEvent();
switch(nEvent)
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·248·
{
case 2:
// 接收缓冲区的字符数目
k=m_Comm.GetInBufferCount();
if(k>0)
{
m_input=m_Comm.GetInput();
str=(char*)(unsigned char*)m_input.parray->pvData;
}
i=0;
str1=str;
while(i<k)
{
i++;
str1++;
}
*str1=’\0’;
str2=(const char*)str; //清除字符串中的不必要字符
m_RevData = (LPCTSTR)str2;
default:
break;
}
// 显示数据
UpdateData(FALSE);
}
(6)发送数据
响应“发送”按钮的单击事件,用来发送数据,代码如下:
void CSCommDlg::OnSendData()
{
// TODO: Add your control notification handler code here
DWORD id;
UpdateData();
DATA senddata;
senddata.hWnd = this->GetSafeHwnd();
枫叶文学网www.fywxw.com
第9 章 多线程
·249·
senddata.length = m_SendData.GetLength();
strcpy(senddata.data, (LPCTSTR)m_SendData);
senddata.pComm = &m_Comm;
// 创建发送线程
CreateThread(NULL, 0, CommSendProc, &senddata, 0, &id);
}
(7)关闭串口
响应“关闭串口”按钮的单击事件,用来关闭串口,代码如下:
void CSCommDlg::OnCloseCom()
{
// TODO: Add your control notification handler code here
if( m_Comm.GetPortOpen() )
{
// 关闭串口
m_Comm.SetPortOpen(0);
}
}
(8)打开串口
响应“打开串口”按钮的单击事件,用来打开串口,代码如下:
void CSCommDlg::OnOpenCom()
{
// TODO: Add your control notification handler code here
if( !m_Comm.GetPortOpen() )
{
// 打开串口
m_Comm.SetPortOpen(1);
}
}
(9)退出
响应“退出”按钮的单击事件,用来释放资源,退出程序,代码如下:
void CSCommDlg::OnCancel()
{
// TODO: Add extra cleanup here
// 释放互斥量资源
ReleaseMutex(g_hMutex);
// 关闭句柄
CloseHandle(g_hMutex);
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·250·
CDialog::OnCancel();
}
(10)运行结果
运行程序后,首先弹出“选择串口参数”对话框,选择需要的串口,如图9-8 所示。
图9-8 “设置串口参数”对话框
在程序主界面中,单击“发送”按钮,发送输入框中的内容会出现在另一个程序的接收
框中,如图9-9 和图9-10 所示。
为了更加简便,本节仅给出利用多线程串口发送数据的方法,读者若有兴趣可以自行实
现多线程控制多个串口的功能。
图9-9 发送窗口 图9-10 接收窗口
9.4 本章小结
本章主要介绍Windows cāo作系统下多线程的基本概念,如何创建和管理线程,以及线程
的同步问题。通过本章的介绍,读者可以看出,多线程程序设计通常比一般的单线程程序复
杂,在程序设计过程中,一定要考虑清楚各线程的关系,避免出现死锁或不同步的现象。另
外需要注意现在大多数用户使用的是单CPU 计算机,在这种机器上运行多线程程序,有时反
而会降低系统的xìng能。因此,在设计多线程应用程序时,应慎重选择,视具体情况加以处理,
使应用程序获得最佳的xìng能。
枫叶文学网www.fywxw.com
第10 章 动态链接库
动态链接库(Dynamic Link Library)是一个可执行模块,其包含的函数可以由Windows
应用程序调用以执行一些功能,主要为应用程序模块提供服务。本章将全面、系统地阐述在
Visual C++平台下进行Win32 动态链接库的设计和应用。主要包括下面几个方面的内容。
? 动态链接库(DLL)的基本知识。
? DLL 的出入口函数。
? 调用DLL 中的两种方式。
? 开发DLL 的方式。
? DLL 中资源的利用。
? 钩子(Hook)函数的应用方法。
为了使读者充分理解概念,对于动态链接库的开发,本章使用了“界面汉化”的示例来
说明资源在动态链接库中的使用。钩子函数对于大多数读者来说可能是一项较为陌生的技术,
为了加深理解,这里列举了两个关于捕获消息的钩子函数示例,可以帮助读者更好地理解钩
子函数的原理和使用方法。
10.1 动态链接库的基础知识
比较大的应用程序都是由很多模块组成的,这些模块彼此协作,以完成整个软件系统的
工作。其中可能存在一些模块的功能较为通用,在构造其他软件系统时仍会被使用。在构造
软件系统时,如果将所有模块的源代码都静态编译到整个应用程序EXE 文件中,会产生一些
问题。一是增加了应用程序的大小,这样会占用更多的磁盘空间,程序运行时也会消耗较大
的内存空间,造成系统资源的浪费;另外,在编写大的EXE 程序时,每次修改重建时都必须
调整编译所有源代码,不但增加了编译过程的复杂xìng,也不利于阶段xìng的单元测试。
Windows 系统平台上提供了一种完全不同的有效编程和运行环境,可以将独立的程序模
块创建为较小的动态链接库(Dynamic Linkable Library)文件,并可对它们单独进行编译和
测试。在运行时,只有在EXE 程序确实要调用这些DLL 模块的情况下,系统才会将它们装
载到内存空间中。这种方式不仅减少了EXE 文件的大小和对内存空间的需求,而且使这些
DLL 模块可以同时被多个应用程序使用,从而充分利用资源。Microsoft Windows 将一些主要
的系统功能以DLL 模块的形式实现。例如IE 中的一些基本功能就是由DLL 文件实现的,它
可以被其他应用程序调用和集成。一般来说,下面的这几种情况必须用到动态链接库技术。
? 多个应用程序共享代码和数据就是通过共享动态链接库实现的,比如Office 软件的各
个组成部分有相似的外观和功能。
? 在钩子程序过滤系统消息时必须使用动态链接库。
? 设备驱动程序必须是动态链接库。
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·252·
? 如果要在对话框编辑器中使用自己定义的控件,也必须使用动态链接库。
? 动态链接库以一种自然的方式将一个大的应用程序划分为几个小的模块,有利于小组
内部成员的分工与合作。而且,各个模块可以独立升级。如果小组中的一个成员开发
了一组实用示例,他就可以把这些示例放在一个动态链接库中,让小组的其他成员使
用。
? 为了实现应用程序的国际化,往往需要使用动态链接库。使用动态链接库可以将针对
某一国家、语言的信息存放在其中。对于不同的版本,使用不同的动态链接库。在使
用AppWizard 生成应用程序时,可以指定资源文件使用的语言,这就是通过提供不同
的动态链接库实现的。
一般来说,DLL 是一种磁盘文件(通常带有DLL 扩展名),它由全局数据、服务函数和
资源组成,在运行时被系统加载到进程的虚拟空间中,成为调用进程的一部分。如果与其他
DLL 之间没有冲突,该文件通常映shè到进程虚拟空间的同一地址上。DLL 模块中包含各种导
出函数,用于向外界提供服务。Windows 在加载DLL 模块时将进程函数调用与DLL 文件的
导出函数相匹配。
在Win32 环境中,每个进程都复制了自己的读写全局变量。如果想要与其他进程共享内
存,必须使用内存映shè文件或者声明一个共享数据段。DLL 模块需要的堆栈内存都是从运行
进程的堆栈中分配出来的。DLL 现在越来越容易编写。Win32 已经大大简化了其编程模式,
并有许多来自AppWizard 和MFC 类库的支持。使用Visual C++ 6.0 工具可以编写3 种不同类
型的动态链接库。
? Non-MFC DLL:指的是不用MFC 的类
松语文学免费小说阅读_www.16sy.com