
2009年2月26日星期四
为什么Rich Edit会使对话框的DoModal()函数失败
用Appwizard生成一个简单的Dialog based Application,在上面放
一个Rich Edit标准控件,编译执行,没有出现主对话框窗口。调试发现,当
执行m_pMainWnd.DoModal()函数时,创建窗口失败返回了-1,直接推出程序。
我能确信是由Rich Edit标准控件引起的,因为将Rich Edit控件换成Edit控件,
程序运行正常。
怀疑是哪一个连接库坏了,于是卸除Visual Studio,重装,问题依旧。
前几天编译一个有List contrl的Dialog based Application,发现
在编译的机器上能执行,在另外一台机器上却不能执行。但是通过在另外那台
机器上装了个Visual Studio,也不用再该机器上重编译程序就解决问题了。
不知道这两个问题时有什么引起的,有没有关系。
1 楼adrianx(蓝色心情)
请在CXXXApp::InitInstance()中m_pMainWnd->DoModal()前调用
AfxInitRichEdit();
你的DoModal就可以用了
另外一台机上是否没有RICHED32.DLL和OLE32.dll
和MFC的一些DLLTop
2 楼coolskeeter(文子)
对于这位兄台所说的关于Rich Edit 控件的问题,小弟碰巧也碰到过。情形是这样的,我在一个document_view 结构的程序中,要从菜单click事件中 使一个dialog .DoModal().但是该对话框并不能出现,调试时发现对话框的m_hWnd NULL。将对话框中的Rich Edit控件删除后,一切完好。最后我的解决办法是,在该dialog 的OnInitDialog() 中用程序去生成一个RichEdit, 那样可以用了,并能正确显示。
class CMyDialog
{
//.....
protected:
CRichEditCtrl m_myRichEdit;//添加一个成员变量到对话框类。
//.............
}
CMyDialog::OnInitDialog()
{
//...................
if(!m_myRichEdit.Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID ))
MessageBox("Create RichEdit fail");
//...................
}
其中,nID 我是在.h 文件中自己去
#defined IDC_RICHEDIT 128 写的。
希望能对老兄有点帮助。不过如果有简单点的办法像其他控件一样的做法的话,还望能告知小弟一声。
你说得第二个问题我想是由于动态库的问题。安装了visual Studio后自动安装了一些 DLLs 吧。
3 楼happylaodu(青菜豆腐)
同意adrianx的意见:当在你的程序中要使用CRichEditCtrl时,就必须调用AfxInitRichEdit();函数。
但coolskeeter所说的方法也可行,只不过,对于rect参数,事先得要花点脑筋算一算,最后显示出来的效果如果不满意,还得重设,有点繁。
2009年2月25日星期三
VC中如何打开指定程序或者文件
UINT Win Exec(LPCSTR lpCmdLine, UINT uCmdShow);
⑵ 参数:
lpCmdLine:指向一个空结束的字符串,串中包含将要执行的应用程序的命令行(文件名加上可选参数)。
uCmdShow:定义Windows应用程序的窗口如何显示,并为CreateProcess函数提供STARTUPINFO参数的wShowWindow成员的值。
⑶ 返回值:
若函数调用成功,则返回值大于31。若函数调用失败,则返回值为下列之一:
① 0:系统内存或资源已耗尽。
② ERROR_BAD_FORMAT:EXE文件无效(非Win32.EXE或.EXE影像错误)。
③ ERROR_FILE_NOT_FOUND:指定的文件未找到。
④ ERROR_PATH_NOT_FOUND:指定的路径未找到。
虽然Microsoft认为WinExec已过时,但是在许多时候,简单的WinExec函数仍是运行新程序的最好方式。简单地传送作为第一个参数的 命令行,还需要决定如何显示程序(该程序也许会忽视它)的第二个参数。通常,将其设置为SW_SHOW,也可尝试SW_MINIMIZED 或 SW_MAXIMIZED。WinExec不允许用CreateProcess获得的所有选项,而它的确简单。
使用ShellExecute命令
⑴ 函数原型:
Quote:
HINSTANCE ShellExecute(HWND hwnd, LPCTSTR lpOperation, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd);
⑵ 参数:
hwnd:指向父窗口的窗口句柄。此窗口接收应用程序产生的任何信息框。
lpOperation:一个空结束的字符串地址,此字符串指定要执行的操作。下面的操作字符串是有效的:
"open" 此函数打开由参数lpFile指定的文件,此文件可以是一个可执行文件或文档文件,也可是一个文件夹。
"print" 此函数打印由参数lpFile指定的文件,此文件应是一个文档文件,假如此文件是一个可执行文件,则打开此文件。
"explore" 此函数搜索由参数lpFile指定的文件夹,此文件应是一个文档文件,
此参数可以为空。这种情况下,函数用于打开由参数lpFile指定的文件。
lpFile:一个空结束的字符串地址,此字符串指定要打开或打印的文件或者是要打开或搜索的文件夹。
lpParameters:假如参数lpFile指定一个可执行文件,lpParameters则是一个空结束的字符串地址,此字符串指定要传递给应用程序的参数。假如lpFile指定一个文档文件,lpParameters应为空。
lpDirectory:一个空结束的字符串地址,此字符串指定默认目录。
nShowCmd:假如lpFile指定一个可执行文件,nShowCmd表明应用程序打开时如何显示。假如lpFile指定一个文档文件,nShowCmd应为空。
⑶ 返回值:
若函数调用成功,则返回值大于32,否则为一个小于等于32的错误值。
说明:可以用此函数打开或搜索一个外壳文件夹。打开文件夹可用下面任何一种形式:
Code:
ShellExecute(handle, NULL, path_to_folder, NULL, NULL, SW_SHOWNORMAL);
或
Quote:
ShellExecute(handle, "open", path_to_folder, NULL, NULL, SW_SHOWNORMAL);
搜索文件夹,可用如下形式
ShellExecute(handle, "explore", path_to_folder, NULL, NULL, SW_SHOWNORMAL);
ShellExecute 命令虽已过时但易于得到。该命令向命令解释程序提出打开、浏览或打印文档或文件夹的请求,虽然可以用 ShellExecute运行程序,但通常只发送文档名,而命令解释程序则决定要运行那个程序。另外在打开目录文件夹时,ShellExecute命令非 常有用。
⑷ 程序示例
下面通过一个例子来说名WinExec和ShellExecute的使用。下面程序有控制台程序示例,其使用两种不同的方法,打开文本文件。下面程序使用WinExec,并明确指定使用记事本程序。然后,使用ShellExecute,打开文本文件。
程序清单
Code:
#include
#include
void main(int argc,char *argv[])
{
cout < <”Opening with WinExec\n”;
if (WinExec(“notepad readme.txt”,SH_SHOW) <32)
MessagBox(NULL,”Can’t WinExec”,NULL,MB_OK);
cout < <”Press Enter\n”;
MessagBox(NULL,”Press OK to continue”,”Progrm Launched”,MB_OK);
cout < <”Opening with ShellExecute\n”;
if (ShellExecute (NULL,”open”,”readme.txt”,NULL,NULL,SW_SHOW) <(HANDLE) 32)
MessagBox(NULL,”Can’t ShellExecute\n”,NULL,MB_OK);
}
使用CreateProcess命令
⑴ 函数原型:
Code:
BOOL CreateProcess(
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
⑵ 参数:
lpApplicationName:指向一个以空结尾的串,他指定了要执行的模块
lpCommandLine:指向一个以空结尾的串,该串定义了要执行的命令行。
lpProcessAttributes:指向一个SECURITY_ATTRIBUTES结构,该结构决定了返回的句柄是否可被子进程继承。
lpThreadAttributes:指向一个SECURITY_ATTRIBUTES结构,该结构决定了返回的句柄是否可被子进程继承。
bInheritHandles,:表明新进程是否从调用进程继承句柄。
dwCreationFlags:定义控制优先类和进程创建的附加标志。
lpEnvironment:指向一个新进程的环境块。
lpCurrentDirectory:指向一个以空结尾的串,该串定义了子进程的当前驱动器和当前目录。
lpStartupInfo:指向一个STARTUPINFO结构,该结构定义了新进程的主窗口将如何显示。
lpProcessInformation:指向PROCESS_INFORMATION结构,该结构接受关于新进程的表示信息。
⑶ 返回值:
若函数调用成功,则返回值不为0;若函数调用失败,返回值为0。
ShellExecute和WinExec命令用于简单的作业。如果要完全控制一个新进程,就必须调用CreateProcess。
在上述参数中,参数lpStartupInfo是STARTUPINFO结构。可以用来设置控台的标题,新窗口的的初始大小和位置,及重定向标准输入 和输出。新程序通常可以忽略多数这些数据项,如果选择那样做的话。可以规定该结构体中的标志,已表明要设置的数据段。有时,不想设置任何信息,也必须传递 一个有效的指针给空结构(确定设置大小到cb,及设置dwFlags成员为0)。参数lpProcessInformation返回进程和线程句柄,还包 括进程和线程ID。这些句柄拥有在参数lpProcessAttributes和lpThreadAttributes中规定的访问。
要注意,针对CreateProcess的一些参数对控制台应用程序是特定的,而其它参数则对各种应用程序有用。大多数情况下,并不一定要填入 STARTUPINFO结构,但无论如何必须提供它。其返回值是布尔型的,而真正感兴趣的返回值发生于作为参数传送的结构中 (PROCESS_INFORMATION)。CreateProcess返回该结构中的进程ID及其句柄,以及初始线程ID及其句柄。可以将ID发送到 其它进程,或使用句柄来控制新进程。
如何对CTreeCtrl遍历和查找
遍历树
//hitem:待遍历树的根节点
void TreeVisit(HTREEITEM hItem)
{
AfxMessageBox(GetItemText(hItem));
if(ItemHasChildren(hItem))
{
HTREEITEM hChildItem = GetChildItem(hItem);
while(hChildItem!=NULL)
{
TreeVisit(hChildItem); //递归遍历孩子节点
hChildItem = GetNextItem(hChildItem, TVGN_NEXT);
}
}
}
如何根据名称查找树中的某个节点
//item:待遍历树的根节点,strtext:待查找节点名称
HTREEITEM finditem(HTREEITEM item, CString strtext)
{
HTREEITEM hfind;
//空树,直接返回NULL
if(item == NULL)
return NULL;
//遍历查找
while(item!=NULL)
{
//当前节点即所需查找节点
if(GetItemText(item) == strtext)
return item;
//查找当前节点的子节点
if(ItemHasChildren(item))
{
item = GetChildItem(item);
//递归调用查找子节点下节点
hfind = finditem(item,strtext);
if(hfind)
{
return hfind;
}
else //子节点中未发现所需节点,继续查找兄弟节点
item = GetNextSiblingItem(GetParentItem(item));
}
else{ //若无子节点,继续查找兄弟节点
item = GetNextSiblingItem(item);
}
}
return item;
}
这两个函数均在CTreeCtrl的扩展类当中做为成员函数即可实现树的遍历和查找。
两个函数遍历的实质是相同的:先查找第一个子节点,再查找以该节点为根的子树,再逐个查找该子节点的兄弟节点,最终完成遍历。第二个函数在找到符合条件的节点时即返回,而第一个函数则访问了树的所有节点。
这两个函数均在CTreeCtrl的扩展类当中做为成员函数即可实现树的遍历和查找。
两个函数遍历的实质是相同的:先查找第一个子节点,再查找以该节点为根的子树,再逐个查找该子节点的兄弟节点,最终完成遍历。第二个函数在找到符合条件的节点时即返回,而第一个函数则访问了树的所有节点。
两个函数均在VC60环境下编译运行通过。
2009年2月19日星期四
MFC动态创建控件的消息处理
本例在view中动态创建一个ListCtrl并响应其NM_CLICK消息,先写出动态创建的代码:
新建一SDI工程,给view加上一个成员变量:
CListCtrl m_list;
在view的OnCreate中创建它:
if(!m_list.Create(WS_BORDER |WS_CHILD|WS_VISIBLE|LVS_ICON |LVS_AUTOARRANGE|LVS_SHOWSELALWAYS|LVS_EDITLABELS,CRect(10,10,310,210),this,ID_LISTCTRL))
{
TRACE0("Failed to create ListCtrl window\n");
return -1;
}
//添加一些项
CString str;
for(int i=0;i<10;i++)>Resource Symbols弹出的对话框中点击New按钮,在Name下面输入ID_LISTCTRL.
创建完毕,编译链接后就可以运行了,但你怎么点击它都没反应,当然,因为我们还没写消息处理的代码。
通常做法是手动写消息处理函数及消息映射,至少得三步:1,在头文件中定义消息处理函数的原型,2,在cpp中实现这个函数,3,在 BEGIN_MESSAGE_MAP和END_MESSAGE_MAP()之间写上消息映射。如果是单单处理一个消息的话那还可以忍受,消息一多的话不但 麻烦还容易出错。
所以我采取下面的方法:
1.打开about对话框资源,放入一个ListCtrl控件到对话框中,并将其ID设为ID_LISTCTRL.
2.Ctrl+W,把Class Name由原来的CAboutDlg改为CxxxView,在左边的Object IDS中选择ID_LISTCTRL,这时右边会出现相应的通知消息,这时就可以跟平常一样双击添加消息映射了.
嘿嘿,是不是跟在对话框里响应控件的消息一样很方便呀.
有一点需要注意,只有当打开你加入控件的那个对话框资源编辑器的时候按Ctrl+W时,Class Wizard中才会出现那个ID_LISTCTRL,还有发行时注意把对话框上的listctrl控件删掉.
2009年2月18日星期三
使用MFC开发ActiveX控件全过程
1、 创建工程:
对于使用过VC的人,可以很容易地创建一个开发ActiveX控件的工程,没有使用过VC的人,按
照下面的操作步骤,也可以很快创建一个同样的工程出来。
第一步:“File”—>“New”—>“(Projects)MFC ActiveX ControlWizard”,在“Project Name”中输入合适的工程名(以test为例),在“Location”中选择工程文件存放路径,然后,“OK”进入下一步;
第二步:选择你想在这个工程中生成的ActiveX控件的个数(至少一个),其余选项决定是否生成一些辅助文件,通常按照默认设置即可,“Next”进入下一步;
第三步:编辑你的工程中各个类和文件的名称,配置一些辅助选项,可以全部选择默认设置,“Finish”进入下一步;
第四步:展示向导为你的工程生成的各种配置信息,“Cancel”重新设置不满意的选项,“OK”结束工程的创建。
2、 绘制控件:
MFC将对ActiveX控件的支持封装在COleControl类中,所有ActiveX控件均从这个类
派生。绘制控件的全部操作则集中在一个虚函数中—OnDraw(),其默认实现如下:
void CTestCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
// TODO: Replace the following code with your own drawing code.
pdc->FillRect(rcBounds,
CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
pdc->Ellipse(rcBounds);
}
可以看到,OnDraw()函数的默认实现是在矩形rcBounds中绘制一个椭圆,通过改写OnDraw()函数中的内容,就可以绘制自己想要的控件了。需要注意的一点,控件的绘制被限制在矩形rcBounds中,绘制范围不能超出这个矩形。
3、 添加属性:
ActiveX控件提供了三种类型的属性供用户选择:公共属性、具有通知特性的属性和Get/Set
类型的属性。下面依次对三种类型属性的添加、初始化、新属性值的获取和属性的永久保存,分别进行介绍。
三种类型的属性的添加,都遵照相同的步骤:“View”—>“ClassWizard”—>“(Automation)Add Property”—>“Add Property”对话框。注意,“Class Name”中应是你想添加属性的控件类的名称(如CTestCtrl)。
㈠. 公共属性:
COleControl类提供了九种常用的属性作为公共属性,我们以“Caption”为例。在“Add Property”对话框中的“External Name”选择“Caption”,“Implementation”中选中“Stock”,然后“OK”即向控件添加了Caption公共属性。
COleControl类为每一个公共属性都提供了默认的设置和获取成员函数,对于Caption属性有SetText()和GetText()(或InternalGetText())。
COleControl类在函数DoPropExchange()中对于公共属性都有默认的初始化,但是我们可以在成员函数OnResetState()中,为公共属性设置我们自己想要的初始值。Caption默认的初始值为空,可以使用成员函数SetText( LPCTSTR pszText ),为其设置任意的字符串。
我们可以通过调用函数GetText()或InternalGetText()获得当前的Caption属性值。需要注意的是,对于GetText()函数,在调用之后,需要调用SysFreeString()函数以释放资源。此外,GetText()函数的返回值为BSTR类型,经常需要与大家常用的CString字符串类型进行变换:BSTR可以直接赋值CString,也可以调用API函数::SysAllocString(Cstring);但是将CString转换成BSTR,必须调用函数CString::AllocSysString()。InternalGetText()函数则无上述烦恼。
对于公共属性的永久保存,在COleControl类的成员函数DoPropExchange()有默认实现,就无需大家费心了。
㈡. 具有通知特性的属性:
在“Add Property”对话框的“Implementation”中选中“Member variable”,即选择了向控
件添加具有通知特性的属性。在“External Name”中输入属性的名字(Shape),在“Type”中选择变量类型(BOOL),“Variable Name”和“Notification function”中会自动生成默认的变量名(m_shape)和通知函数名(OnShapeChanged),当然也可以手工输入自己习惯的名字。最后,“OK”便向控件添加了一个具有通知特性的属性。
对于属性的初始化和永久保存,只要在DoPropExchange()函数中调用PX_函数即可。按照上面添加的属性例子,可以如是调用:PX_Bool(pPX, _T("Shape"), m_bShape, FALSE)。其中,“FALSE”为变量“m_bShape”即属性“Shape”的初始值。
至此,不得不先说一下控件的属性对话框了。MFC将控件的属性对话框封装在类COlePropertyPage中,所有控件的属性对话框都派生自这个类。控件的属性对话框,是控件开发者提供给控件用户、用于定制控件外观的通讯工具,控件用户可以通过它来修改控件的某些属性。COlePropertyPage类中最重要的成员当属DoDataExchange()函数了,控件开发者就是通过在其中调用DDP_函数把控件的属性与控件属性对话框中的各种控件联系起来的。对于控件的公共属性,在为属性对话框中的相应控件添加变量的时候,可以在“Optional property name”中选择相应的公共属性,则属性对话框类就会在DoDataExchange()函数种自动添加相应的DDP_函数,例如,对于Caption属性,就会自动添加这样的语句:DDP_Text(pDX, IDC_CAPTION_EDIT, m_strCaption, _T("Caption") );但是对于用户添加的自定义属性,则必须手工输入DDP_函数,例如,对于前面添加的“Shape”属性,应添加如下语句:DDP_Check(pDX, IDC_SHAPE_CHECK, m_bShape, _T("Shape") )
。
对于“Notification function”需要补充说明的是,虽然在相应的函数中,属性添加向导会自动添加函数SetModifiedFlag(),但是如果需要在属性被改变时,相应改变控件的显示,则需开发人员手工加入函数InvalidateControl()(它会激发OnDraw()函数的调用)。
㈢. Get/Set类型的属性:
在“Add Property”对话框的“Implementation”中选中“Get/Set mothods”,即选择了向控
件添加“Get/Set类型的属性”。 在“External Name”中输入属性的名字(Color),在“Type”中选择变量类型(OLE_COLOR),“Get function”和“Set function”中会自动生成默认的函数名“GetColor”和“SetColor”,当然也可以手工输入自己习惯的函数名。最后“OK”,便完成了向控件添加Get/Set类型的属性。
对于属性的初始化和永久保存,和上面“具有通知特性的属性”一样,只要在DoPropExchange()函数中调用相应的PX_函数即可,不过在此之前,需要声明一个与属性类型相同的变量(m_clrInside),用于保存属性的值。(PX_函数: PX_Color(pPX, _T("Color"), m_clrInside, RGB(255, 255, 255)),其中“RGB(255, 255, 255)”为变量“m_clrInside”即属性“Color”的初始值)。
现在,需要说明一下公共属性页的问题了:在ActiveX默认的属性页中,没有字体和颜色属性页,但我们可以通过在属性页的ID表中添加入口的方法添加这两个属性页。加入颜色属性页的代码如下:
BEGIN_PROPPAGEIDS(CTest1Ctrl, 2)
PROPPAGEID(CTest1PropPage::guid)
PROPPAGEID(CLSID_CColorPropPage)
END_PROPPAGEIDS(CTest1Ctrl)
其中,以粗体显示的代码,由开发人员加入,同时ID数目增加1,由原来的1变为2。
Get/Set类型的属性,属性值的获取和设置分别由相应的Get/Set函数处理。Get函数中返回当前的属性值(return m_clrInside);Set函数中设置新的属性值(m_clrInside = nNewValue),同样,如果需要在属性被改变时,改变控件的显示,需开发人员手工加入函数InvalidateControl()。
4、 添加事件:
事件是AcTiveX控件通知ActiveX控件容器的手段,一般事件是由一些交互操作激发的,如鼠
标操作、键盘操作等。ActiveX控件支持公共事件和自定义事件,两者添加方法相似,只是公共事件由COleControl类自动处理。
使用VC自带的ClassWizard添加事件:“View”—>“ClassWizard”—>“(ActiveX Events)Add Event...”,出现“Add Event”对话框。如果是添加公共事件,只要在“External Name”下拉列表中选择想要添加的事件,“OK”即可;添加自定义事件,则在“External Name”中输入自定义事件的名称(ClickIn),“Internal Name”中会自动生成事件激发函数的名称(FireClickIn),也可以输入自己习惯的名字,然后在“Parameter list”中输入需要的参数名称(Hit)和参数类型(BOOL),最后“OK”就完成了自定义事件的添加。
对于公共事件,COleControl类会自动激发,例如:当鼠标中的任意一个键在单击控件时,Click事件就会自动被激发,向控件容器发送通知;对于自定义事件,必须由开发人员在需要激发事件的时候,调用相应的成员函数,来激发事件,以上面的“ClickIn”自定义事件为例,在需要激发事件的地方,需要开发人员调用相应的事件激发函数“FireClickIn”来激发“ClickIn”事件。附带一提,事件携带的参数是控件容器判断事件类型的依据,例如上面的“Hit”参数,其值为“TRUE”或“FALSE”,就可以代表两种不同的情形。
5、 测试控件:
完成了控件的编码工作,接下来就需要对控件进行相关的测试了。测试控件,就需要一个控件
容器来装载控件,如果只是为了测试控件,而专门开发一个控件容器,根本就是本末倒置、得不偿失,开发一个ActiveX控件容器的工作量,是绝对不容小觑的!幸运的是,VC为我们提供了一个方便的ActiveX控件测试工具—“ActiveX Control Test Container”(以下简称ACTC),这是VC为ActiveX控件开发人员,方便进行ActiveX控件的测试,而专门开发的一个ActiveX控件容器。
ACTC使用起来非常方面,可以通过两种方式打开它:一、在VC的主界面中,“Tools”—>“ActiveX Control Test Container”;二、“Execute Program”时,在跳出的“Executable For Debug Session”对话框的“Executable file name”中选择“ActiveX Control Test Container”,然后“OK”即可。
ACTC打开之后,“Edit”—“Insert New Control”或在窗口空白处单击右键,然后选择“Insert New Control”,就会打开“Insert Control”对话框,最后在对话框左边的列表中找到自己的控件并选中,“OK”相应控件就会被添加到并显示在ACTC中。
测试控件的属性,可以“Edit”—>“Properties...”,就会打开控件的属性对话框;测试事件,事件的结果会在ACTC下面的窗口中显示。
2009年2月17日星期二
ActiveX控件在IE中不能显示的解决方案
Q:
VC6生成MFC ActiveX,使用htm调用,
CLASSID="CLSID:12342234-3234-4234-5234-623472348234"
CODEBASE="testocx.cab#version=1,0,0,1"
在WinXPSp1上,http://www.smth.org/,ok
在WinXPSp2/2k3SP1/Vista上,http://www.smth.org/,红X
在WinXPSp2/2k3SP1/Vista上,file:///C:/ActiveX.htm,ok
A:
ActiveX在远程IE页面上执行,需要实现安全接口。
在ATL写的ActiveX中,用IObjectSafety。
http://support.microsoft.com/kb/168371/en-us
在MFC写的ActiveX中,直接修改注册表。
http://support.microsoft.com/kb/161873/en-us
mfc实现的ocx,要在app实现文件中包括两个文件:
atl实现的ocx,在声明文件CTestCtl.h中:
或者:
VC常用数据类型使用转换详解
我们先定义一些常见类型变量借以说明
int i = 100;
long l = 2001;
float f=300.2;
double d=12345.119;
char username[]="女侠程佩君";
char temp[200];
char *buf;
CString str;
_variant_t v1;
_bstr_t v2;
一、其它数据类型转换为字符串
短整型(int)
itoa(i,temp,10);///将i转换为字符串放入temp中,最后一个数字表示十进制
itoa(i,temp,2); ///按二进制方式转换
长整型(long)
ltoa(l,temp,10);
二、从其它包含字符串的变量中获取指向该字符串的指针
CString变量
str = "2008北京奥运";
buf = (LPSTR)(LPCTSTR)str;
BSTR类型的_variant_t变量
v1 = (_bstr_t)"程序员";
buf = _com_util::ConvertBSTRToString((_bstr_t)v1);
三、字符串转换为其它数据类型
strcpy(temp,"123");
短整型(int)
i = atoi(temp);
长整型(long)
l = atol(temp);
浮点(double)
d = atof(temp);
四、其它数据类型转换到CString
使用CString的成员函数Format来转换,例如:
整数(int)
str.Format("%d",i);
浮点数(float)
str.Format("%f",i);
字符串指针(char *)等已经被CString构造函数支持的数据类型可以直接赋值
str = username;
五、BSTR、_bstr_t与CComBSTR
CComBSTR、_bstr_t是对BSTR的封装,BSTR是指向字符串的32位指针。
char *转换到BSTR可以这样: BSTR b=_com_util::ConvertStringToBSTR("数据");///使用前需要加上头文件comutil.h
反之可以使用char *p=_com_util::ConvertBSTRToString(b);
六、VARIANT 、_variant_t 与 COleVariant
VARIANT的结构可以参考头文件VC98\Include\OAIDL.H中关于结构体tagVARIANT的定义。
对于VARIANT变量的赋值:首先给vt成员赋值,指明数据类型,再对联合结构中相同数据类型的变量赋值,举个例子:
VARIANT va;
int a=2001;
va.vt=VT_I4;///指明整型数据
va.lVal=a; ///赋值
对于不马上赋值的VARIANT,最好先用Void VariantInit(VARIANTARG FAR* pvarg);进行初始化,其本质是将vt设置为VT_EMPTY,下表我们列举vt与常用数据的对应关系:
unsigned char bVal; VT_UI1
short iVal; VT_I2
long lVal; VT_I4
float fltVal; VT_R4
double dblVal; VT_R8
VARIANT_BOOL boolVal; VT_BOOL
SCODE scode; VT_ERROR
CY cyVal; VT_CY
DATE date; VT_DATE
BSTR bstrVal; VT_BSTR
IUnknown FAR* punkVal; VT_UNKNOWN
IDispatch FAR* pdispVal; VT_DISPATCH
SAFEARRAY FAR* parray; VT_ARRAY|*
unsigned char FAR* pbVal; VT_BYREF|VT_UI1
short FAR* piVal; VT_BYREF|VT_I2
long FAR* plVal; VT_BYREF|VT_I4
float FAR* pfltVal; VT_BYREF|VT_R4
double FAR* pdblVal; VT_BYREF|VT_R8
VARIANT_BOOL FAR* pboolVal; VT_BYREF|VT_BOOL
SCODE FAR* pscode; VT_BYREF|VT_ERROR
CY FAR* pcyVal; VT_BYREF|VT_CY
DATE FAR* pdate; VT_BYREF|VT_DATE
BSTR FAR* pbstrVal; VT_BYREF|VT_BSTR
IUnknown FAR* FAR* ppunkVal; VT_BYREF|VT_UNKNOWN
IDispatch FAR* FAR* ppdispVal; VT_BYREF|VT_DISPATCH
SAFEARRAY FAR* FAR* pparray; VT_ARRAY|*
VARIANT FAR* pvarVal; VT_BYREF|VT_VARIANT
void FAR* byref; VT_BYREF
_variant_t是VARIANT的封装类,其赋值可以使用强制类型转换,其构造函数会自动处理这些数据类型。
例如:
long l=222;
ing i=100;
_variant_t lVal(l);
lVal = (long)i;
COleVariant的使用与_variant_t的方法基本一样,请参考如下例子:
COleVariant v3 = "字符串", v4 = (long)1999;
CString str =(BSTR)v3.pbstrVal;
long i = v4.lVal;
七、其它
对消息的处理中我们经常需要将WPARAM或LPARAM等32位数据(DWORD)分解成两个16位数据(WORD),例如:
LPARAM lParam;
WORD loValue = LOWORD(lParam);///取低16位
WORD hiValue = HIWORD(lParam);///取高16位
对于16位的数据(WORD)我们可以用同样的方法分解成高低两个8位数据(BYTE),例如:
WORD wValue;
BYTE loValue = LOBYTE(wValue);///取低8位
BYTE hiValue = HIBYTE(wValue);///取高8位
vc2005中属性和属性页控件值同步的问题
-----------------------------------------
我刚开始学习ActiveX开发,使用MFC。我对ActiveX控件新增一个名为"Caption",类型为int的属性.我在ActiveX的属性页上新增一个Edit control,想通过该Edit control来直接设置"Caption"属性的值.
现在问题来了:
在VC6.0中开发时可以通过class wizard的"增加成员变量"来直接将该Edit control与"Caption"属性关联.生成的代码在DoDataExchange方法中如下:
void CCircPropPage::DoDataExchange(CDataExchange* pDX)
{
//{{AFX_DATA_MAP(CCircPropPage)
DDP_Text(pDX, IDC_CAPTION, m_caption, _T("Caption") );//(1)
DDX_Text(pDX, IDC_CAPTION, m_caption);
//}}AFX_DATA_MAP
DDP_PostProcessing(pDX);
}
其中(1)行的最一个参数:_T("Caption") ,就是该Edit control所关联的属性名.
但在VC++ 2005中,却没有直接将Edit control与属性相关联的功能.在VC++ 2005中在Edit control上右击,选择"添加变量",打开"添加成员变量向导",但该向导只能添加与Edit control关联的"成员变量",无法将该Edit control 与"Caption"属性关联.VC++ 2005生成的代码如下:
void CCircPropPage::DoDataExchange(CDataExchange* pDX)
{
//{{AFX_DATA_MAP(CCircPropPage)
//DDP_Text(pDX, IDC_CAPTION, m_caption, _T("Caption") );//(1)//这是VC6生成的代码,这里留作参照
//DDX_Text(pDX, IDC_CAPTION, m_caption);//这也是VC6生成的
//}}AFX_DATA_MAP
DDP_PostProcessing(pDX); //(2)
DDX_Text(pDX, IDC_CAPTION1, m_caption1);//<--(3) VC++产生的代码.
}
其中第(3)行为VC++ 2005产生的代码.它并没有调用DDP_系列的函数,也将代码放在了AFX_DATA_MAP宏的外面、第(2)行的下面(第2行函数的功能为:结束属性值与Control的转换),也就是说VC++ 2005并没有将Edit control与属性关联的功能!??
虽然可以通过手动添加方式解决,但我总觉得VC++ 2005应该提供如此功能,不知道有没有朋友遇到过与我相同的问题,如何解决之?
难道,正如别人所说,VC2005比VC6是一个退步。
--------------------------------------------
VC2005开发ActiveX控件把属性页和属性关联
在属性页里面添加好需要的控件.比如说一个文本框,让它和控件的一个属性向关联.这件事情在VC6.0时代应该是轻而易举的,可是VC2005好像是为了支持WindowsCE等不支持属性页的移动设备把这一个省事的 ClassWizard给取消了.不过关联这件事情其实并不困难.以下给出几个约略的步骤,如果你开发过AcitveX那么会十分清楚的,如果没有那么就是我再怎么说也不清楚.
首先给你的文本框关联一个变量;
然后在生成的DataExchange函数中添加一句
DDP_Text(pDX, IDC_EDIT4, AccSel,_T("Accurate"));
第二项十文本框的标识符,AccSel是你关联的那个变量名字,"Accurata"是属性的名字.
最后这个很关键,就是把
DDP_PostProcessing(pDX);
这句从函数开头移到函数末尾,这样就可以了.关联工作到此结束.
孙鑫VC学习笔记:ActiveX 控件
容器和服务器程序
容器应用程序时可以嵌入或链接对象的应用程序。Word 就是容器应用程序。服务器应用程序是创建对象并且当对象被双击时,可以被启动的应用程序。Excel 就是服务器应用程序。ActiveX 控件不能独立运行,它必须被嵌入容器应用程序中,和容器应用程序一起运行。
---------------------------------------------------------------------------------
编写一个 ActiveX 时钟控件
1.利用 MFC ActiveX ContrlWizard 新建一个 Clock 工程
2.在新建的工程中有三个类,其中 CClockApp 从 COleControlModule 中派生出来的,
可以将其看作是应用程序类,它的一个实例表示控件程序本身。
COleControl 从 CWnd 派生得到,也是一个窗口类,
CClockCtrl 相当于单文档程序的主窗口类。
这个类包含重绘用的 OnDraw 函数,也包含了一些消息映射,包括调度映射。
Dispatch maps 调度映射,主要是 MFC 提供让外部应用程序可以访问控件的属性和方法。
Event maps 事件映射,控件向包含它的容器发送事件通知。
CClockPropPage 类由 COlePropertyPage 派生而来:
COlePropertyPage
The COlePropertyPage class is used to display the properties of a custom control in a graphical interface, similar to a dialog box. 被用来显示一个自定义控件的属性,类似于一个对话框。它是对话框类,于是 enum { IDD = IDD_PROPPAGE_CLOCK }给它关联了一个对话框资源。
在工程中,还有两个全局函数:STDAPI DllRegisterServer(void);将控件信息写入注册表中 STDAPI DllUnregisterServer(void)卸载注册信息。
3.在三个类之上,还有类似小勺的图标 Dclock,_DClockEvents,它们表示接口。
接口是外部程序和控件进行通信的协议,可以把接口看作是函数的集合,外部程序通过接口提供的方法,去访问控件的属性和方法。也可以将接口看作抽象基类,接 口中所定义的所有函数都是纯虚函数, 这些函数的实现都是在 CClockCtrl 类中实现,MFC 通过底层的封装使 CClockCtrl 类继承 Dclock 接口。所以调用接口,事实上调用的是 CClockCtrl 类中的函数。由于封装,底层的通信细节我们看不到,如果对这个感兴趣,可以看一些 COM 编程的资料。
4.如果些时编译一下工程,会生成一个 Clock.ocx 文件,它就是 ActiveX 控件的文件。
要用控件的时候,只需要把这个文件传递给对方。注意:ActiveX 控件不能单独使用,必须嵌入到一个窗口当中一直运行。当我们用 VB 添加控件时,发现了我们刚才编译的控件,但是 VB 怎么知道我们新建的控件的位置呢? 我们发现编译时,输出窗口的最后一行 “Registering ActiveX Control...”说明编译时会注册控件。事实上编译之后,VC 会调用 regsvr32 注册控件,并将信息写入到注册表中,VB 在加载 ActiveX 控件时,会从注册表中搜寻相关的 ActiveX 控件信息。注意:ActiveX 控件在使用之前都需要注册。
如果想卸载控件,可以在“运行”中输入命令:
“regsvr32 /u 控件文件完整路径名”,事实上是调用工程中全局函数 DllUnregisterServer 来完成卸载的。
如果想再次注册控件,可以在“运行”中输入下面命令:
“regsvr32 控件文件完整路径名”事实上是调用工程中全局函数 DllRegisterServer 来完成注册的。
5.下面实现在控件上输出当时系统时间。
可以在 OnDraw 函数中完成这个功能。
这样就能做出一个静态的时间控件,如果我们想使控件实时显示时间,需要添加两个消息响应函数 WM_CREATE,WM_TIMER.要使时间可以每秒更新,我们先在 CClockCtrl 类中添加 WM_CREATE 消息处理,在其响应函数 OnCreate()中设置一个计时器:SetTimer(1,1000,NULL);接着增加一个 WM_TIMER 消息响应处理,在 OnTimer 中写上 Invalidate(); 使窗口发生重绘。也可以调用 InvalidateControl()强制控件重绘自身。
6.在 VB 测试中发现,其他很多控件可以修改控件的背景色、前景色和字体颜色,而我们还不行。
ActiveX 控件有四种属性:
Stock:为每个控件提供的标准属性,如字体或颜色。
Ambient:围绕控件的环境属性——已被置入容器的属性。
这些属性不能被修改,但控件可以使用它们调整自己的属性。
Extended:这些是由容器处理的属性,一般包括大小和在屏幕上的位置。
Custom:由控件开发者添加的属性。
7.这时在 VB 测试中,我们的控件也可以修改控件的背景色和前景色了,但是设置完以后没有效果,因为还要 OnDraw 函数中自己编写代码来完成这些改变。要得到控件标准属性,要通过一些函数来完成,如COleControl::GetForeColor 得到前景色,COleControl::GetBackColor 得到背景色,不过要注意的是它们得到的是 OLE_COLOR 的颜色,还需要通过 TranslateColor 方法转换成 COLORREF。
8.控件一般都会有属性页,当我们右键点击控件时会弹出这个属性页方便对控件属性的设置,我们的控件已经有一个属性页,通过 CClockPropPage 类来实现的,显示的面容是对话框资源的内容,下面修改控件属性页:
属性页之所以可以在窗口中看得到,是因为在 ClockCtl.cpp 中的 BEGIN_PROPPAGEIDS(CClockCtrl, 1)与 END_PROPPAGEIDS(CClockCtrl)之间调用了 PROPPAGEID(CClockPropPage::guid),其中的 guid 表示全局唯一标识符,它是一个128位的整数,用来唯一地标识一个组件或者一个接口。我们可以用 PROPPAGEID 宏增加属性页。
增加颜色属性页
首先在 BEGIN_PROPPAGEIDS(CClockCtrl, 1)与END_PROPPAGEIDS(CClockCtrl)之间添加代码 PROPPAGEID(CLSID_CColorPropPage)来添加属性页。
CLSID_CColorPropPage 属性表单是控件自身提供的, 我们添加之后不用作任何处理,就可以使用,效果如下:
注意:
BEGIN_PROPPAGEIDS(CClockCtrl, 2)中的数字2表示有多少个属性页代码要显示,
如果增加了属性页数字一定要加1,不然如果没有修改或修改错误,会产生不可预料错误。
9.增加一个自定义属性也是通过 MFC ClassWizard 来完成,与第6步增加前景色与背景色的步骤相同。
1)下面增加设置时间间隔的属性,用这个属性来控件时间刷新频率:
属性添加成功以后,在_DClock 接口中增加了 Interval 属性,同时在 CClockCtrl 类中增加了一个
成员变量 m_interval 和 OnIntervalChanged()方法(当外部属性修改之后就会调用这个方法)。
而且它们都在调度映射当中:
2)在 CClockCtrl::OnIntervalChanged()中添加属性处理程序代码
10.使增加的自定义属性在属性表单中设置
在对话框资源中添加一个编辑框,再为这个编辑框关联一个变量, 注意,我们在为编辑框关联一个变量 m_updateInterval 的同时也关联了一个属性是,这样我们不需要增加代码就能把控件和自定义属性相关联。在 void CClockPropPage::DoDataExchange(CDataExchange* pDX)中会生成下面代码:
这样,我们就可以在属性页里面设置时间间隔了。
11.为控件添加一个方法:
为控件增加函数,MFC ClassWizard-->Automation-->Add Method Class Name 要选择CClockCtrl 输入函数名,之后就可以在 CClockCtrl 类中找到了。方法添加成功以后,在_DClock 接口中增加了 Hello 方法,同时在 CClockCtrl 类中增加了 Hello 方法。接下来,我们可以在 CClockCtrl 类中增加了 Hello 方法添加自己的代码就可以了。
12.为控件添加一个标准事件
我们选择 MFC ClassWizard-->ActiveX Events--->Add Event。事件添加成功以后,会在_DClockEvents 中增加一个事件 Click,DClockEvents 接口是源接口,控件将用这个接口发送通知事件,它不是控件本身实现的接口,这个接口是通过容器来实现的
13.增加一个自定义事件:当秒数为0时,发出一个 NewMinute 事件。
1)增加一个自定义事件的过程与增加一个标准事件的步骤相同,也可以这样添加在事件接口_DClockEvents 上点击右键,选择增加事件,效果一样,都会弹出 Add Event 对话框。事件添加成功以后,会在_DClockEvents 中增加一个事件NewMinute事件,同时在在 CClockCtrl 类中增加了void FireNewMinute(),也就是在控件内部可以调用 FireNewMinute()向容器发送事件通知,而这个函数内部会调用接口的 void NewMinute()向容器发出事件通知。
2)在 OnDraw()方法添加代码 if(0 == time.GetSecond())FireNewMinute();使当秒数为0时,向容器发出一个 NewMinute 事件通知。标准消息不需要另外写代码向容器发出事件通知,VC在底层代码内部实现了这个过程。
14.将我们自定义的控件属性在修改之后永久保存下来,用户打开程序之后,控件的属性都是使用原先保存的值。需要在 void CClockCtrl::DoPropExchange(CPropExchange* pPX)加入如下代码 PX_Short(pPX,"Interval",m_interval,1000);之后再在程序中 OnCreate()方法中将 SetTimer(1,1000,NULL); 修改代码为 SetTimer(1,m_interval,NULL);
15.在属性页中修改自定义控件属性的时候,通知容器做相应的调整,从而使容器属性列表中实时地显示我们对属性所作的修改。在 void CClockCtrl::OnIntervalChanged() 中加入如下代码:BoundPropertyChanged(0x1); //通知容器调度 ID 为1的属性发生了改变
16.前面都是让控件在容器设计时改变控件属性,如果希望用户在设计模式时时钟控件停止运行,而在用户模式下时间会跳动,在控件内部可以通过 AmbientUserMode()方法得到当前控件是处于设计状态还是运行状态。在 void CClockCtrl::OnTimer(UINT nIDEvent)下修改代码如下:
if(AmbientUserMode()) InvalidateControl();
17.编写完控件以后,我们可以选择 Win32 Release 方式进行编译,生成发行版 ActiveX 控件。在开发的时候通常是以 Win32 Debug 模式下编译的,这种模式下开发有助于我们开发过程的产生的错误,例如非法内存访问错误;还可以帮助我们调试程序,跟踪程序进而排查错误。但是在调试模式下 生成的文件比较大,因为在文件中包含了调试的信息。
而当我们开发完成后,在 Release 模式下进行编译时,VC 编译器会在代码生成上、执行速度上做一些优化,同时生成的可执行程序或控件文件会比较小。
-------------------------------------------------------------------
在 VC 中编写一个客户端调用 ActiveX 控件:
1.新建一个基于 MFC 对话框的 ClockTest 工程项目
2.点击右键,选择“插入 ActiveX 控件”,然后在弹出的对话框中选择刚才我们创建的控件。
也可以通过菜单的方式增加控件:
“工程”->增加到工程->Componets and Controls
选择已注册控件"Registered ActiveX Contrlos",找到我们自己控件,再按下插入。
通过这种方式插入 ActiveX 控件时,会在工程中自动生成一个类 CCock,其基类为 CWnd。这个类是一个封装类,封装了对 ActiveX 控件进行访问的一些操作。同时在 VC 的工具箱上面也增加一个时钟控件,可以直接将一个时钟控件拖放到窗体上。
3.用第二种方法插入的控件,除了将控件手动插入到窗体以外,我们也可以通过代码动态生成一个控件。
1)CClockTestDlg 增加一个成员变量:CClock m_clock;
2)在 CClockTestDlg 的头文件中包含一个 clock.h
3)接下来就要以在一个按钮的单击事件中增加创建控件的代码:
4)在设计时,可以点击右键为控件添加事件响应。
结婚
可笑的是,两个在一起快十年的人,一旦领了那个红本本,竟然说着从此要转换心态,以后就是夫妻的身份了,要学着适应。 我真的觉得很好笑,但是后来回想起他们经历的一些事,也许真如他们如说,真正成了家的人,心态从此就会不一样吧。
回到电脑前,翻出大学时爱听的孙燕姿和Michael Learns to Rock 的专辑,听着那些老情歌,感叹光阴和时间,我们真的已经不是青涩的少年。
很想联系到自己,为自己结婚大事做个计划,但是,却怎么也静不下心。
大概我还没有到那个时候吧。