STK与VC++联合编程实战(第二回:STK服务准备&初始化)

STK与VC++联合编程实战(第二回:STK服务准备&初始化),第1张

仍以示例项目“Events”为例,首先从MFC标准头文件stdafx.h开始。


stdafx.h

主要全局包含文件如下图:

首先可以看出STK是基于COM编程的(#include ),注意上图的下部是STK对外提供的接口信息,定义这些接口信息的文件在第一回(补充链接……)中提到的外部Include文件夹(CppIncludes)中,由STK默认安装的实例代码提供。


该文件夹中有33个文件(其中.tlb文件13个,.tlh文件13个,.tli文件4个,.dll文件3个),如下图:

各文件的意义请自行参考COM编程中有关IDL文件的内容,其中*.tlb为IDL文件编译后的库文件,可用于其他编程语言;*.tlh为头文件;*.tli为c/cpp源代码。


其中agstkobjects.tlh文件有79520行!!!,这些文件应该都是MFC的IDL文件编译器自动生成的,在后续如果涉及到再具体了解。


Events.h/Events.cpp

头文件Events.h中规中矩,标准MFC框架生成的MFC应用类(CWinApp)。


在Events.cpp中,包含了三个STK的.tli(C++源代码)文件,这个引用非常重要,相当于把IDL(接口)有关的方法定义直接嵌入了项目代码,因为c/c++需要事先对函数声明,如果没有这个引用,则在项目生成时会报告链接错误。


如下图:

         

在应用初始化(InitInstance())中,重点代码如下图:

其中第48行:AfxOleInit(),根据微软文档,是初始化OLE对应用的支持,大概就是初始化(必要时加载)OLE系统的DLLs,并检测版本是否正确。


OLE是MFC更高层的COM技术,关于这些技术细节,暂时不必了解,先知道如何用即可。


第62行:AfxEnableControlContainer(),与AfxOleInit()一起使用,在MFC在线帮助中此两个函数是放在同一个条目(OLE Initialization)下的,作用是执行初始化使得应用支持OLE控件容器(原文:Call this funciton in your application object’s InitInstance function to enable containment of OLE controls)。


第54~60行是执行STK License检测,不过根据代码看m_pApp是局部变量,这个License检测应该不是应用运行所必须的,有待后面验证。


经验证,注释掉License检测代码,程序仍然可以正常运行,不过在新建场景的 *** 作中,代码再次执行了License检测。


Events.cpp,第55行:IAgSTKXApplicationPtr   m_pApp;

IAgSTKXApplicationPtr是一个智能指针,在文件stkx.tlh中定义(第96行),如下:

_COM_SMARTPTR_TYPEDEF(IAgSTKXApplication, __uuidof(IAgSTKXApplication));

Events.cpp第56行的方法‘CreateInstance()’是智能指针(CComPtr)的方法,参见微软在线文档帮助,条目‘_com_ptr_t::CreateInstance’,解释为:为给定的CLSID或ProgID创建对象实例,此处的ProgID为‘AgSTKXApplication’,在文件stkx.tlh中(第32~33行)定义。


如下:

struct __declspec(uuid("98ac4db1-6e59-4fad-b03d-54a67f1cf350"))

/* dual interface */ IAgSTKXApplication;

根据申明判断是一个dual接口,既支持IUnknown调用,也支持IDispatch调用。


ObjectModelEventSink.h/ ObjectModelEventSink.cpp

COM规范使用事件和回调机制来处理COM对象(服务端)和调用者(客户端)之间的事件响应和处理,即COM规范中的连接点机制。


如果一个COM对象可以在执行执行完 *** 作之后,发送一个事件(即一个回调函数),那么这个对象被称为可连接对象(Connectable Object)。


每一个事件,通过接口来回调,这个接口称为回调接口(Outgoing Interface),调用者来实现这个接口。


那么,如果可连接对象能够调用某一个回调接口,那么它就要为这个接口定义一个连接点对象(Connection Point Object),调用者实现这个回调接口,其具体的实现类型称为Sink(槽,类似于QT中的信号/槽机制)。


STK所给出的本实例中的CObjectModelEventSink类就是一个Sink类,继承于CCmdTarget,类CCmdTarget实现了IDispatch接口,所以这个自定义的Sink类也是符合的COM对象标准。


在ObjectModelEventSink.h中,回调函数均以On*打头。


ObjectModelEventSink.cpp

在类CObjectModelEventSink的构造函数中执行了方法:EnableAutomation()


该方法使得Sink类能够接收从STK COM组件对象传递的事件,这主要通过该文件后续的几个宏定义实现,代码如下:

BEGIN_DISPATCH_MAP(CObjectModelEventSink, CCmdTarget)

DISP_FUNCTION_ID(CObjectModelEventSink, …)

END_DISPATCH_MAP()

BEGIN_INTERFACE_MAP(CObjectModelEventSink, CCmdTarget)

         INTERFACE_PART(CObjectModelEventSink, __uuidof(STKObjects::IAgStkObjectRootEvents), Dispatch)

END_INTERFACE_MAP()

ObjectModelHelper.h/ ObjectModelHelper.cpp

Helper类是辅助我们调用STK COM对象方法(接口)的中间类,Helper类的方法也可以在应用的任何地方直接调用(可不必经过Helper类),借助一个Helper类可以使得程序结构更加清晰。


例如,在Helper类中新建场景的代码如下:

void CObjectModelHelper::NewScenario(STKObjects::IAgStkObjectRootPtr pRoot)

{

    ASSERT(pRoot != NULL);

    pRoot->NewScenario("Demo");

}

实际上只要我们拥有pRoot对象,可以在任何地方执行“pRoot->NewScenario("Demo");”。


EventsDlg.h/EventsDlg.cpp

InitObjectModel()

重要初始化:InitObjectModel(),其中一个诡异的用法,m_pRoot看变量名应该是一个指针,确实是一个指针,而且是智能指针,参见.h文件中的定义(IAgStkObjectRootPtr m_pRoot;)但在第一个初始化 *** 作中执行的是点 *** 作(.),如下:

HRESULT hr = m_pRoot.CreateInstance(__uuidof(AgStkObjectRoot));

看来COM真的是博大精深呐,得分情况,如果是IAgStkObjectRoot(接口)类自己的方法,则需要用指针 *** 作符(->)。


俺不知其所以然,跟着学,会用就是了!不求甚解,一时也理解不了。


然后是获取Sink类的IDispatch接口,如下:

LPUNKNOWN pUnkSink = m_pSink->GetIDispatch(FALSE);

最后,建立STK COM服务与客户端(即本应用)定制的Sink之间的连接点,代码如下:

AfxConnectionAdvise(m_pRoot, __uuidof(STKObjects::IAgStkObjectRootEvents),

        pUnkSink, FALSE, &m_dwCookie);

事件回调接口IAgStkObjectRootEvents定义在文件agstkobjects.tlh中(自4496行开始),共定义了22个事件回调接口。


TerminateObjectModel()

断开源(source)和槽(Sink)之间的连接。


AfxConnectionUnadvise()方法,m_pRoot用的依然是点 *** 作(.),神奇!

OnInitDialog()

初始化随机数发生器:srand(),看来STK算法内部依赖于随机数。


后经代码分析,是在添加卫星对象的时候,生成卫星轨道参数时用到了随机数,保证每次加入的卫星轨道都是随机的。


调用InitObjectModel(),完成与STK COM对象之间的连接点建立。


关于二三维视窗显式与场景的关系

示例对话框界面的那个三维视窗控件,查了半天没有找到其与STK当前场景的关系,猜测STK应用会自动搜索本进程空间中的视窗控件,包括二维和三维视窗,然后内部建立联系进行显示控制和更新,为此,进行如下简单测试。


默认模式打开项目资源文件中的应用对话框(IDD_EVENTS_DIALOG),调整界面,并通过拷贝和复制新建三个三维视窗对象,除了调整一下未知,其他什么也不做,调整后界面如下图:

 运行程序,新建场景,并向场景加入对象,果然如预期,四个窗口都能显示,并且能独立通过鼠标 *** 作控制视窗显示缩放、调整视角等,运行效果如下图:

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/562724.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-02
下一篇 2022-04-02

发表评论

登录后才能评论

评论列表(0条)

保存