將GMap封裝為Activex供QT使用(工具:VS2017,QT5.12) 更新部署到其它電腦


由於一開始定的開發平台在QT下,到后面要加入地圖控件.qt里本身帶有地圖qmap(在qt的官方案例中可以找到,用qml做的),但只有固定的幾個地圖源,要做google或者bing地圖,時間和人力不允許,而且對地圖也不是很熟悉..就決定先用gmap.

思路:

   gmap 是用c#開發的,像QT這種C++語言類無法直接使用,固將gmap 以com組件的形式封裝成控件,供c++使用;

1.以管理員模式身份打開vs2017-文件-新建-項目-創建 Windows窗體控件庫

 

框架可根據情況自行選擇;   確定;

2.這時候就出現控件窗體了,可以更一下文件名

 

3. 工程屬性設置    勾選 使程序集COM可見 ,勾選 為COM互操作可見 (其作用在於在程序編譯時,vs會自動將dll注冊,如不是管理員身體啟動vs,勾選它在編譯時會報無權限錯誤),  添加程序集簽名,后面注冊時會用到

  

 

 

 

 

 

 

4.添加gmap引用,我這里使用 NuGet 來添加....項目---管理NuGet程序包    瀏覽 搜索 gmap  點擊 安裝   

 

5.安裝完后 添加gmap控件到 控件窗體 

  在工具箱 找到GmapControl 拖到控件窗體

  如果找不到 GmapControl  控件,, 需手動添加gmapcontrol控件 

       在工具箱空白處右鍵 打開 選擇項    瀏覽到本項目解決方案下,有個packages 文件名,就是gmap的引用包,,瀏覽找到 GMap.NET.WindowsForms.dll 添加 即可.....這時工具箱內就會有GmapControl了

 

 

 

6.將 GmapControl 添加到窗體后,默認名為 gMapControl1  可自行更改 ..... 在  gMapControl1 控件上雙擊  添加以下代碼 

需添加引用

using GMap.NET.MapProviders;
using GMap.NET;

  

this.gMapControl1.CacheLocation = System.Windows.Forms.Application.StartupPath;//緩存文件路徑
            this.gMapControl1.MapProvider = GMapProviders.BingHybridMap;
            this.gMapControl1.Manager.Mode = AccessMode.ServerAndCache;//地圖模式為本地緩存,完全離線

            this.gMapControl1.ShowCenter = true;//隱藏或顯示中心十字
            this.gMapControl1.MinZoom = 1;
            this.gMapControl1.MaxZoom = 23;
            this.gMapControl1.Zoom = 12;
            //this.gMapControl.ShowTileGridLines = false;//道路網格線
            this.gMapControl1.DragButton = System.Windows.Forms.MouseButtons.Left;//左健拖動地圖
            this.gMapControl1.MouseWheelZoomType = MouseWheelZoomType.MousePositionWithoutCenter;//不以中心點縮放
            this.gMapControl1.Position = new PointLatLng(34.2313041, 108.8774211);

  

7,添加一個新的接口 IObjectSafety.cs

 

   代碼如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices; //需手動添加的

namespace TestLibrary
{
    //以下代碼可完全復制過去,不做任何改動,Guid 也不需要更改
    [ComImport, Guid("1D9AD540-F2C9-4368-8697-C4AAFCCE9C55")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IObjectSafety
    {
        [PreserveSig]
        int GetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] ref int pdwSupportedOptions, [MarshalAs(UnmanagedType.U4)] ref int pdwEnabledOptions);

        [PreserveSig()]
        int SetInterfaceSafetyOptions(ref Guid riid, [MarshalAs(UnmanagedType.U4)] int dwOptionSetMask, [MarshalAs(UnmanagedType.U4)] int dwEnabledOptions);

    }
}

 

在AssemblyInfo.cs 文件中 添加  using System.Security;  和  [assembly: AllowPartiallyTrustedCallers()]

 

8,返回到  MapLibrary.cs , 給MapLibrary添加父類 IObjectSafety ,,,添加Guid (在工具--創建Guid   這個小工具里生成)

 並添加代碼

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using GMap.NET.MapProviders;
using GMap.NET;
using System.Runtime.InteropServices;

namespace TestLibrary
{
    [Guid("0E32CE01-4D40-45A6-8B59-B39ABFE8ADB0")]
    public partial class MapLibrary: UserControl, IObjectSafety //接口類的引用
    {
        public MapLibrary()
        {
            InitializeComponent();
        }

        private void gMapControl1_Load(object sender, EventArgs e)
        {

            this.gMapControl1.CacheLocation = System.Windows.Forms.Application.StartupPath;//緩存文件路徑
            this.gMapControl1.MapProvider = GMapProviders.BingHybridMap;
            this.gMapControl1.Manager.Mode = AccessMode.ServerAndCache;//地圖模式為本地緩存,完全離線

            this.gMapControl1.ShowCenter = true;//隱藏或顯示中心十字
            this.gMapControl1.MinZoom = 1;
            this.gMapControl1.MaxZoom = 23;
            this.gMapControl1.Zoom = 12;
            //this.gMapControl.ShowTileGridLines = false;//道路網格線
            this.gMapControl1.DragButton = System.Windows.Forms.MouseButtons.Left;//左健拖動地圖
            this.gMapControl1.MouseWheelZoomType = MouseWheelZoomType.MousePositionWithoutCenter;//不以中心點縮放
            this.gMapControl1.Position = new PointLatLng(34.2313041, 108.8774211);
        }

        #region IObjectSafety //以下 到 #endregion 為添加的代碼,不需要做任何修改
        private const string _IID_IDispatch = "{00020400-0000-0000-C000-000000000046}";
        private const string _IID_IDispatchEx = "{a6ef9860-c720-11d0-9337-00a0c90dcaa9}";
        private const string _IID_IPersistStorage = "{0000010A-0000-0000-C000-000000000046}";
        private const string _IID_IPersistStream = "{00000109-0000-0000-C000-000000000046}";
        private const string _IID_IPersistPropertyBag = "{37D84F60-42CB-11CE-8135-00AA004BB851}";

        private const int INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001;
        private const int INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002;
        private const int S_OK = 0;
        private const int E_FAIL = unchecked((int)0x80004005);
        private const int E_NOINTERFACE = unchecked((int)0x80004002);

        private bool _fSafeForScripting = true;
        private bool _fSafeForInitializing = true;

        public int GetInterfaceSafetyOptions(ref Guid riid, ref int pdwSupportedOptions, ref int pdwEnabledOptions)
        {
            int Rslt = E_FAIL;

            string strGUID = riid.ToString("B");
            pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
            switch (strGUID)
            {
                case _IID_IDispatch:
                case _IID_IDispatchEx:
                    Rslt = S_OK;
                    pdwEnabledOptions = 0;
                    if (_fSafeForScripting == true)
                        pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
                    break;
                case _IID_IPersistStorage:
                case _IID_IPersistStream:
                case _IID_IPersistPropertyBag:
                    Rslt = S_OK;
                    pdwEnabledOptions = 0;
                    if (_fSafeForInitializing == true)
                        pdwEnabledOptions = INTERFACESAFE_FOR_UNTRUSTED_DATA;
                    break;
                default:
                    Rslt = E_NOINTERFACE;
                    break;
            }

            return Rslt;
        }

        public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions)
        {
            int Rslt = E_FAIL;
            string strGUID = riid.ToString("B");
            switch (strGUID)
            {
                case _IID_IDispatch:
                case _IID_IDispatchEx:
                    if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_CALLER) && (_fSafeForScripting == true))
                        Rslt = S_OK;
                    break;
                case _IID_IPersistStorage:
                case _IID_IPersistStream:
                case _IID_IPersistPropertyBag:
                    if (((dwEnabledOptions & dwOptionSetMask) == INTERFACESAFE_FOR_UNTRUSTED_DATA) && (_fSafeForInitializing == true))
                        Rslt = S_OK;
                    break;
                default:
                    Rslt = E_NOINTERFACE;
                    break;
            }

            return Rslt;
        }
        #endregion
       
    }


}

9. 在Release 模式下編譯生成 ,,成功后就生成 就可以封裝組件啦

 

#start ################################################### 從此行到 #end ###之間為網上的教程,,沒有測試成功,,,具體為什么還未知,,可能是還要再做一層接口封裝,如有網友知道可留言

10,生成activex控件.....在當前解決方案里添加一新項目 Setup Project    沒有的自行百度解決..網上資料很多

添加工程后如下圖,,在項目上  Add -- 項目輸出     主輸出   確定; 

 先點 下圖的1 ,再點 2 的屬性 ,,設置 3   Register為vsdrpCOM

  

編譯生成!!!!  這時在項目目錄下就會 有setup 兩個文件...這時控件就生成了,,安裝就可以使用..可以直接雙擊setup1.msi安裝..也可以在項目上右鍵 安裝/卸載

 

#end ############################################################

 

2019年3月15日更新  開始位置:

 這里用的是我自己的工程,和之前更新的工程名和動態庫名會不一樣,,但意思是一樣的...如不理解可留言...

在第9步 Release 編譯結束后,,可以編譯目錄下找到 編譯完成的dll文件夾  Release  

將整個文件夾復制出來 這里我自己用的工程,,只保留dll,tlb,和x86,x64文件夾,放兩個bat文件進去,代碼如下

注冊:

@echo off
set filename=gMapActiveX.dll
set Frameworkdc=%SystemRoot%\Microsoft.NET\Framework\v4.0.30319
 
if exist "%Frameworkdc%" goto netOld 
:DispError 
echo 您的機器上沒有安裝 .net Framework 4.0,安裝即將終止.
goto LastEnd 
:netOld 
cd %Frameworkdc%
echo 您的機器上安裝了相應的.net Framework 4.0,可以安裝本服務. 
echo 請輸入回車確認安裝:
PAUSE
rem %~dp0表示(bat文件)當前目錄    
%SystemRoot%\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe /codebase %~dp0\%filename%
PAUSE

  反注冊:

@echo off
set filename=gMapActiveX.dll
rem %~dp0表示(bat文件)當前目錄    
%SystemRoot%\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe /unregister %~dp0\%filename%
PAUSE

  里面的filename需根據自己情況更改

這樣只要將如下圖的整個文件夾復制到任意一台電腦,,只要是有.net4.0的電腦,,以管理員運行注冊,就可以將控件注冊到注冊表,,然后就可以調用了..

提示::整個過程有幾點需要注意的,,如果gmap是以x64來編譯的,那在調用的時候也必須要以64位程序來調用...反之用x86也一樣..如果用any cpu 調用,理論上是可以64位和32位都可調用..

  需要運行的機器有.net4.0 

  vs 勾選了 為com互操作后,vs就可以自動注冊,,本開發機上就可隨意調

 

 2019年3月15日更新結束位置...............................................................

 

 

安裝結束后 在注冊表里可查看是否安裝成功

 

 

 

 

 

以下是如何在QT中調用gMap

一.創建一個新的帶窗口的QT工程

在pro 文件中添加配置   axcontainer ,  如下:   

QT       += core gui axcontainer

重新qmake 

添加頭文件

#include <QAxWidget>

 

cpp中添加以下代碼

#include "MainWindow.h"
#include "ui_MainWindow.h"
#include <QAxWidget>  //activex控件頭文件

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QAxWidget * axWidget = new QAxWidget(this);
    axWidget->setControl(QString::fromUtf8("{0E32CE01-4D40-45A6-8B59-B39ABFE8ADB0}"));//這里的guid 就是在gmap中生成添加的guid..同時在注冊表中也可以看到
   // axWidget->resize(620,720);
    axWidget->show();
}

MainWindow::~MainWindow()
{
    delete ui;
}

 

 

正常來說到這里就可以使用了,,但我這次為了寫這個記錄,順便操作了一次,不知道是什么原因沒有成功調用...

哪天我發現了再上來說明

 

封裝過程參考:https://www.cnblogs.com/wyynts/p/6874387.html

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM