前言
在前面的文章中,我们分别完成了前端React应用和后端C++ DLL的开发。本文将详细介绍如何通过koffi库实现JavaScript与C++的无缝集成,这是整个系统最关键的技术环节。我们将深入探讨数据类型转换、错误处理、性能优化等核心实践。
koffi技术方案概述
为什么选择koffi?
koffi是一个现代化的Node.js原生库调用方案,相比传统的node-ffi具有以下优势:
- 性能更优: 基于最新的V8 API,调用开销更小
- 类型安全: 更好的TypeScript支持和类型检查
- 内存管理: 自动的内存管理,减少内存泄漏风险
- 现代API: 支持Promise、async/await等现代异步模式
- 跨平台: 支持Windows、Linux、macOS等多平台
技术架构设计
我们的前后端集成采用三层架构:
Frontend (React/TypeScript)
↓ (IPC)
Electron Main Process (koffi)
↓ (FFI)
C++ DLL (Business Logic)
数据流向:
- React组件调用useBackendAPI Hook
- 通过Electron IPC发送请求到主进程
- 主进程使用koffi调用C++ DLL函数
- C++ DLL处理业务逻辑并返回JSON结果
- 结果通过相同路径返回到React组件
koffi集成实现
1. 主进程koffi配置
在 code/frontend/electron/main.ts 中配置koffi:
import {
koffi } from 'koffi';
import path from 'path';
interface BackendLibrary {
InitLibrary: (configJson: string) => boolean;
ShutdownLibrary: () => void;
AddBook: (bookJson: string, resultJson: koffi.Buffer, resultSize: number) => boolean;
DeleteBook: (bookId: string, resultJson: koffi.Buffer, resultSize: number) => boolean;
EditBook: (bookJson: string, resultJson: koffi.Buffer, resultSize: number) => boolean;
GetBookList: (queryJson: string, resultJson: koffi.Buffer, resultSize: number) => boolean;
BorrowBook: (requestJson: string, resultJson: koffi.Buffer, resultSize: number) => boolean;
ReturnBook: (requestJson: string, resultJson: koffi.Buffer, resultSize: number) => boolean;
GetDashboardStats: (resultJson: koffi.Buffer, resultSize: number) => boolean;
}
class BackendManager {
private static instance: BackendManager;
private library: BackendLibrary | null = null;
private initialized = false;
public static getInstance(): BackendManager {
if (!BackendManager.instance) {
BackendManager.instance = new BackendManager();
}
return BackendManager.instance;
}
public initialize(): boolean {
try {
// 确定DLL路径
const dllPath = this.getDllPath();
// 加载DLL
const lib = koffi.load(dllPath);
// 定义函数接口
this.library = {
InitLibrary: lib.func('InitLibrary', 'bool', ['string']),
ShutdownLibrary: lib.func('ShutdownLibrary', 'void', []),
// 图书管理接口
AddBook: lib.func('AddBook', 'bool', ['string', 'char *', 'int']),
DeleteBook: lib.func('DeleteBook', 'bool', ['string', 'char *', 'int']),
EditBook: lib.func('EditBook', 'bool', ['string', 'char *', 'int']),
GetBookList: lib.func('GetBookList', 'bool', ['string', 'char *', 'int']),
// 借阅管理接口
BorrowBook: lib.func('BorrowBook', 'bool', ['string', 'char *', 'int']),
ReturnBook: lib.func('ReturnBook', 'bool', ['string', 'char *', 'int']),
// 仪表板接口
GetDashboardStats: lib.func('GetDashboardStats', 'bool', ['char *', 'int'])
};
// 初始化C++库
const config = {
dbPath: path.join(app.getPath('userData'), 'library.db'),
logPath: path.join(app.getPath('userData'), 'logs'),
maxCacheSize: 1000,
enableDebugLog: process.env.NODE_ENV === 'development'
};
const success = this.library.InitLibrary(JSON.stringify(config));
if (success) {
this.initialized = true;
console.log('Backend library initialized successfully');
}
return success;
} catch (error) {
console.error('Failed to initialize backend library:', error);
return false;
}
}
private getDllPath(): string {
const isDev = process.env.NODE_ENV === 'development';
const basePath = isDev
? path.join(__dirname, '../../dll')
: path.join(process.resourcesPath, 'dll');
const dllName = process.platform === 'win32'
? 'libBackend.dll'
: process.platform === 'darwin'
? 'libBackend.dylib'
: 'libBackend.so';
return path.join(basePath, dllName);
}
public async callFunction<T = any>(
functionName: keyof BackendLibrary,
params?: any
): Promise<{
success: boolean; data?: T; error?: string }> {
if (!this.initialized || !this.library) {
throw new Error('Backend library not initialized');
}
return new Promise((resolve) => {
try {
const resultBuffer = Buffer.alloc(8192); // 8KB缓冲区
let success = false;
// 根据函数类型调用不同的接口
switch (functionName) {
case 'GetDashboardStats':
success = this.library.GetDashboardStats(resultBuffer, resultBuffer.length);
break;
case 'AddBook':
case 'EditBook':
success = this.library[functionName](
JSON.stringify(params),
resultBuffer,
resultBuffer.length
);
break;
case 'DeleteBook':
success = this.library.DeleteBook(
params.id,
resultBuffer,
resultBuffer.length
);
break;
case 'GetBookList':
success = this.library.GetBookList(
JSON.stringify(params || {
}),
resultBuffer,
resultBuffer.length
);
break;
case 'BorrowBook':
case 'ReturnBook':
success = this.library[functionName](
JSON.stringify(params),
resultBuffer,
resultBuffer.length
);
break;
default:
resolve({
success: false, error: 'Unknown function' });
return;
}
if (success) {
// 解析返回的JSON数据
const resultStr = resultBuffer.toString('utf8').replace(/\0.*$/g, '');
try {
const result = JSON.parse(resultStr);
resolve({
success: true, data: result });
} catch (parseError) {
resolve({
success: false, error: 'Failed to parse result JSON' });
}
} else {
resolve({
success: false, error: 'Backend function call failed' });
}
} catch (error) {
resolve({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
});
}
});
}
public shutdown(): void {
if (this.initialized && this.library) {
this.library.ShutdownLibrary();
this.initialized = false;
console.log('Backend library shutdown');
}
}
}
export {
BackendManager };
2. IPC通信层
实际项目中的IPC处理器通过 DllBridge 类实现:
// electron/main.ts 中的主进程配置
import {
app, BrowserWindow, ipcMain

最低0.47元/天 解锁文章
853

被折叠的 条评论
为什么被折叠?



