一、项目背景与技术选型
在上篇文章实现"选中即问AI"功能的基础上,本文重点解决AI对话窗口的集成与核心功能菜单的开发。通过Notepad++插件体系,我们将实现以下功能矩阵:
- AI交互系统:支持自然语言提问与任务执行
- 代码智能处理:代码解释、优化建议、注释生成
- 配置管理系统:AI平台切换与参数调整
- 即时交互体验:选中内容快速提问机制
在Dock窗口的实现方案选择上,经过对原生开发、第三方库集成和源码剥离三种方案的对比评估,最终采用Notepad++源码裁剪方案。该方案既能保持与宿主程序的UI风格一致性,又可避免引入额外依赖,但需要解决源码依赖链过长的问题。通过裁剪非核心模块(如NppDarkMode)和重构关键函数,成功实现了轻量化集成,Notepad++文件引用如下:
注
:项目已开源、镜像,欢迎使用及指正
二、Dock窗口集成关键技术
2.1 源码裁剪与适配
通过提取Notepad++的窗口管理核心模块,保留以下关键文件:
text
Common.h
Docking.h // 停靠窗口基础定义
DockingDlgInterface.h // 对话框接口
dpiManagerV2.h // DPI自适应管理
StaticDialog.h // 静态对话框基类
dockingResource.h
dpiManagerV2.cpp
StaticDialog.cpp
NppDarkMode.cpp
NppDarkMode.h
Window.h
对NppDarkMode.cpp
进行深度改造,仅保留版本检测功能,移除主题相关实现,最终获得精简的Windows版本检测模块:
cpp
#include "NppDarkMode.h"
enum class SystemVersion
{
Unknown,
Windows10,
Windows11
};
SystemVersion GetWindowsVersion()
{
// 使用RtlGetVersion替代已废弃的GetVersionEx
typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
OSVERSIONINFOW osInfo = { 0 };
HMODULE hMod = GetModuleHandleW(L"ntdll.dll");
if (hMod)
{
auto RtlGetVersion = reinterpret_cast<RtlGetVersionPtr>(
GetProcAddress(hMod, "RtlGetVersion"));
if (RtlGetVersion) {
osInfo.dwOSVersionInfoSize = sizeof(osInfo);
if (RtlGetVersion(&osInfo) == 0)
{ // STATUS_SUCCESS
// Windows 11的版本号为10.0.22000+
if (osInfo.dwMajorVersion == 10 &&
osInfo.dwMinorVersion == 0)
{
if (osInfo.dwBuildNumber >= 22000)
{
return SystemVersion::Windows11;
}
else if (osInfo.dwBuildNumber >= 10240)
{
return SystemVersion::Windows10;
}
}
}
}
}
return SystemVersion::Unknown;
}
bool NppDarkMode::isWindows10() { return GetWindowsVersion() == SystemVersion::Windows10; }
bool NppDarkMode::isWindows11() { return GetWindowsVersion() == SystemVersion::Windows11; }
void NppDarkMode::setDarkTitleBar(HWND hwnd) {}
2.2 AI窗口类设计
构建AiAssistWnd
继承自DockingDlgInterface
,实现以下核心功能:
cpp
class AiAssistWnd : public DockingDlgInterface {
public:
// 构造/析构
AiAssistWnd(HINSTANCE hInst, const NppData& nppData);
// 窗口生命周期管理
virtual void init();
virtual INT_PTR run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam);
// 业务功能接口
void updateModelList(const std::vector<std::wstring>& models);
void appendAnswer(const std::wstring& answer);
};
关键技术实现:
- DPI自适应布局 :通过
dpiManagerV2
实现控件动态缩放 - 富文本交互 :采用
Msftedit.dll
的RichEdit 4.1控件支持格式文本 - 线程安全通信 :使用
SendMessage
进行跨线程UI更新
依赖 <<abstract>> DockingDlgInterface +init() +run_dlgProc() +display() AiAssistWnd -HWND _hModelCombo -HWND _hInputEdit -HWND _hAnswerView +init() +updateModelList() +appendAnswer() +clearConversation() NppData -HWND _nppHandle -HWND _scintillaMainHandle -HWND _scintillaSecondHandle
三、核心菜单功能实现与关键技术解析
3.1 功能菜单架构设计
cpp
// PluginDefinition.h
const int nbFunc = 6; // 定义6个功能命令
// 功能原型声明
void PluginConfig(); // 参数配置
void OpenAiAssistWnd(); // 窗口控制
void ReadCode(); // 代码解读
void OptimizeCode(); // 代码优化
void AddCodeComment(); // 注释生成
void AskBySelectedText(); // 选中提问
关键技术要点:
- 命令标识体系 :
nbFunc
常量定义菜单项总数,保证命令索引的严格对应 - 功能隔离设计:每个功能对应独立函数,符合单一职责原则
- 动态加载机制 :通过
commandMenuInit
实现按需初始化
User Notepad Plugin AiAssistWnd DockingDlgInterface 点击"显示窗口"菜单 调用OpenAiAssistWnd() new() init() NPPM_DMMREGASDCKDLG 注册成功 显示Dock窗口 User Notepad Plugin AiAssistWnd DockingDlgInterface
3.2 菜单初始化实现
cpp
// PluginDefinition.cpp
//
// Initialization of your plugin commands
// You should fill your plugins commands here
void commandMenuInit()
{
//--------------------------------------------//
//-- STEP 3. CUSTOMIZE YOUR PLUGIN COMMANDS --//
//--------------------------------------------//
// with function :
// setCommand(int index, // zero based number to indicate the order of command
// TCHAR *commandName, // the command name that you want to see in plugin menu
// PFUNCPLUGINCMD functionPointer, // the symbol of function (function pointer) associated with this command. The body should be defined below. See Step 4.
// ShortcutKey *shortcut, // optional. Define a shortcut to trigger this command
// bool check0nInit // optional. Make this menu item be checked visually
// );
// 初始化数据
g_pNppImp = new NppImp(g_nppData);
// 初始化菜单
ShortcutKey* pSck = new ShortcutKey[nbFunc];
g_pShortcutKeys = pSck;
size_t nCid = 0;
setCommand(nCid, L"参数配置", PluginConfig, NULL, false); ++nCid;
pSck[nCid] = { false, true, false, 'K' };
setCommand(nCid, L"显示窗口", OpenAiAssistWnd, pSck + nCid, false); ++nCid;
pSck[nCid] = { false, true, false, 'J' };
setCommand(nCid, L"解读代码", ReadCode, pSck + nCid, false); ++nCid;
pSck[nCid] = { false, true, false, 'Y' };
setCommand(nCid, L"优化代码", OptimizeCode, pSck + nCid, false); ++nCid;
pSck[nCid] = { false, true, false, 'Z' };
setCommand(nCid, L"代码注释", AddCodeComment, pSck + nCid, false); ++nCid;
pSck[nCid] = { false, true, false, 'A' };
setCommand(nCid, L"选中即问", AskBySelectedText, pSck + nCid, false); ++nCid;
}
关键技术解析:
-
setCommand参数详解:
index
:菜单项位置索引(0-based)commandName
:菜单显示文本(支持多语言)functionPointer
:功能函数地址shortcut
:快捷键组合指针check0nInit
:初始选中状态
-
快捷键数据结构:
cpp
struct ShortcutKey {
bool isCtrl;
bool isAlt;
bool isShift;
UCHAR key;
};
- 内存管理 :使用
new
动态分配快捷键数组,需在插件卸载时释放
3.3 窗口控制实现
cpp
// PluginDefinition.cpp
// 打开Ai助手窗口
void OpenAiAssistWnd() {
if (!g_pAiWnd) {
g_pAiWnd = new AiAssistWnd((HINSTANCE)g_hModule, g_nppData);
g_pAiWnd->init();
}
g_pAiWnd->display(true); // 显示/激活窗口
}
关键技术要点:
- 单例模式 :通过全局指针
g_pAiWnd
确保窗口唯一性 - 资源绑定 :
g_hModule
:插件DLL模块句柄g_nppData
:Notepad++核心数据结构
- 显示控制 :
display(true)
调用API实现窗口显示
否 是 用户触发窗口显示 窗口实例是否存在? 创建AiAssistWnd对象 调用init()方法 注册Dock窗口 初始化UI控件 调用display(true) 显示交互窗口
四、功能扩展与效果展示
4.1 插件菜单效果

4.2 AI交互窗口

五、总结说明
当前实现已构建起AI辅助开发的基础框架,后续可通过扩展AI引擎接口、优化交互流程、增强代码理解能力等方向持续演进,最终打造智能化的代码辅助工具。开发者可根据实际需求,参考本文提供的技术路径进行功能扩展和深度定制。