银弹制造:开发最灵活的软件(一)

类别:VC语言 点击:0 评论:0 推荐:

银弹制造:开发最灵活的软件(一)

在非托管代码中使用托管资源是如何实现的

BrianLiang (原创)

[email protected]

 

   我一直在寻找这样一个类库:他对Windows的窗口提供面向对象的封装,有灵活的消息响应机制和比较完备的界面框架解决方案,对标准控件提供简练实用的封装,支持操作系统的新特性,支持功能扩充和二次开发,有代码自动生成向导机制,生成的程序使用较少的系统资源,最后是有完全的代码支持和文档支持;对!用MFC;我希望他能对纷繁芜杂的世界有所感应,能让我充分利用到各种开发语言的优点而不为一种环境所困扰;没错!用.NET;我想我可以恰当的使用它们,让他们在我的项目中各尽所能,我纵横于各种开发语言和对象模型之间,而不用太过需要去顾及到他们的差异;很幸运,这样的工具也被我发现了;我使用MFC Application Project with .NET Support 1.0,我不费吹灰之力把CrystalReportView等成熟的.NET组件插入到我的MFC项目中,我用VB.NET和C#完成一些琐碎的工作(我的朋友正在开始学习ADO.NET,他很乐意帮助我完成数据管理部分),而关键部分仍然交给C++来完成。我有点好奇,我不知道作者是怎么做到的!我冒昧的与作者联系,希望能够了解地更多;然后我获得了全部的源代码,同时作者希望我能够把这项技术介绍给大家,他希望能够帮助广大的开发人员自由地整合各种资源,他相信整个技术行业应该是开放性的、具有合作意识的而且是自由自在不受约束的,他认为开发工具能够提供给我们的应该更多,有些必要因素必须是天生的比如资源整合能力、无限的可扩展性、表现层和业务逻辑层的独立以及更重要的是用户对界面和功能的自主与自由的选择等等;开发工具必须能够尽量保证这一切都是可行的、自动的、快速的、简单的。他给我展示了他所完成的开发工具是如何达到以上要求的,在今后的一两个月内他们就会与广大的开发人员见面,这一切都令我震惊。这一系列的工具为我们的开发人员提供更完美的解决方案。这些产品作为构架工具和组件容器,会带来超乎想象的组件重用率和开发者生产率的提高进而带来的软件质量的提高(构架师专注于功能定义和任务调度,生活在地球上各个角落里的组件开发人员维护自己的组件和文档甚至后续的技术支持)。整个软件产业发展的必然之路就在眼前!

 

构件软件最可能的彻底解决方案是:不开发任何软件!

                                    ------------弗雷德里克·布鲁克斯

 

一、我们需要整合,我们需要互操作!

通过允许MFC项目属性的“Use Managed Extensions(使用托管扩展)”,我们就可以在项目中增加诸如.NET Framework Windows 窗体控件或UserControl等等托管项;

从 Visual C++ .NET 开始,MFC 对消息处理函数的返回类型和参数类型进行更严格的类型检查。这些新增行为通过使用错误信息标记潜在不安全的消息处理函数,来通知开发人员可能会遇到的问题。大家会注意到在这里所有返回类型用 LRESULT 保证类型安全。

源程序中一个重要的类是WFControlSite,他继承自COleControlSite (COleControlSite 提供客户端的控制接口,更多请参考看MSDN);第一步,使用CreateManagedControl方法创建托管控件(窗体、UserControl)把它作为客户端对象激活(客户端激活的对象由基于租约的生存期管理器管理,该管理器确保在对象的租约过期时对该对象进行垃圾回收);以下代码演示了如何获得托管资源:

HRESULT hr = E_FAIL;

ASSERT(m_pObject == NULL);

CWinFormsView::CWFControlWrapper * pWndCtrlLocal = dynamic_cast<CWinFormsView::CWFControlWrapper *>(m_pWndCtrl);

IUnknown * pUnk = pWndCtrlLocal->GetManagedControl();

    if (FAILED(hr = pUnk->QueryInterface(IID_IOleObject, (void**)&m_pObject))) {

         return hr;

     }

GetEventIID(&m_iidEvents);

//激活客户端对象部分略

上述代码中的CWFControlWrapper是另外一个重要的类CWinFormsView(从使用MFC Application Project with .NET Support 1.0 为我们的Visual Studio.NET 2003的VC++项目模板中添加的”MFC Project with .NET Support”向导生成任何一个应用程序的”UserControlView.cpp”里的映射可以看到他们的对应关系)包含的一个子类,类定义如下:

class  CWFControlWrapper : public CWnd

     {

         public:

              CWFControlWrapper();

              virtual ~CWFControlWrapper(void);

              IUnknown * pUnkControl ;

              IUnknown * GetManagedControl() { return pUnkControl; }

              void  SetControlSite(COleControlSite * pSite) { m_pCtrlSite = pSite; }

     };
CWinFormsView::CWFControlWrapper::CWFControlWrapper():pUnkControl(NULL){}
//析构函数略

 

我们必须重写 CreateControl方法以调用CreateManagedControl方法;在这段方法实现中,如果hr = CreateManagedControl(clsid, pPersist, bStorage, bstrLicKey)成功完成,创建托管资源窗口/UserControl,然后,作为OLE就地激活对象,它需要被激活;

插曲:

COLEControlSite对象是一个OLE容器文档对象,在这个容器文档中可以任意创建托管项。开发人员在容器文档中创建托管项后会频繁激活这个对象,以便对其进行编辑。

 

借助在CWFControlWrapper中的方法SetControlSite使得CWnd的保护成员m_pCtrlSite得以获得新的托管资源:

部分代码如下:

CWinFormsView::CWFControlWrapper * pWndCtrlLocal = dynamic_cast<CWinFormsView::CWFControlWrapper *>(pWndCtrl);

m_pWndCtrl = pWndCtrlLocal;

pWndCtrlLocal->SetControlSite(this);

接下来的问题是,如何将 Windows 窗体控件用作 MFC 视图类。 首先自然要在CWinFormView类中创建CWFControlSite对象,如果创建成功,就把它画在客户区,代码实现如下:

UserControl*  pUserControl = __try_cast< UserControl* >( control );

m_Control.pUnkControl = reinterpret_cast<IUnknown*>
(System::Runtime::InteropServices::Marshal::GetIUnknownForObject(control).ToPointer());

OnInitialUserControl();
CRectclientRect;
GetClientRect( &clientRect );

使用过MFC Application Project with .NET Support 1.0的程序员都会发现在UserControlView中可以使用OnInitialUserControl来初始化非托管资源,以下代码取自官方网站提供的例子example003.zip里的TestAppView.cpp(不要被这个类名所迷惑,他其实就是UserControlView,这个例子的作者并没有用向导生成UserContrlView,而是自己增加了一个):

m_pCtrl->Button2->set_Text(S"VB.NET usercontrol-button changed by c++ code.");

m_pCtrl->Button2->add_Click(new system::EventHandler((CTest*)m_pTestObj,CTest::ClickButton2));

m_pCtrl->TabPage1->set_Text(S"VB.NET usercontrol-TabPage title changed by c++ code.");

m_pCtrl->Button1->add_Click(new System::EventHandler((CTest*)m_pTestObj,CTest::Click));

m_pCtrl->add_Click(new System::EventHandler((CTest*)m_pTestObj,CTest::Click));

//Handle events of .NET usercontrol
m_pCtrl->add_xxx(new testcls::UserControl1::xxxEventHandler((CTest*)m_pTestObj, CTest::Fire));

可以看到不管是托管的控件的属性,还是方法甚至是事件,都可以使用非托管代码控制、作出响应。如何保证这一点,本文作者由于对MFC及.NET对象模型掌握程度有限,无法给出更详细的描述,请大家见谅。

MFC Application Project with .NET Support 1.0官方网站:

http://www.apptemplate.com/

下载地址:

http://www.apptemplate.com/GB/downloads/

Brian Liang 2004-04

本文地址:http://com.8s8s.com/it/it976.htm