C++编写PowerPoint插件(四):集成DuiLib

本系列描述的是如何使用C++/COM来编写PowerPoint插件,使用的开发工具是 Visual Studio 2017。

Step 1:工程中引入DuiLib

  1. 将编译好的duilib(包括头文件、lib、dll)拷贝到工程所在目录下

  2. 右键NativePPTAddin工程节点,属性->VC++目录->包含目录和库目录中加入duilib所在目录,加入后大概长这样:

build-cplusplus-addin-for-ppt-18.png
  1. 由于Duilib的头文件中包含了StdAfx.h,如果工程中没有这个文件,需要新建一个,留空白即可

  2. pch.h中引入DuiLib

    #include "UIlib.h"
    using namespace DuiLib;
    #ifdef _DEBUG
    #   ifdef _UNICODE
    #       pragma comment(lib, "DuiLib_d.lib")
    #   else
    #       pragma comment(lib, "DuiLibA_d.lib")
    #   endif
    #else
    #   ifdef _UNICODE
    #       pragma comment(lib, "DuiLib.lib")
    #   else
    #       pragma comment(lib, "DuiLibA.lib")
    #   endif
    #endif
    

Step 2:添加DuiLib的XML界面到资源

  1. 我们以登录框为例,添加一个XML到当前工程,取名叫LoginDialog.xml

  2. 将LoginDialog.xml导入到资源中,资源类型为DUILIB

    <?xml version="1.0" encoding="utf-8" ?>
    <Window size="400,320" caption="0,0,0,60" bktrans="true">
      <VerticalLayout>
        <HorizontalLayout height="52" bkcolor="#FF0288D1">
          <Label text="登录测试框" textcolor="0xFFFFFFFF" padding="20,0,0,0" font="1" />
          <Control />
          <Button name="minBtn" width="10" height="10" padding="0,21,20,0" normalimage="images\minimize.png" />
          <Button name="closeBtn" width="10" height="10" padding="0,21,20,0" normalimage="images\close.png" />
        </HorizontalLayout>
        <VerticalLayout bkcolor="0xFFFFFFFF">
          <Edit name="usernameEdit" height="48" font="3" padding="20,30,20,0" bordercolor="0xFF808080" bottombordersize="1" />
          <Edit name="passwordEdit" height="48" password="true" font="3" padding="20,20,20,0" bordercolor="0xFF808080" bottombordersize="1" />
          <Button name="loginBtn" text="登   录" height="50" bkcolor="#FF0277BD" textcolor="0xFFFFFFFF" font="2" padding="20,40,20,0" />
        </VerticalLayout>
      </VerticalLayout>
    </Window>
    
  3. 添加资源后,资源列表大概长这样:

build-cplusplus-addin-for-ppt-19.png

Step 3:C++实现LoginDialog

  1. 在NativePPTAddin工程节点,添加->新建项,选择"C++类"
build-cplusplus-addin-for-ppt-20.png
  1. 修改LoginDialog类,让它继承自WindowImplBase

    class LoginDialog : public WindowImplBase
    
  2. 实现基类WindowImplBase的几个虚函数

    DuiLib::CDuiString LoginDialog::GetSkinType()
    {
        return _T("DUILIB");
    }
    
    CDuiString LoginDialog::GetSkinFile()
    {
        DuiLib::CDuiString dsResID;
        dsResID.Format(_T("%d"), IDR_DUILIB_LOGIN);
        return dsResID;
    }
    

    这表示LoginDialog将从资源中获取界面,资源类型为DUILIB,资源ID为IDR_DUILIB_LOGIN。

  3. 再简单实现最小化、关闭、登录按钮的事件处理

    DUI_BEGIN_MESSAGE_MAP(LoginDialog, WindowImplBase)
    DUI_ON_MSGTYPE_CTRNAME(DUI_MSGTYPE_CLICK, _T("closeBtn"), onCloseBtnClick)
    DUI_ON_MSGTYPE_CTRNAME(DUI_MSGTYPE_CLICK, _T("minBtn"), onMinBtnClick)
    DUI_ON_MSGTYPE_CTRNAME(DUI_MSGTYPE_CLICK, _T("loginBtn"), onLoginBtnClick)
    DUI_END_MESSAGE_MAP()
        
    void LoginDialog::onCloseBtnClick(TNotifyUI & msg)
    {
        CWindowWnd::SendMessage(WM_CLOSE);
    }
    
    void LoginDialog::onMinBtnClick(TNotifyUI & msg)
    {
        CWindowWnd::SendMessage(WM_SYSCOMMAND, SC_MINIMIZE, 0);
    }
    
    void LoginDialog::onLoginBtnClick(TNotifyUI & msg)
    {
        CEditUI *usernameEdit = static_cast<CEditUI *>(m_pm.FindControl(_T("usernameEdit")));
        CEditUI *passwordEdit = static_cast<CEditUI *>(m_pm.FindControl(_T("passwordEdit")));
        if (usernameEdit == nullptr || passwordEdit == nullptr)
            return;
        TCHAR tmp[1024] = { 0 };
        swprintf_s(tmp, _T("您输入的用户名是 %s, 密码是 %s"), usernameEdit->GetText().GetData(), passwordEdit->GetText().GetData());
        ::MessageBox(m_hWnd, tmp, _T("提示"), MB_OK);
    }
    

Step 4:添加获取应用实例和窗口句柄的全局变量

  1. 在dllmain.cpp中定义全局变量

    HINSTANCE g_hInstance;
    
  2. 在pch.h中添加一个extern声明

    extern HINSTANCE g_hInstance;
    
  3. 修改DllMain入口函数,给g_hInstance赋值

    extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
    {
        g_hInstance = hInstance;
     return _AtlModule.DllMain(dwReason, lpReserved);
    }
    
  4. 在dllmain.cpp再定义一个全局变量,表示PowerPoint的窗口句柄

    我们可以通过GetCurrentProcessId拿到当前进程ID,再用EnumWindows枚举窗口,结合GetWindowThreadProcessId拿到PowerPoint的窗口句柄。

    struct _TempHandleData {
        unsigned long processId;
        HWND windowHandle;
    };
    
    BOOL isMainWindow(HWND handle)
    {
        return ::GetWindow(handle, GW_OWNER) == (HWND)0 && ::IsWindowVisible(handle);
    }
    
    BOOL CALLBACK enumWindowsCallback(HWND handle, LPARAM lParam)
    {
        _TempHandleData& data = *(_TempHandleData*)lParam;
        unsigned long process_id = 0;
        ::GetWindowThreadProcessId(handle, &process_id);
        if (data.processId != process_id || !isMainWindow(handle))
            return TRUE;
        data.windowHandle = handle;
        return FALSE;
    }
    
    HWND GetMainWindow(unsigned long process_id)
    {
        _TempHandleData data;
        data.processId = process_id;
        data.windowHandle = 0;
        ::EnumWindows(enumWindowsCallback, (LPARAM)&data);
        return data.windowHandle;
    }
    
    extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
    {
        g_hInstance = hInstance;
        g_hWnd = GetMainWindow(::GetCurrentProcessId());
        return _AtlModule.DllMain(dwReason, lpReserved);
    }
    

Step 5:在Connect中添加弹出对话框的处理

  1. 将Connect.cpp中原登录按钮的代码删除

  2. 添加弹出登录对话框的函数

    void ShowLoginDialog()
    {
        DuiLib::CPaintManagerUI::SetInstance(g_hInstance);
        TCHAR szPath[MAX_PATH] = { 0 };
        GetModuleFileName(g_hInstance, szPath, _countof(szPath));
        *_tcsrchr(szPath, _T('\\')) = 0;
        DuiLib::CPaintManagerUI::SetResourcePath(szPath);
        LoginDialog dialog;
        dialog.Create(g_hWnd, _T("登录"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
        dialog.CenterWindow();
        dialog.ShowModal();
    }
    
  3. 重新实现Connect的ButtonClicked

    STDMETHODIMP_(HRESULT __stdcall) CConnect::ButtonClicked(IDispatch * control)
    {
        CComQIPtr<IRibbonControl> ribbonCtl(control);
        CComBSTR idStr;
        if (ribbonCtl->get_Id(&idStr) != S_OK)
            return S_FALSE;
        if (idStr == OLESTR("loginButton")) {
            ShowLoginDialog();
        } else if (idStr == OLESTR("uploadButton")) {
            WCHAR msg[64];
            swprintf_s(msg, L"I am uploadButton");
            MessageBoxW(NULL, msg, L"NativePPTAddin", MB_OK);
        }
        return S_OK;
    }
    
  4. 启动调试,点击登录后,大概长这样:

build-cplusplus-addin-for-ppt-21.png

下一篇我们将介绍如何部署。

完整的代码在这里

Reference

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容