GeoMedia是美国Intergraph公司开发的桌面GIS平台,至于GeoMedia的介绍我在这里不多说,可以到Intergraph的网站www.intergraph.com/geomedia/或者通过Google了解更多详细情况。在这里所提到的Geomedia均指GeoMedia Professional 4.0 或 5.0。
使用GeoMedia开发GIS应用程序,需要了解一些GeoMedia平台中的一些术语和概念,请参见。在GeoMedia的帮助文档GeoMedia Professional Object Reference中可以找到很多VB例子,在这里我主要介绍用VC++6.0如何开发基于GeoMedia的应用程序。
为了让大家有个初步的概念和便于理解的环境,我先不介绍一些平台相关的术语和特性,而是直接描述如何用VC++6创建一个嵌入GeoMedia平台中运行的自定义工具程序。
为了你在下面的过程中不碰到什么不必要的麻烦,请先安装好VC++6.0英文版+SP6补丁。
好,开始进入正题。
第一步:
打开VC6,创建工程。
工程类型为ATL COM AppWizard,我在这里将工程名字取为TestCmd。Next到下一步设置我们创建的ATL COM组件类型为Dynamic Link Library (DLL),并且选择MFC支持(这是为了使用大家比较熟悉的界面控制方式,避免对本主题内容理解上的障碍,如果你对ATL/WTL比较熟悉,那么完全可以不选择MFC的支持)。如下图:
第二步:
工程创建好了之后,我们接下来要做的第一件事就是创建一个ATL COM对象,选择菜单Insert -> New ATL Object,在ATL Ojbect Wizard对话框中选择Objects,类型为Simple Object。在Wizard的下一步中,我们需要设置ATL Object的名字,我这里设置为TestCmdCom。在attributes标签页中设置线程模型(Threading Model)选择为单线程(Simgle), 接口(Interface)设置为双接口(Dual),也就是支持IDispatch和vtable绑定。 Aggregation设置为NO,也就是不需要聚合支持。另外勾选Support ISupportErrorInfo,以实现ISupportErrorInfo接口,其他的不需要选择。如下图:
第三步:
在IDE自动生成的TestCmdCom.h文件中加入如下接口声明:
接口声明
// ITestCmdCom
public:
STDMETHOD(Help)(/*[out, retval]*/ VARIANT_BOOL * pvbVal);
STDMETHOD(Terminate)();
STDMETHOD(RestoreEvents)(/*[in]*/ IDispatch* objViewWindow);
STDMETHOD(IgnoreEvents)(/*[in]*/ IDispatch* objViewWindow);
STDMETHOD(RemoveView)(/*[in]*/ IDispatch* objViewWindow);
STDMETHOD(AddView)(/*[in]*/ IDispatch* objViewWindow);
STDMETHOD(Deactivate)();
STDMETHOD(CanDeactivate)(/*[in]*/ IDispatch* objViewWindow, /*[out, retval]*/ VARIANT_BOOL* pbCanDeactivate);
STDMETHOD(Activate)();
STDMETHOD(CanActivate)(/*[out, retval]*/ VARIANT_BOOL* pbCanActivate);
STDMETHOD(CanEnable)(/*[out, retval]*/ VARIANT_BOOL* pbCanEnable);
STDMETHOD(Initialize)(/*[in]*/ IDispatch* objApp, /*[in]*/ IDispatch* objViewListeners);
STDMETHOD(get_IsDone)(/*[out, retval]*/ VARIANT_BOOL *pVal);
private:
VARIANT_BOOL m_bIsDone;
GeoMedia::IGMApplicationPtr m_spApplication;
在自动生成的TestCmdCom.cpp文件中加入如下接口定义:
接口定义
STDMETHODIMP CTestCmdCom::get_IsDone(VARIANT_BOOL * pvbVal)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
// TODO: Add your implementation code here
*pvbVal = m_bIsDone;
return S_OK;
}
// This method is called once immediately after the command server object is created.
// This method performs any necessary runtime initialization. Command servers use the
// objApp argument to access the application's automation model. The Application object
// (objApp) is passed to this method from the Command Manager.
STDMETHODIMP CTestCmdCom::Initialize(IDispatch * pobjApplication, IDispatch * pobjViewListeners)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
// Save the pointer to the application object
m_spApplication = pobjApplication;
m_bIsDone = VARIANT_TRUE;
return S_OK;
}
// this method is only called for those commands that specified that they had custom
// enabling conditions.
STDMETHODIMP CTestCmdCom::CanEnable(VARIANT_BOOL * pvbVal)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
// TODO: Add your implementation code here
*pvbVal = VARIANT_TRUE;
return S_OK;
}
// This method is only called for those Map View Listener commands with custom listener
// requirements. These requirements are for sharing access to the Map View
STDMETHODIMP CTestCmdCom::CanActivate(VARIANT_BOOL * pvbVal)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
// TODO: Add your implementation code here
*pvbVal = VARIANT_TRUE;
return S_OK;
}
// this method is called to start the command. A modal command should show its main
// form or dialog box. Modaless commands should set their IsDone property to FALSE
// and connect to their views (if they are also view listeners).
STDMETHODIMP CTestCmdCom::Activate()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
// Create or show main window
CMainDlg mainDlg;
mainDlg.SetGMApp(m_spApplication);
mainDlg.DoModal();
m_bIsDone = VARIANT_FALSE;
return S_OK;
}
// This method is called to determine if a modeless command is ready to deactivate.
// Usually this method invokes a dialog box prompting the user to decide whether to end
// the command (and lose uncommitted work) or prevent initialization of a confliating
// command.
STDMETHODIMP CTestCmdCom::CanDeactivate(IDispatch * pobjViewWindow, VARIANT_BOOL * pvbVal)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
// TODO: Add your implementation code here
*pvbVal = VARIANT_TRUE;
return S_OK;
}
// This methodis caled to inactivate the command. It must disconnect the command from all
// views, remove any modeless windows that it owns, set its IsDone property to TRUE, and return.
STDMETHODIMP CTestCmdCom::Deactivate()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
m_bIsDone = VARIANT_TRUE;
return S_OK;
}
STDMETHODIMP CTestCmdCom::AddView(IDispatch * objViewWindow)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
return S_OK;
}
STDMETHODIMP CTestCmdCom::RemoveView(IDispatch * pobjViewWindow)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
Deactivate();
return S_OK;
}
STDMETHODIMP CTestCmdCom::IgnoreEvents(IDispatch * pobjViewWindow)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
return S_OK;
}
STDMETHODIMP CTestCmdCom::RestoreEvents(IDispatch * pobjViewWindow)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
return S_OK;
}
// This method is called when the command server object is about to be released.
// Typically, it releases object variables and unloads forms if applicable
STDMETHODIMP CTestCmdCom::Terminate()
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
// Release the application object
m_spApplication.Release();
return S_OK;
}
STDMETHODIMP CTestCmdCom::Help(VARIANT_BOOL * pvbVal)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState())
//*pvbVal = VARIANT_TRUE;
return S_OK;
}
第四步:
在TestCmd.idl文件中的
interface ITestCmdCom : IDispatch
{
};
中加入接口描述,完成后的格式如下:
接口描述
interface ITestCmdCom : IDispatch
{
[propget, id(1), helpstring("property IsDone")] HRESULT IsDone([out, retval] VARIANT_BOOL *pVal);
[id(2), helpstring("method Initialize")] HRESULT Initialize([in] IDispatch* objApp, [in] IDispatch* objViewListeners);
[id(3), helpstring("method CanEnable")] HRESULT CanEnable([out, retval] VARIANT_BOOL* pbCanEnable);
[id(4), helpstring("method CanActivate")] HRESULT CanActivate([out, retval] VARIANT_BOOL* pbCanActivate);
[id(5), helpstring("method Activate")] HRESULT Activate();
[id(6), helpstring("method CanDeactivate")] HRESULT CanDeactivate([in] IDispatch* objViewWindow, [out, retval] VARIANT_BOOL* pbCanDeactivate);
[id(7), helpstring("method Deactivate")] HRESULT Deactivate();
[id(8), helpstring("method AddView")] HRESULT AddView([in] IDispatch* objViewWindow);
[id(9), helpstring("method RemoveView")] HRESULT RemoveView([in] IDispatch* objViewWindow);
[id(10), helpstring("method IgnoreEvents")] HRESULT IgnoreEvents([in] IDispatch* objViewWindow);
[id(11), helpstring("method RestoreEvents")] HRESULT RestoreEvents([in] IDispatch* objViewWindow);
[id(12), helpstring("method Terminate")] HRESULT Terminate();
[id(13), helpstring("method Help")] HRESULT Help([out, retval] VARIANT_BOOL * pvbVal);
};
第五步:创建界面
在这一篇里,我们实现一个简单的功能:枚举当前的所有连接并显示在一个ComboBox中,先在工程中添加一个对话框(我在这里命名为CMainDlg),然后在对话框中添加一个ComboBox和一个Button,在Button的事件中我们添加如下代码:
OnButton1
void CMainDlg::OnButton1()
{
m_cboConns.ResetContent();
CComQIPtr<GeoMedia::IGMApplication> ptrApp(m_dispGMApplication);
IDispatchPtr pdispDocument;
ptrApp->get_Document(&pdispDocument);
CComQIPtr<GeoMedia::IGMDocument> ptrDocument(pdispDocument);
IDispatchPtr pdispConns;
ptrDocument->get_Connections(&pdispConns);
CComQIPtr<PClient::DGMConnections> ptrConns(pdispConns);
CComBSTR bstrConnName;
PClient::DGMConnectionPtr ptrConn;
long lConnCount = 0;
ptrConns->get_Count(&lConnCount);
for (long i = 1; i <= lConnCount; i++)
{
ptrConns->Item(CComVariant(i), &ptrConn);
if (ptrConn == 0) continue;
VARIANT_BOOL bUpdatable = VARIANT_FALSE;
ptrConn->get_Updatable(&bUpdatable);
if(bUpdatable == VARIANT_FALSE) continue;
ptrConn->get_ConnectionName(&bstrConnName);
CString strConnName(bstrConnName.m_str);
m_cboConns.InsertString(-1, strConnName);
}
}
完成这些后,我们需要在CTestCmdCom的Activate方法中启动该对话框,代码如下:
CMainDlg mainDlg;
mainDlg.SetGMApp(m_spApplication);
mainDlg.DoModal();
第六步:插件程序的注册安装
我们需要使用GeoMedia Pro自带的工具installusrcmd来注册我们生成的程序,使用这个工具需要我们创建一个ini文件来描述注册信息,格式如下:
[TestCmdCom]
ProgID=TestCmd.TestCmdCom
CommandType=0
EnableMask=43008
ListenerMask=0
IsModal=1
Description=GM Tool Test
ToolTip=GM Tool Test
SmallMonoBitmapPath=
SmallColorBitmapPath=
LargeMonoBitmapPath=
LargeColorBitmapPath=
其中EnableMask的值是指什么状态下,该控件可以被使用(Enable),这个值可以用GeoMedia自带VB程序创建想到来生成;IsModal值为1表明我们的界面是模态对话框。
然后,用一下批处理来注册我们写好的插件:
regsvr32 TestCmd.dll /s
"D:\Program Files\GeoMedia Professional\Program\installusrcmd" /prod "GeoMedia Professional" TestCmd.dll TestCmd.ini
注册后,我们就可以在GeoMedia中的自定义工具中选择使用该工具了。
最后补充一句,发布程序的时候,使用MinDependency方式,这样就可以避免在没有安装VC6的机器上不能启动该插件工具的问题。
该文章中代码都可以在这里下载得到:TestCmd VC6 Project