欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++插件化 NDD源碼的插件機(jī)制實(shí)現(xiàn)解析

 更新時(shí)間:2023年03月20日 15:02:39   作者:特立獨(dú)行的貓a  
這篇文章主要介紹了C++插件化 NDD源碼的插件機(jī)制實(shí)現(xiàn)解析,這里再介紹推薦下優(yōu)秀的國(guó)產(chǎn)軟件開源項(xiàng)目?NDD(notepad--),一個(gè)支持windows/linux/mac的文本編輯器,目標(biāo)是要國(guó)產(chǎn)替換同類軟件,需要的朋友可以參考下

插件機(jī)制是一種框架,允許開發(fā)人員簡(jiǎn)單地在應(yīng)用程序中添加或擴(kuò)展功能。它使廣泛使用,因?yàn)樗梢宰鳛槟K被重復(fù)使用,并使它們更易于維護(hù)和擴(kuò)展,因此它們?cè)趹?yīng)用程序中非常有用。插件機(jī)制允許管理員在需要時(shí)輕松安裝和卸載插件,而無需對(duì)基礎(chǔ)應(yīng)用程序做出更改。

NDD介紹

這里再介紹推薦下優(yōu)秀的國(guó)產(chǎn)軟件開源項(xiàng)目 NDD(notepad--)。一個(gè)支持windows/linux/mac的文本編輯器,目標(biāo)是要國(guó)產(chǎn)替換同類軟件。對(duì)比其它競(jìng)品Notepad類軟件而言,優(yōu)勢(shì)是可以跨平臺(tái),支持linux mac操作系統(tǒng)。期待國(guó)人參與開源,貢獻(xiàn)更多有意思的插件。

gitee倉(cāng)庫(kù)地址:https://gitee.com/cxasm/notepad--

 插件的優(yōu)勢(shì)

基于插件的擴(kuò)展性,進(jìn)而實(shí)現(xiàn)業(yè)務(wù)模塊兒的獨(dú)立和解耦,增加可維護(hù)性和可擴(kuò)展性。插件使得第三方開發(fā)人員可以為系統(tǒng)做增值和拓展工作,也可以使其他開發(fā)人員協(xié)同開發(fā)相互配合,增加新的功能而不破壞現(xiàn)有的核心功能。插件化還能夠促進(jìn)將關(guān)注點(diǎn)分開,保證隱藏實(shí)現(xiàn)細(xì)節(jié),且可以將測(cè)試獨(dú)立開來,并最具有實(shí)踐意義。

比如強(qiáng)大的Eclipse的平臺(tái)實(shí)際上就是一個(gè)所有功能都由插件提供的骨架。Eclipse IDE自身(包括UI和Java開發(fā)環(huán)境)僅僅是一系列掛在核心框架上的插件。

NDD的插件化實(shí)現(xiàn),是一種很好的范例,讓我們看到插件化機(jī)制的好處,可以靈活的對(duì)軟件進(jìn)行功能拓展,以下對(duì)NDD的插件化實(shí)現(xiàn)原理做下分析。

NDD插件機(jī)制分析

用C++實(shí)現(xiàn)插件機(jī)制的基本思路是:

一、應(yīng)用程序(框架)提供出插件接口。

二、由用戶或第三方實(shí)現(xiàn)這些接口,并編譯出相應(yīng)的動(dòng)態(tài)庫(kù)(即插件);

三、將所有插件放到某個(gè)特定目錄,應(yīng)用程序(框架)運(yùn)行時(shí)會(huì)自動(dòng)搜索該目錄,并動(dòng)態(tài)加載目錄中的插件。

按照以上思路,分析下NDD源碼中的插件機(jī)制實(shí)現(xiàn)。

插件接口

NDD源碼中提供出來的插件接口有兩個(gè),接口聲明如下:

#define NDD_EXPORT __declspec(dllexport)
 
#ifdef __cplusplus
	extern "C" {
#endif
 
	NDD_EXPORT bool NDD_PROC_IDENTIFY(NDD_PROC_DATA* pProcData);
	NDD_EXPORT int NDD_PROC_MAIN(QWidget* pNotepad, const QString& strFileName, std::function<QsciScintilla* ()>getCurEdit, NDD_PROC_DATA* procData);
 
 
#ifdef __cplusplus
	}
#endif

需要注意,插件接口必須要用extern "C"包含,因?yàn)镃++的編譯器會(huì)對(duì)程序中符號(hào)進(jìn)行修飾,這個(gè)過程在編譯器中叫符號(hào)修飾(Name Decoration)或者符號(hào)改編(Name Mangling)。如果不改為c的方式,那么動(dòng)態(tài)庫(kù)resolve這種查找入口方式,會(huì)找不到句柄handle入口。

以上兩個(gè)接口,一個(gè)是插件的相關(guān)說明信息,一個(gè)是插件的核心功能實(shí)現(xiàn)。

插件實(shí)現(xiàn)

NDD_PROC_IDENTIFY接口最簡(jiǎn)單,就是用來讓插件開發(fā)者填充插件信息用的。傳進(jìn)來的參數(shù)有以下信息:

struct ndd_proc_data
{
	QString m_strPlugName; //插件名稱 必選
	QString m_strFilePath; //lib 插件的全局路徑。必選。插件內(nèi)部不用管,主程序傳遞下來
	QString m_strComment; //插件說明
	QString m_version; //版本號(hào)碼。可選
	QString m_auther;//作者名稱。可選
	int m_menuType;//菜單類型。0:不使用二級(jí)菜單 1:創(chuàng)建二級(jí)菜單
	QMenu* m_rootMenu;//如果m_menuType = 1,給出二級(jí)根菜單的地址。其他值nullptr
 
	ndd_proc_data(): m_rootMenu(nullptr), m_menuType(0)
	{
 
	}
};
 
 
typedef struct ndd_proc_data NDD_PROC_DATA;
bool NDD_PROC_IDENTIFY(NDD_PROC_DATA* pProcData)
{
	if(pProcData == NULL)
	{
		return false;
	}
	pProcData->m_strPlugName = QObject::tr("Hello World Plug");
	pProcData->m_strComment = QObject::tr("char to Upper.");
 
	pProcData->m_version = QString("v1.0");
	pProcData->m_auther = QString("yangqq.xyz");
 
	pProcData->m_menuType = 1;
 
	return true;
}

另外一個(gè)接口是NDD_PROC_MAIN這個(gè)是插件功能的具體實(shí)現(xiàn)接口,插件開發(fā)者可在此接口中實(shí)現(xiàn)插件的主要功能。

//插件的入口點(diǎn)接口實(shí)現(xiàn)
//則點(diǎn)擊菜單欄按鈕時(shí),會(huì)自動(dòng)調(diào)用到該插件的入口點(diǎn)函數(shù)接口。
//pNotepad:就是CCNotepad的主界面指針
//strFileName:當(dāng)前插件DLL的全路徑,如果不關(guān)心,則可以不使用
//getCurEdit:從NDD主程序傳遞過來的仿函數(shù),通過該函數(shù)獲取當(dāng)前編輯框操作對(duì)象QsciScintilla
int NDD_PROC_MAIN(QWidget* pNotepad, const QString &strFileName, std::function<QsciScintilla*()>getCurEdit, NDD_PROC_DATA* pProcData)
{
    //對(duì)于不需要?jiǎng)?chuàng)建二級(jí)菜單的例子,pProcData總是nullptr。
    //該函數(shù)每次點(diǎn)擊插件菜單時(shí),都會(huì)被執(zhí)行。
    QsciScintilla* pEdit = getCurEdit();
    if (pEdit == nullptr)
    {
	    return -1;
    }
 
	//務(wù)必拷貝一份pProcData,在外面會(huì)釋放。
	if (pProcData != nullptr)
	{
		s_procData = *pProcData;
	}
 
	s_pMainNotepad = pNotepad;
	s_getCurEdit = getCurEdit;
 
	//做一個(gè)簡(jiǎn)單的轉(zhuǎn)大寫的操作
	QtTestClass* p = new QtTestClass(pNotepad,pEdit);
	//主窗口關(guān)閉時(shí),子窗口也關(guān)閉。避免空指針操作
	p->setWindowFlag(Qt::Window);
	p->show();
 
	return 0;
}

完成了以上這兩個(gè)接口,編譯成動(dòng)態(tài)dll庫(kù),其實(shí)插件開發(fā)就完成啦。如果編譯器和使用的QT庫(kù)同NDD發(fā)行版一致,則直接把dll庫(kù)放入plugin目錄即可。接下來看下NDD應(yīng)用程序是如何加載和使用插件的。

NDD插件加載過程

從ndd應(yīng)用程序啟動(dòng)到插件加載。過程大致如下:

int main(int argc, char *argv[])
{
	//可以防止某些屏幕下的字體擁擠重疊問題
	QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#ifdef Q_OS_MAC
    MyApplication a(argc, argv);
#else
	QApplication a(argc, argv);
#endif
 //......
    CCNotePad *pMainNotepad = new CCNotePad(true);
	pMainNotepad->setAttribute(Qt::WA_DeleteOnClose);
	pMainNotepad->setShareMem(&shared);
	pMainNotepad->quickshow();
 
    a.exec();
 
}
//
//先快速讓窗口展示處理,后續(xù)再去做復(fù)雜的初始化
void CCNotePad::quickshow()
{
    //......
    init_toolsMenu();
}
//
void CCNotePad::init_toolsMenu()
{
	slot_dynamicLoadToolMenu();
	//connect(ui.menuTools,&QMenu::aboutToShow,this,&CCNotePad::slot_dynamicLoadToolMenu);
}
//動(dòng)態(tài)加載工具菜單項(xiàng)
void CCNotePad::slot_dynamicLoadToolMenu()
{
 //......
#ifdef NO_PLUGIN
	//動(dòng)態(tài)加載插件
	m_pluginList.clear();
	loadPluginLib();
#endif
}

插件的加載過程在loadPluginLib()函數(shù)中,進(jìn)入到plugin目錄中加載插件。

#ifdef NO_PLUGIN
void CCNotePad::loadPluginLib()
{
	QString strDir = qApp->applicationDirPath();
	QDir dir(strDir);
	if (dir.cd("./plugin"))
	{
		strDir = dir.absolutePath();
 
		loadPluginProcs(strDir,ui.menuPlugin);
	}
}

foundCallback回調(diào)函數(shù)接口,找到插件信息后 在onPlugFound函數(shù)中處理,完成與界面菜單的綁定。

void CCNotePad::loadPluginProcs(QString strLibDir, QMenu* pMenu)
{
	std::function<void(NDD_PROC_DATA&, QMenu*)> foundCallBack = std::bind(&CCNotePad::onPlugFound, this, std::placeholders::_1, std::placeholders::_2);
 
	int nRet = loadProc(strLibDir, foundCallBack, pMenu);
	if (nRet > 0)
	{
		ui.statusBar->showMessage(tr("load plugin in dir %1 success, plugin num %2").arg(strLibDir).arg(nRet));
	}
}

在點(diǎn)擊菜單后觸發(fā)執(zhí)行onPlugWork,如果設(shè)置的有啟用二級(jí)菜單,則初始化設(shè)置二級(jí)菜單。

void CCNotePad::onPlugFound(NDD_PROC_DATA& procData, QMenu* pUserData)
{
	QMenu* pMenu = pUserData;
 
	if (pMenu == NULL)
	{
		return;
	}
 
	//創(chuàng)建action
	if (procData.m_menuType == 0)
	{
		QAction* pAction = new QAction(procData.m_strPlugName, pMenu);
		pMenu->addAction(pAction);
	pAction->setText(procData.m_strPlugName);
	pAction->setData(procData.m_strFilePath);
	connect(pAction, &QAction::triggered, this, &CCNotePad::onPlugWork);
	}
	else if (procData.m_menuType == 1)
	{
		//創(chuàng)建二級(jí)菜單
		QMenu* pluginMenu = new QMenu(procData.m_strPlugName, pMenu);
		pMenu->addMenu(pluginMenu);
 
		//菜單句柄通過procData傳遞到插件中
		procData.m_rootMenu = pluginMenu;
		sendParaToPlugin(procData);
	}
	else
	{
		return;
	}
    // 暫存加載到的插件信息
	m_pluginList.append(procData);
}
//把插件需要的參數(shù),傳遞到插件中去
void CCNotePad::sendParaToPlugin(NDD_PROC_DATA& procData)
{
	QString plugPath = procData.m_strFilePath;
 
	QLibrary* pLib = new QLibrary(plugPath);
 
	NDD_PROC_MAIN_CALLBACK pMainCallBack;
	pMainCallBack = (NDD_PROC_MAIN_CALLBACK)pLib->resolve("NDD_PROC_MAIN");
 
		if (pMainCallBack != NULL)
		{
			std::function<QsciScintilla* ()> foundCallBack = std::bind(&CCNotePad::getCurEditView, this);
 
			pMainCallBack(this, plugPath, foundCallBack, &procData);
		}
		else
		{
			ui.statusBar->showMessage(tr("plugin %1 load failed !").arg(plugPath), 10000);
		}
}
//真正執(zhí)行插件的工作
void CCNotePad::onPlugWork(bool check)
{
	QAction* pAct = dynamic_cast<QAction*>(sender());
	if (pAct != nullptr)
	{
		QString plugPath = pAct->data().toString();
 
		QLibrary* pLib = new QLibrary(plugPath);
 
		NDD_PROC_MAIN_CALLBACK pMainCallBack;
		pMainCallBack = (NDD_PROC_MAIN_CALLBACK)pLib->resolve("NDD_PROC_MAIN");
 
		if (pMainCallBack != NULL)
		{
			std::function<QsciScintilla* ()> foundCallBack = std::bind(&CCNotePad::getCurEditView, this);
 
			pMainCallBack(this, plugPath, foundCallBack, nullptr);
		}
		else
		{
			ui.statusBar->showMessage(tr("plugin %1 load failed !").arg(plugPath), 10000);
		}
	
	}
}

雖然以上過程看似復(fù)雜一點(diǎn)兒,其實(shí)關(guān)鍵調(diào)用就是拿到函數(shù)指針,然后根據(jù)需要做些處理。插件信息存儲(chǔ)在QList<NDD_PROC_DATA> m_pluginList。有個(gè)界面對(duì)這個(gè)信息進(jìn)行展示。

void  CCNotePad::slot_pluginMgr()
{
#ifdef NO_PLUGIN
	PluginMgr* pWin = new PluginMgr(this, m_pluginList);
	pWin->setAttribute(Qt::WA_DeleteOnClose);
	pWin->show();
#else
	QMessageBox::warning(this, "info", u8"便攜版本不支持插件,請(qǐng)下載插件版!");
#endif
}

為防止中文亂碼,支持中文的方法是文件編碼保存為utf-8格式。 輸入漢字如上寫法,u8"中文字符"。編譯腳本指定如下:

# win下需要開啟UNICODE進(jìn)行支持TCHAR
if(CMAKE_HOST_WIN32)
    add_definitions(-D_UNICODE -DUNICODE)
endif()

plugin機(jī)制的關(guān)鍵,既定義函數(shù)指針,拿到函數(shù)指針,使用函數(shù)指針。 

typedef bool (*NDD_PROC_IDENTIFY_CALLBACK)(NDD_PROC_DATA* pProcData);
typedef void (*NDD_PROC_FOUND_CALLBACK)(NDD_PROC_DATA* pProcData, void* pUserData);
#include "plugin.h"
#include <QLibrary>
#include <QDir>
#include <QMenu>
#include <QAction>
 
bool loadApplication(const QString& strFileName, NDD_PROC_DATA* pProcData)
{
	QLibrary lib(strFileName);
	NDD_PROC_IDENTIFY_CALLBACK procCallBack;
 
	procCallBack = (NDD_PROC_IDENTIFY_CALLBACK)lib.resolve("NDD_PROC_IDENTIFY");
 
	if (procCallBack == NULL)
	{
		return false;
	}
 
	if (!procCallBack(pProcData))
	{
		return false;
	}
	pProcData->m_strFilePath = strFileName;
	return true;
}
 
int loadProc(const QString& strDirOut, std::function<void(NDD_PROC_DATA&, QMenu*)> funcallback, QMenu* pUserData)
{
	int nReturn = 0;
	QStringList list;
 
	QDir dir;
	dir.setPath(strDirOut);
 
	QString strDir, strName;
	QStringList strFilter;
 
	strDir = dir.absolutePath();
	strDir += QDir::separator();
#if  defined(Q_OS_WIN)
	strFilter << "*.dll";
#else
	strFilter << "lib*.so";
#endif
	list = dir.entryList(strFilter, QDir::Files | QDir::Readable, QDir::Name);
	QStringList::Iterator it = list.begin();
 
	for (; it != list.end(); ++it)
	{
		NDD_PROC_DATA procData;
		strName = *it;
		strName = strDir + strName;
 
		if (!loadApplication(strName, &procData))
		{
			continue;
		}
 
		funcallback(procData, pUserData);
		
		nReturn++;
	}
 
	return nReturn;
}

到此這篇關(guān)于C++插件化 NDD源碼的插件機(jī)制實(shí)現(xiàn)解析的文章就介紹到這了,更多相關(guān)c++ NDD源碼插件機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Linux下用C語言實(shí)現(xiàn)推箱子游戲

    Linux下用C語言實(shí)現(xiàn)推箱子游戲

    這篇文章主要為大家詳細(xì)介紹了Linux下用C語言實(shí)現(xiàn)小老鼠推箱子的游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • OpenCV reshape函數(shù)實(shí)現(xiàn)矩陣元素序列化

    OpenCV reshape函數(shù)實(shí)現(xiàn)矩陣元素序列化

    reshape函數(shù)是OpenCV中一個(gè)很有用的函數(shù),不僅可以改變矩陣的通道數(shù),還可以對(duì)矩陣元素進(jìn)行序列化。本文將主要介紹如何通過reshape實(shí)現(xiàn)矩陣元素序列化,需要的小伙伴可以參考一下
    2021-12-12
  • C語言函數(shù)的遞歸調(diào)用詳情

    C語言函數(shù)的遞歸調(diào)用詳情

    這篇文章主要介紹了C語言函數(shù)的遞歸調(diào)用詳情,遞歸做為一種算法在程序設(shè)計(jì)語言中廣泛應(yīng)用,主要的思考方式就是大事化小,下文具體的相關(guān)介紹,需要的小伙伴可以參考一下
    2022-04-04
  • 基于C++中setiosflags()的用法詳解

    基于C++中setiosflags()的用法詳解

    下面小編就為大家?guī)硪黄贑++中setiosflags()的用法詳解。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • epoll封裝reactor原理剖析示例詳解

    epoll封裝reactor原理剖析示例詳解

    這篇文章主要為大家介紹了epoll封裝reactor原理剖析示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • c++與c中的數(shù)組初始化默認(rèn)值如何為0

    c++與c中的數(shù)組初始化默認(rèn)值如何為0

    這篇文章主要介紹了c++與c中的數(shù)組初始化默認(rèn)值如何為0問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • 顯示內(nèi)存狀態(tài)示例分享

    顯示內(nèi)存狀態(tài)示例分享

    這篇文章主要介紹了顯示內(nèi)存狀態(tài)示例,代碼簡(jiǎn)單,下面直接看代碼,需要的朋友可以參考下
    2014-02-02
  • MFC創(chuàng)建右鍵彈出菜單的方法

    MFC創(chuàng)建右鍵彈出菜單的方法

    這篇文章主要介紹了MFC創(chuàng)建右鍵彈出菜單的方法,較為詳細(xì)的分析了創(chuàng)建菜單資源及視類添加WM_RBUTTONDOWN消息的實(shí)現(xiàn)方法,是非常實(shí)用的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-01-01
  • C語言回溯法 實(shí)現(xiàn)組合數(shù) 從N個(gè)數(shù)中選擇M個(gè)數(shù)

    C語言回溯法 實(shí)現(xiàn)組合數(shù) 從N個(gè)數(shù)中選擇M個(gè)數(shù)

    在平時(shí)的算法的題目中,時(shí)常會(huì)遇到組合數(shù)相關(guān)的問題,暴力枚舉。在N個(gè)數(shù)中挑選M個(gè)數(shù)出來。利用for循環(huán)也可以處理,但是可拓展性不強(qiáng),于是寫這個(gè)模板供以后參考
    2018-08-08
  • VisualStudio2010安裝教程

    VisualStudio2010安裝教程

    這篇文章通過圖文并茂的形式給大家介紹VisualStudio2010安裝教程,在日常開發(fā)中是必不可少的搭建過程,感興趣的朋友跟隨小編一起看看吧
    2021-11-11

最新評(píng)論