vc控件编程
⑴ 如何用VC进行串口编程
1、新建MFC对话框工程如下
双击两个Button按钮;
代码中显示如下:
[cpp] view plain print?
voidCMSCommTestDlg::OnBnClickedBtnopen()
{
//TODO:
}
voidCMSCommTestDlg::OnBnClickedBtnsend()
{
//TODO:
}
voidCMSCommTestDlg::OnOncommMscomm1()
{
//TODO:Addyourmessagehandlercodehere
}
- void CMSCommTestDlg::OnBnClickedBtnopen()
- {
- // TODO: Add your control notification handler code here
- }
- void CMSCommTestDlg::OnBnClickedBtnsend()
- {
- // TODO: Add your control notification handler code here
- }
- void CMSCommTestDlg::OnOncommMscomm1()
- {
- // TODO: Add your message handler code here
- }
voidCMSCommTestDlg::OnClickedBtnopen()
{
//TODO:
//如果端口已经开启,那么先关闭
if(m_comm1.get_PortOpen())
{
m_comm1.put_PortOpen(FALSE);
}
m_comm1.put_CommPort(3);//选择com3,可以根据具体情况更改
m_comm1.put_InBufferSize(1024);//设置输入缓冲区的大小,Bytes
m_comm1.put_OutBufferSize(1024);//设置输出缓冲区的大小,Bytes
m_comm1.put_Settings(_T("9600,n,8,1"));//波特率9600,无校验,8个数据位,停止位1
m_comm1.put_InputMode(1);//1:表示以二进制方式检索数据
m_comm1.put_RThreshold(1);//参数1表示每当串口接收缓冲区中有多于或等于1个字符时将引发一个接收数据的OnComm事件
m_comm1.put_InputLen(0);//设置当前接收区长度是0
if(!m_comm1.get_PortOpen())
{
m_comm1.put_PortOpen(TRUE);
}
else
{
AfxMessageBox(_T("Cannotopenserialport!"));
}
m_comm1.get_Input();//先预读缓冲区以清除残留数据
UpdateData(FALSE);
}
voidCMSCommTestDlg::OnClickedBtnsend()
{
//TODO:
UpdateData(TRUE);
m_comm1.put_Output(COleVariant(m_sTXDATA));//发送数据
}
voidCMSCommTestDlg::OnOncommMscomm1()
{
//TODO:Addyourmessagehandlercodehere
VARIANTvariant_inp;
COleSafeArraysafearray_inp;
LONGlen,k;
BYTErxdata[2048];
CStringstrtemp;
if(m_comm1.get_CommEvent()==2)//事件值为2表示缓冲区内有字符
{
variant_inp=m_comm1.get_Input();//读缓冲区
safearray_inp=variant_inp;//VARIANT型变量转换为ColeSafeArray型变量
len=safearray_inp.GetDim();//得到有效数据长度
for(k=0;k<len;k++)
{
safearray_inp.GetElement(&k,rxdata+k);//转换为BYTE型数组
}
for(k=0;k<len;k++)//将数组转换为CString型变量
{
BYTEbt=*(char*)(rxdata+k);//字符型
strtemp.Format(_T("%c"),bt);//将字符送入临时变量strtemp存放
m_sRXDATA+=strtemp;//接收到的数据放到编辑框对应的变量中
}
}
SetDlgItemText(IDC_EDIT_RXDATA,m_sRXDATA);
}
5、将上面代码补全如下:
[cpp] view plain print?
⑵ VC++编写ActiveX控件
ActiveX这门技术棚正是通过生成“*.ocx”文件来实现的。先来了解下OCX文件,在网络上面对OCX是这样解释的:“.ocx是ocx控件的扩展名,OCX 是对象类别扩充组件。如果你用过Visual Basic或者Delphi一类的可视化编程工具,那么对控件这个概念一定不会陌生,就是那些工具条上的小按钮,如 EditBox,Grid,ImageBox,明帆Timer等等。每个控件都有自己的事件、方法和属性。使用了控件的编程非常容易。首先,在程序的设计阶段可以设置一些属性,如大小,位置,标题(caption)等等;在程序运行阶段,可以更改这些属性,还可以针对不同的事激和雹件,调用不同的方法来实现对该控件的控制。控件就好像一块块的积木,程序要做的事只是将这些积木搭起来。控件的最大好处是可以重复使用,甚至可以在不同的编程语言之间使用,例如你可以在 VB中嵌入用VC开发的控件。”
里面最后一句话比较重要,就是用VC开发的OCX控件,你可以在其它语言里面都能调用,这样很好的实现了功能化组件的良好循环使用,而且还可以实现跨语言地调用(例如,你完全可以用C#调用C++开发的OCX控件)。
下面开始介绍,如何用VC++一步步生成你想要的“*.ocx”文件。
1. 建立最简单的ocx文件并进行调试
1.1 建立最简单的ocx文件
VC-新建项目-MFC ActiveX WinZard
一路点击“确定”,直到点击“完成”。最后VC++会自动生成一些文件,这些文件就构成了ActiveX的基本模板,文件的主要结构如下:
直接编译一下,然后在Debug目录下面就会生成一个名为“ocxDemo.ocx”的控件注册文件,然后利用“regsvr32”命令就可以实现本机对此控件的注册,然后就可以使用本语言或者跨语言编写程序时引用此控件来实现相应的功能(后面将会讲到)。
1.2 ocx调试方法:
VC++自带有一个调试控件的工具“ActiveX控件测试容器”,通过三种方式可以打开:
1.点击“调试”按钮,会出现如下对话框:
然后浏览"C:Program FilesMicrosoft Visual StudioCommonToolsTSTCON32.EXE“
2. 系统的“开始“-》“程序”-》“Microsoft Visual C++ 6.0”-》“Microsoft Visual C++ 6.0 Tools”-》“Active Control Test Container”
3. VC++开发环境中的“工具”-》“ActiveX Control Test Container”
通过上面的任意一种方法,都可以调出下面的程序:
右击空白区域,插入控件,然后会弹出下面的对话框:
选中指定控件,然后点击确定,控件就被加载到此工具中了,然后可以通过这个工具来看此控件的相关事件响应等等。
2.自VC++生成的模板基础上自定义功能
所有的自定义功能基本上都来自于“MFC ClassWizard”类向导对话框。
(“快捷键Ctrl+W”或者“查看”-“建立类向导”)
在“Automation”选项卡中为控件添加方法和属性。
在“ActiveX Events”选项卡中为控件添加事件。
2.1 添加控件属性
切换到“Automation”选项卡中,点击右边的“Add Property”会弹出对话框:
External name:外部名称。指此控件被使用时,外部程序看到的属性名称,仅在外部引用时被使用。
Type:属性类型。除了基本的整形等数据类型外,还有很多复杂的高级数据类型。
Variable name:变量名称。此属性在控件源文件中的变量名称,在编写控件源码时使用。
Notification function:提醒函数。当此属性被改变时,会触发此提醒函数。
Implementation:实现方式。指属性的三种类型:固有型,成员变量型,Get/Set方法型。固有型是指系统赋予的固有属性,如背景色,标题;成员变量型是用户自定义的属性;Get/Set方法型,可能是指只能通过Get/Set方法才能获取和改变的变量吧(这个没研究)。
2.2 添加控件方法
在“Automation”选项卡中,点击右边的“Add Method”会弹出对话框:
External name:方法外部名称。
Internal name:方法内部名称。
Return type:返回值类型。除了基本的整形等数据类型外,还有很多复杂的高级数据类型。
Implementation:实现方式。两种:固有方法,自定义方法。
Parameter list:参数列表。参数名称和参数类型:参数类型包含很多高级数据类型。
2.3 添加控件事件
切换到“ActiveX Events”选项卡中,点击右边的“Add Event”会弹出对话框:
External name:事件外部名称。
Internal name:事件内部名称。比外部名称多了个前缀“Fire”。
Implementation:实现方式。两种:固有事件,自定义事件。固有事件一般是鼠标移动,双击等等事件,这些事件都由系统消息触发;自定义事件则是完全由用户定义的一个函数,但这个函数需要用户在源文件中调用(在内部调用,对于控件的使用方来说,就相当于在调用的地方此事件被触发,而内部传入的参数,则是此事件产生的消息附带信息)。
Parameter list:参数列表。参数名称和参数类型:参数类型包含很多高级数据类型。
总述:通过“类向导”工具,为控件添加属性、方法和事件后,VC++会自动在相应的文件里面生成代码,比如内部方法属性和外部方法属性之间的映射,消息的建立,消息的声明,等等。如果用户要对引进行深入研究,还需要对程序的结构比较熟悉,知道各部分代码的作用,知道哪些地方的代码是系统自动生成的,哪些代码需要用户手动加入的。Visual C++开发环境虽然有很多优点,但有个缺点也很明显,就是代码结构比较乱,感觉没有VS2005和后面的Visual Studio系列要好。但是由于VC6.0作为一个比较经典的开发环境,而且网上的有关C++的程序设计基本上都是基于VC6.0的,所以,有必要对其进行学习,便于自己读懂网上的代码并进行消化吸收。
2.4 生成ocx文件并调试
直接编译用户加入了自定义代码的项目,然后在项目的Debug目录下面会生成一个ocx文件,这个就是此控件的注册文件了。
控件的调试工具仍然是“ActiveX Control Test Container”。
假设我们在控件中加入了一个事件:固有事件——“MouseMove”鼠标移动事件;用户自定义事件——ocxClick事件(此事件是通过“WM_MOUSEMOVE”消息来触发的,返回的是鼠标当前位置的x坐标)。
运行“ActiveX Control Test Container”并插入当前控件,当鼠标在上面移动的时候,可以看到MouseMove产生了事件了。
同时可以通过“Control”-》“Invoke Method”来对控件的方法进行测试,测试的方法就是你输入参数,它返回计算结果(下面以自定义的方法funHello为例)。
3.控件的使用方法
3.1 注册控件
ocx控件的安装方式有很多种,这里介绍最简单的一种。
步骤:
1.将需要安装的OCX控件文件复制到某个目录,例如C盘根目录下。
2.进入开始,点击运行。
3.在出现的框中键入regsvr32 C:/xxxx.ocx 。(XXXX为控件名, C:/为目录)
4.点击确认后等待出现提醒注册成功即可。
3.2 ActiveX控件的调用
ActiveX作为一种通用的COM组件,可以被不同语言调用的。
3.2.1 通过VC++调用
利用VC6.0建立一个MFC的基本对话框应用程序
在完成程序向导后。执行下面的步骤:
1. 执行“工程”-》“添加到工程”--》“Components and Controls”。
2. 在弹出的文件浏览对话框中,找到Registered ActiveX Controls文件目录下的你刚才注册的控件,比如“OcxDemo Control”,然后点击“Insert”按钮即可将此控件添加到控件工具条集合中。
3. 将控件工具条上新增加的OCX控件拖入到应用程序主窗口中。
完成上面的步骤后,就可以像使用普通控件一样在VC中使用此控件了(右击此控件,可以查看此控件的“事件”和“属性”,就是你在编写控件源码时的那些“外部名称External name”)。
3.2.2 通过C#调用
其实这个才是重点,因为跨语言调用ActiveX技术最被笔者看好的地方。
用Visual Studio 2005新建一个C#.NET的Windows窗口程序,然后在工具箱面板上,右击“选择项”,选择COM组件,找到你注册的ActiveX控件:
确定后,那个OcxDemo Control控件就加载到工具箱里面了。可以直接拖动这个控件到C#.NET应用程序的主窗口上去了,然后就像使用普通控件那样使用此控件了。
比如,本文中的ActiveX控件的自定义事件中,是通过鼠标移动来触发,那么在应用程序中,只要鼠标移动到控件上,那么就会触发此自定义事件,并获取当前鼠标位置的横坐标。
4. 最后一些Tip
1) 在自定义控件时,可在控件源码的OnDraw()函数中设置控件的外观(也就是控件被拖入到应用程序中时呈现的样子,一般默认是一个白色的方框内切椭圆的样式)。
2) 用C#来使用ActiveX的事件时,事件所产生的数据都包含在Event变量中,只需要用个点运算符就可以取出来了。
5.展望
控件函数的返回值类型那么多,那么复杂,如果要用得好,还需要对那些OLE数据类型进行好好学习,这个等今后需要时再慢慢学习吧。
⑶ 如何用vc编写程序
第一篇
为Non-COM程序添加对象模型(2)
初始化对象模型
创建一个新的组件实例,调用Load方法来获得一对结果。首先,连接到记事本运行中的拷贝。其次,在记事本窗口中打开一个已存在的文档或创建一个空文档。
与记事本相结合,需要夺取主窗体的句柄和覆盖了整个客户端区域的编辑控件的句柄。可以用C++ FindWindow API函数检索第一个打开的并且和记事本的Windows类名“notepad”相匹配的窗口(此后台信息已经可以由Spy++提供,它是一个Visual Studio工具,可以透视Windows的隐私),可以使用以下的C++代码:
STDMETHODIMP
CNotepadApplication::Load(BSTR bstrFile)
{
m_hwnd = FindWindow(_T("notepad"), NULL);
if (!IsWindow(m_hwnd))
_StartApp(OLE2T(bstrFile));
Load方法尝试找到一个运行中的记事本实例。如果成功,它忽略输入的文件名。否则,它产生nodepad.exe,并用命令行传递bstrFile参数。
这是仅有的可能的方法来做到这些了。可以更改Load方法的行为遵守其他的规则。然而,需要注意的是,在程序的用户接口中隐蔽地加载一个文本文件是通过命令行来实现的。否则,必须求助File菜单中的Open命令,但这就不是自动和隐蔽的了。
一旦找到了记事本主窗体的句柄,就可以利用它并使用C++代码检索子编辑控件。
m_hwndEdit = FindWindowEx(
m_hwnd, NULL, _T("edit"), NULL);
记事本的结构提供了一个类名为“notepad”的窗口,它的客户区域被一个编辑控件占据——一个类名为“edit”的窗口。FindWindowsEx API函数检索第一个类名为“edit”的窗口,它是m_hwnd的子女。
下一步,在COM对象中创建一个属性,它描述子编辑控件的内容。调用名为Text的可读写属性。给它一个文本内容,它将会立即影响到记事本的缓冲区。
Set npad = CreateObject("NotepadOM.Application")
npad.Load ""
npad.Text = "Sample text"
在前面的代码中,我们建立了一个新的未明名的文本文档,它的内容已经被赋予了某个字符串。当然,可以使用Text属性连接文本到其他变量中。
npad.Text = "Sample text"
npad.Text = npad.Text & vbCrLf & "for the article"
即使记事本是个SDI程序,也可能需要像清晰的对象调用过程那样公开文本内容,例如文档操作。这符合更清楚、更雅致的模型设计,但是它仍需要为架构设计带来多余的复杂性。为什么创建一个新的ATL对象仅仅是为了优化一些文本相关的功能呢?
在实现Text属性时,利用了Windows32编辑控件的一个鲜为人知的特性。所有Windows32控件不能跨进程访问。例如,不能请求另一个应用程序的rich edit box以字符串类型返回它的内容。产生这个问题的原因是,任何内存地址只在进程管理范围内才有效。这个规则有少部分例外。
所有的Windows标准控件buttons、listboxes、和edit controls或者其他控件都不违背这项规则。它们的内容以在进程间被任意地读或写。这功能在Windows 95时为了保持向后兼容现存的Windows3x程序就出现了,它用进程间子类化。此同样存在于Windows XP和Windows 2000中。
可以使用一些消息,如WM_GETTEXT和WM_SETTEXT来获得或写入文本框的内容而不顾实际进程的相关情况。同样,当运行VBS脚本时,实际上已涉及到两个不同的进程,记事本和wscript.exe,它们控制着VBS脚本。用C++实现此Text属性,代码如下:
STDMETHODIMP
CNotepadApplication::get_Text(BSTR *pVal)
{
USES_CONVERSION;
int nLen = 1 + SendMessage(m_hwndEdit, WM_GETTEXTLENGTH, 0, 0);
LPTSTR pszBuf = new TCHAR[nLen];
SendMessage(m_hwndEdit, WM_GETTEXT, nLen, (LPARAM) pszBuf);
*pVal = SysAllocString(T2OLE(pszBuf));
delete [] pszBuf;
return S_OK;
}
STDMETHODIMP
CNotepadApplication::put_Text(BSTR newVal)
{
USES_CONVERSION;
SendMessage(m_hwndEdit, WM_SETTEXT, 0, (LPARAM) OLE2T(newVal));
return S_OK;
}
添加编辑函数
访问编辑控件的句柄可以弄清编辑所需的一串函数——特别是关于文本选择的部分。可以很容易地添加方法选择所有的缓冲区中的文本或限制为某个区域选择。SelectAll和SelectText用C++实现,方法如下:
STDMETHODIMP
CNotepadApplication::SelectText(
int nFrom, int nTo) {
SendMessage(m_hwndEdit, EM_SETSEL, nFrom-1, nTo-1);
return S_OK;
}
通过EM_SETSET消息可以很容易地在编辑控件中实现文本选择。在Windows32中,第一个可选的字符是在0位置,但是相关方法使它从1开始。而指定-1~0的范围可以选择整个文本。
编辑框中正文的字体名称由某个注册值lfFaceName决定,在以下位置可以找到此键值:
HKEY_CURRENT_USER
\Software
\Microsoft
\Notepad
将它设为想要用的键值。记事本在启动之前读取这个设置。为了使它生效,请记住在调用Load之前设置好它。
set npad = CreateObject("NotepadOM.Application")
npad.Font = "Lucida Console"
npad.Load "readme.txt"
当一个交互式的用户单击菜单时,例如“File | Open”,主窗体发送WM_COMMAND消息,其中WPARAM参数被赋予串联的两个字。低位字是命令的ID,高位字包含消息码或表示触发的值——键盘加速键或菜单。用C++调用一个菜单命令、发送一个WM_COMMAND消息到记事本,代码如下:
SendMessage(m_hwnd, WM_COMMAND,
MAKELONG(nCommand,0), 0);
必须用特殊的工具为nCommand参数指出正确的值,就像Spy++。既然这样,我稍微修改文章中所描述的DLL版本。“Hook,Line and Sinker”〔Visual C++ Developers Journal February 2001〕。此例程产生并钩住,然后创建记事本的子类。它过滤窗口接收到的所有消息,并在命令代码是WM_COMMAND时弹出对话框显示command ID。
if (uiMsg == WM_COMMAND) {
// Get the value of LOWORD(wParam)
}
需要添加的仅仅是存储或显示命令代码的程序。检验主记事本的菜单命令ID。只要给出了这个,调用菜单命令就很简单了,代码如下:
const NOTEPAD_FILE_OPEN = 10
Set npad = CreateObject("NotepadOM.Application")
npad.InvokeMenu NOTEPAD_FILE_OPEN
如果要编程关闭运行中的实例,需要想到在记事本窗口上调用DestroyWindows。然而,DestroyWindows只能在属于同一进程的窗口的进程中调用。要卸载记事本,用C++简单的发送一条退出代码的WM_COMMAND消息:
SendMessage(m_hwnd, WM_COMMAND,
MAKELONG(28,0), 0);
有些功能是无法从非自动化的程序中获得的。例如,打开文件和另存为是不可能实现的,因为程序并不通过消息或API暴露这些代码,需要编写代码来存储它。举个例子来说,在记事本中,存储运行时结果需要响应Save或Save As命令,但是它们都是交互式的命令,需要用户单击OK按钮或输入一个新的文件名。这是原解决方案固有的限制。
最近,在一个客户中碰到一个相似的问题,我应要求在不同环境处理一些传统的Windows程序(其中一个是记事本)。本质上来说,Win32 made-to-measure应用程序获得TCP/IP通道指令并转换它们以执行本地的Windows应用程序。通过Windows32消息请求服务的方式和在此所做的很相似。下一目标是用COM对象模型封装此通信模式。
关于作者
Dino Esposito是Wintellect的ADO.NET专家和培训师并且在罗马当咨询师。Dino是《Building Web Solutions With ASP.NET and ADO.NET》(微软出版)一书的作者,是VB-2-The-Max (http://www.vb2themax.com <http://www.vb2themax.com>)的创始人。可通过[email protected]联系到Dino。