first commit

This commit is contained in:
wangminngg
2025-12-02 17:41:15 +08:00
commit 2bc32c2e6b
23 changed files with 4913 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
node_modules/
dist/
*.log
.DS_Store

9
.npmrc Normal file
View File

@@ -0,0 +1,9 @@
registry=https://registry.npmmirror.com
electron_mirror=https://npmmirror.com/mirrors/electron/
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/
ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/
ELECTRON_BUILDER_BINARIES_MIRROR=https://npmmirror.com/mirrors/electron-builder-binaries/
sass_binary_site=https://npmmirror.com/mirrors/node-sass
phantomjs_cdnurl=https://npmmirror.com/mirrors/phantomjs
chromedriver_cdnurl=https://npmmirror.com/mirrors/chromedriver
operadriver_cdnurl=https://npmmirror.com/mirrors/operadriver

View File

@@ -0,0 +1,16 @@
<License>
<Id>d1fc286b-faeb-456e-81c5-93148c0f62fc</Id>
<Type>Standard</Type>
<Expiration>Sun, 30 Nov 2025 16:00:00 GMT</Expiration>
<Quantity>1</Quantity>
<ProductFeatures>
<Feature name="LicVersion">3</Feature>
<Feature name="ProjectSN">10303</Feature>
<Feature name="SerialNumber">5EEE-2929-FA83-5954</Feature>
</ProductFeatures>
<Customer>
<Name>北京灏博云天科技有限公司</Name>
<Email>Obosky@Obosky.com</Email>
</Customer>
<Signature>i1p7HPl5bD5VBg5SpXGX91U/i5ajnJYTjeujkat9C2TsSBbICwapHw2MObkTve45FuAT0Yd37gSfthltbVY1QBbgYeY1mJRfooJMt7xbtOpsZIQfEo88XkUHGLJ0E05WFmY5+Cy1MQhwvNQyomd7wrlCaB3J8AgfWPTGhFAggi9jUTRA/9ZIZKIwX+m1PRfxHRt85STFQzKC1kK9mBC89YUz4vI8tHzWg/vIlJf6iCSZVr6lv8StvRMD+T55qyXQbu84BjObNnMAs6Z63snQfjvu6b28IMuoSIAd73fC8DdRF0OUeX+qvjMcCpMXWaCvXpDIE87bpJVPh/7MSyQUJg==</Signature>
</License>

234
README.md Normal file
View File

@@ -0,0 +1,234 @@
# FFI-NAPI Electron Application
基于 Electron 20 的 外壳程序。
## 特点
- **源码可见**:打包后 main.js、index.html、test.js 等文件保留在 resources/app 目录中
- **便携版**:无需安装,双击运行
- **支持 32 位和 64 位**
## 安装依赖
```bash
npm install
```
或使用国内镜像:
```bash
.\install-electron.bat
```
## 添加图标(必须步骤)
**在打包前必须完成:**
1. 创建 `build` 文件夹:
```bash
mkdir build
```
2. 准备一个 `.ico` 格式的图标文件
- 推荐尺寸256x256 像素
- 必须是 `.ico` 格式(不是 `.png` 或 `.jpg`
3. 将图标文件重命名为 `icon.ico`
4. 将 `icon.ico` 复制到 `build` 文件夹中
5. 验证图标文件:
```bash
node verify-icon.js
```
## 运行开发模式
```bash
npm start
```
## 打包便携版
### 推荐:使用完整重新打包脚本
```bash
.\rebuild-with-icon-fix.bat
```
### 单独打包
32位便携版
```bash
npm run build:32
```
64位便携版
```bash
npm run build:64
```
同时打包:
```bash
npm run build
```
## 便携版使用说明
### 打包后的文件结构
便携版首次运行后会自解压,生成以下目录结构:
```
你的文件夹/
├── FFI-NAPI-App Portable x64 1.0.0.exe # 主程序
└── (首次运行后自动生成)
├── resources/
│ ├── app/ # 应用源码目录(可编辑)
│ │ ├── main.js # 主进程文件
│ │ ├── index.html # 界面文件
│ │ ├── test.js # 业务逻辑文件
│ │ └── package.json # 配置文件
│ ├── exe/ # 外部可执行文件目录
│ │ └── Test.exe
│ └── electron.asar # Electron 核心(不可编辑)
└── ...其他 Electron 文件
```
### 便携版的优势
1. **源码可见**:所有 JavaScript 文件都在 `resources/app/` 目录中,未加密
2. **可以修改**:可以直接编辑 `resources/app/main.js` 等文件来调整功能
3. **便于调试**:出现问题时可以直接查看和修改源码
4. **无需重新打包**:小的修改可以直接编辑文件,无需重新打包
### 部署到其他电脑
直接复制整个文件夹到目标电脑即可,包括:
- 主 `.exe` 文件
- 首次运行后生成的所有文件和文件夹
### 修改便携版代码
1. 运行一次便携版程序(会自解压)
2. 找到 `resources/app/` 目录
3. 用文本编辑器打开需要修改的文件(如 `main.js`
4. 保存修改
5. 重新启动程序即可看到更改效果
### 测试便携版
```bash
.\test-portable.bat
```
此脚本会:
- 模拟便携版部署
- 自动复制 exe 文件夹
- 启动程序进行测试
## 输出文件
打包完成后,便携版程序将生成在 `dist` 目录中:
- 32位: `FFI-NAPI-App-Portable-ia32-1.0.0.exe`
- 64位: `FFI-NAPI-App-Portable-x64-1.0.0.exe`
便携版特点:
- 无需安装,双击即可运行
- 首次运行会自解压所有文件
- 源码保存在 `resources/app` 目录,可以直接修改
- 配置文件保存在程序同目录
- 可以放在 U 盘中使用
## 路径说明
### 开发环境
- 应用文件: `项目根目录/main.js`
- exe 文件路径: `项目根目录/exe/Test.exe`
### 打包后(便携版运行时)
- 应用文件: `resources/app/main.js`(可编辑)
- exe 文件路径: `resources/exe/Test.exe`
**重要:** 便携版不需要编辑任何代码就能正常运行,但如果需要修改,可以直接编辑 `resources/app/` 下的文件。
## 常见问题
### Q: 为什么选择不使用 asar 打包?
A:
- **便于修改**:用户可以直接修改代码,无需重新打包
- **易于调试**:出现问题时可以直接查看源码
- **透明化**:所有代码都是可见的,用户可以了解程序的工作原理
### Q: 如何修改便携版的代码?
A:
1. 首次运行便携版程序,让它自解压
2. 找到程序目录下的 `resources/app/` 文件夹
3. 用任何文本编辑器记事本、VS Code 等)打开要修改的文件
4. 保存后重启程序即可
### Q: 修改代码后需要重新打包吗?
A: 不需要。因为代码没有打包进 asar所以可以直接修改 `resources/app/` 下的文件。
### Q: 便携版的安全性如何?
A: 源码是明文的,任何人都可以查看和修改。如果需要保护代码,可以:
1. 将 `asar: false` 改为 `asar: true`
2. 使用代码混淆工具
3. 使用加密方案
### Q: 如何在便携版中调试?
A:
1. 编辑 `resources/app/main.js`
2. 找到 `webPreferences` 配置
3. 确保包含 `nodeIntegration: true` 和 `contextIsolation: false`
4. 取消注释 `mainWindow.webContents.openDevTools()` 来打开开发者工具
## 目录结构
### 打包前(项目目录)
```
ffi-napi/
├── main.js # Electron 主进程
├── index.html # 应用界面
├── test.js # FFI 调用模块
├── package.json # 项目配置
├── electron-builder.yml # 构建配置asar: false
├── build/ # 构建资源目录
│ └── icon.ico # 应用图标
├── exe/ # 可执行文件目录
│ └── Test.exe
└── dist/ # 打包输出目录
```
### 打包后(便携版运行后)
```
程序目录/
├── FFI-NAPI-App Portable x64 1.0.0.exe
├── resources/
│ ├── app/ # 应用源码(可编辑)
│ │ ├── main.js # 可以用记事本打开修改
│ │ ├── index.html
│ │ ├── test.js
│ │ └── package.json
│ ├── exe/ # 外部程序
│ │ └── Test.exe
│ └── electron.asar # Electron 核心
├── locales/
└── ...其他文件
```
## 注意事项
- 源码在 `resources/app/` 目录中是明文的,可以直接编辑
- 修改代码后无需重新打包,重启程序即可
- 如果需要保护代码,请修改 `electron-builder.yml` 中的 `asar: false` 为 `asar: true`
- 32位程序可以在64位系统上运行
- 64位程序只能在64位系统上运行
- 图标文件必须是标准的 `.ico` 格式
- Windows 会缓存图标,更换图标后需要清理缓存

BIN
build/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

43
clean-build.bat Normal file
View File

@@ -0,0 +1,43 @@
@echo off
REM filepath: /c:/Users/Administrator/Desktop/ffi-napi/clean-build.bat
echo ========================================
echo 清理构建文件
echo ========================================
echo.
echo 步骤 1: 结束可能占用文件的进程...
taskkill /F /IM "FFI-NAPI App.exe" 2>nul
taskkill /F /IM electron.exe 2>nul
timeout /t 2 /nobreak >nul
echo.
echo 步骤 2: 删除 dist 目录...
if exist dist (
echo 正在删除 dist 文件夹...
rmdir /s /q dist 2>nul
if exist dist (
echo 第一次删除失败,等待 3 秒后重试...
timeout /t 3 /nobreak >nul
rmdir /s /q dist 2>nul
)
)
if exist dist (
echo 警告: dist 目录仍然存在,可能有文件被占用
echo 请手动关闭所有相关程序后重试
pause
exit /b 1
) else (
echo dist 目录清理成功!
)
echo.
echo 步骤 3: 删除临时文件...
if exist node_modules\.cache rmdir /s /q node_modules\.cache 2>nul
echo.
echo ========================================
echo 清理完成!现在可以重新构建
echo ========================================
echo.
pause

39
electron-builder-32.yml Normal file
View File

@@ -0,0 +1,39 @@
appId: com.ffi.napi.app
productName: FFI-NAPI App
directories:
output: dist/32bit
buildResources: build
win:
target:
- target: nsis
arch:
- ia32
icon: build/icon.ico
nsis:
oneClick: false
allowToChangeInstallationDirectory: true
perMachine: true
createDesktopShortcut: always
createStartMenuShortcut: true
shortcutName: FFI-NAPI App
artifactName: "${productName}-Setup-Win32-${version}.${ext}"
files:
- "main.js"
- "index.html"
- "test.js"
- "package.json"
- "exe/**/*"
- "!node_modules/**/*"
- "!dist/**/*"
- "!.git/**/*"
- "!*.log"
asar: true
extraResources:
- from: "exe"
to: "exe"
filter: "**/*"

39
electron-builder-64.yml Normal file
View File

@@ -0,0 +1,39 @@
appId: com.ffi.napi.app
productName: FFI-NAPI App
directories:
output: dist/64bit
buildResources: build
win:
target:
- target: nsis
arch:
- x64
icon: build/icon.ico
nsis:
oneClick: false
allowToChangeInstallationDirectory: true
perMachine: true
createDesktopShortcut: always
createStartMenuShortcut: true
shortcutName: FFI-NAPI App
artifactName: "${productName}-Setup-Win64-${version}.${ext}"
files:
- "main.js"
- "index.html"
- "test.js"
- "package.json"
- "exe/**/*"
- "!node_modules/**/*"
- "!dist/**/*"
- "!.git/**/*"
- "!*.log"
asar: true
extraResources:
- from: "exe"
to: "exe"
filter: "**/*"

39
electron-builder.yml Normal file
View File

@@ -0,0 +1,39 @@
appId: com.ffi.napi.app
productName: FFI-NAPI-App
directories:
output: dist
buildResources: build
win:
target:
- target: portable
arch:
- ia32
- x64
icon: build/icon.ico
requestedExecutionLevel: asInvoker
portable:
artifactName: "${productName}-Portable-${arch}-${version}.${ext}"
files:
- "main.js"
- "index.html"
- "test.js"
- "package.json"
- "!node_modules/**/*"
- "!dist/**/*"
- "!.git/**/*"
- "!*.log"
- "!*.bat"
- "!*.md"
- "!build/**/*"
asar: false
extraResources:
- from: "exe"
to: "exe"
filter: "**/*"
- from: "build/icon.ico"
to: "build/icon.ico"

View File

@@ -0,0 +1,3 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAveeb/uEn1ufFqHj7A2zyPQd0ugm5+H321vOrT/yHBXcziqH124Dwpqet4+S/b/JB+jSR8IFAwzoLWGUNRHOuipbf1cPj8eO7EYjyubRXLle296Zf+9K3bQTTm4qnN4mlE+rv35obbKjhcAA8QicASLLU6CzjUtiyWuC9i4fGNUD1jZC7GhDY9Fty7p1KhhMpzDAr1nfRL3UjdCiVOPZ/0R6PYLLATVcsGLUCklnVctwlxxHY9Z00NrBXctMe6ryDcnQ20KAbrnTQwr1IgxVCj6j/HdM9LsllDSBQHDaPIfv5A4VSj/IOVqnZdlua/LIOMLyx9mSUsoPDHSeUbkXV1QIDAQAB
-----END PUBLIC KEY-----

BIN
exe/Obosky.LicenseCore.pdb Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

74
index.html Normal file
View File

@@ -0,0 +1,74 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FFI-NAPI App</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
background: #f0f0f0;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
background: #007acc;
color: white;
border: none;
border-radius: 4px;
}
button:hover {
background: #005a9e;
}
#result {
margin-top: 20px;
padding: 10px;
background: #f5f5f5;
border-radius: 4px;
min-height: 100px;
}
</style>
</head>
<body>
<div class="container">
<h1>FFI-NAPI Application</h1>
<button onclick="checkAuth()">检查授权</button>
<pre id="result"></pre>
</div>
<script>
const { remote, app } = require('electron');
const { checkAuthorizationSync } = require('./test.js');
const path = require('path');
function checkAuth() {
try {
// 获取资源路径
const currentApp = remote ? remote.app : require('electron').app;
const resourcePath = currentApp.isPackaged
? path.join(process.resourcesPath, 'exe')
: path.join(process.cwd(), 'exe');
const exePath = path.join(resourcePath, 'Test.exe');
const command = `"${exePath}" 10303 "${resourcePath}"`;
console.log('执行命令:', command);
const result = checkAuthorizationSync(command);
document.getElementById('result').textContent = JSON.stringify(result, null, 2);
} catch (error) {
document.getElementById('result').textContent = '错误: ' + error.message;
}
}
</script>
</body>
</html>

18
install-electron.bat Normal file
View File

@@ -0,0 +1,18 @@
@echo off
REM filepath: /c:/Users/Administrator/Desktop/ffi-napi/install-electron.bat
echo 正在配置国内镜像源...
npm config set registry https://registry.npmmirror.com
npm config set electron_mirror https://npmmirror.com/mirrors/electron/
npm config set electron_builder_binaries_mirror https://npmmirror.com/mirrors/electron-builder-binaries/
echo 正在清理缓存...
npm cache clean --force
echo 正在删除 node_modules...
if exist node_modules rmdir /s /q node_modules
echo 正在安装依赖...
npm install
pause

20
install-with-cnpm.bat Normal file
View File

@@ -0,0 +1,20 @@
@echo off
REM filepath: /c:/Users/Administrator/Desktop/ffi-napi/install-with-cnpm.bat
echo 使用 cnpm 安装依赖...
echo.
echo 步骤 1: 安装 cnpm...
call npm install -g cnpm --registry=https://registry.npmmirror.com
echo.
echo 步骤 2: 清理旧文件...
if exist node_modules rmdir /s /q node_modules
if exist package-lock.json del /f /q package-lock.json
echo.
echo 步骤 3: 使用 cnpm 安装依赖...
call cnpm install
echo.
echo 安装完成!
pause

57
main.js Normal file
View File

@@ -0,0 +1,57 @@
const { app, BrowserWindow } = require('electron');
const path = require('path');
const fs = require('fs');
let mainWindow;
function createWindow() {
// 简化图标路径:直接从 app 目录下的 build 文件夹获取
const iconPath = path.join(__dirname, 'build', 'icon.ico');
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
icon: fs.existsSync(iconPath) ? iconPath : undefined,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true
}
});
mainWindow.loadFile('index.html');
// 开发模式下打开开发者工具
if (!app.isPackaged || process.env.NODE_ENV === 'development') {
mainWindow.webContents.openDevTools();
}
mainWindow.on('closed', () => {
mainWindow = null;
});
// 打印路径信息用于调试
console.log('========================================');
console.log('应用路径信息:');
console.log('是否打包:', app.isPackaged);
console.log('图标路径:', iconPath);
console.log('图标存在:', fs.existsSync(iconPath));
console.log('当前目录:', __dirname);
console.log('========================================');
}
app.whenReady().then(() => {
createWindow();
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});

4131
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

20
package.json Normal file
View File

@@ -0,0 +1,20 @@
{
"name": "ffi-napi-app",
"version": "1.0.0",
"description": "FFI-NAPI Electron Application",
"main": "main.js",
"scripts": {
"start": "electron .",
"build:32": "electron-builder --win portable --ia32",
"build:64": "electron-builder --win portable --x64",
"build": "npm run build:32 && npm run build:64",
"postinstall": "electron-builder install-app-deps"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"electron": "^20.3.12",
"electron-builder": "^24.13.3"
}
}

92
rebuild-with-icon-fix.bat Normal file
View File

@@ -0,0 +1,92 @@
@echo off
REM filepath: /C:/Users/Administrator/Desktop/ffi-napi/rebuild-with-icon-fix.bat
echo ========================================
echo 完整重新打包(修复图标)
echo ========================================
echo.
REM 1. 检查图标
echo 步骤 1/6: 检查图标文件...
if not exist "build\icon.ico" (
echo [错误] 找不到 build\icon.ico
pause
exit /b 1
)
echo [OK] 图标文件存在
echo.
REM 2. 结束所有相关进程
echo 步骤 2/6: 结束相关进程...
taskkill /F /IM "FFI-NAPI-App*.exe" 2>nul
taskkill /F /IM electron.exe 2>nul
timeout /t 2 /nobreak >nul
echo [OK] 进程已结束
echo.
REM 3. 清理构建目录
echo 步骤 3/6: 清理旧的构建文件...
if exist dist (
rmdir /s /q dist
timeout /t 1 /nobreak >nul
)
if exist node_modules\.cache (
rmdir /s /q node_modules\.cache
)
echo [OK] 构建目录已清理
echo.
REM 4. 清理 Windows 图标缓存
echo 步骤 4/6: 清理 Windows 图标缓存...
echo 正在结束资源管理器...
taskkill /f /im explorer.exe 2>nul
timeout /t 1 /nobreak >nul
cd /d %userprofile%\AppData\Local\Microsoft\Windows\Explorer
attrib -h IconCache.db 2>nul
del IconCache.db /f /q 2>nul
attrib -h iconcache_*.db 2>nul
del iconcache_*.db /f /q 2>nul
cd /d %~dp0
start explorer.exe
timeout /t 2 /nobreak >nul
echo [OK] 图标缓存已清理
echo.
REM 5. 打包 32 位
echo 步骤 5/6: 打包 32 位版本...
echo ========================================
call npm run build:32
if errorlevel 1 (
echo [错误] 32位打包失败
pause
exit /b 1
)
echo [OK] 32位打包完成
echo.
REM 6. 打包 64 位
echo 步骤 6/6: 打包 64 位版本...
echo ========================================
call npm run build:64
if errorlevel 1 (
echo [错误] 64位打包失败
pause
exit /b 1
)
echo [OK] 64位打包完成
echo.
echo ========================================
echo 所有版本打包完成!
echo ========================================
echo.
echo 输出文件:
dir dist\*.exe /b
echo.
echo 注意:如果图标仍未更新,请:
echo 1. 重启电脑
echo 2. 删除旧的 exe 文件
echo 3. 重新运行此脚本
echo.
pause

0
test-icon.js Normal file
View File

33
test.js Normal file
View File

@@ -0,0 +1,33 @@
const { execSync } = require('child_process');
const path = require('path');
/**
* 同步方式调用控制台程序并判断授权
* @param {string} command - 要执行的命令
* @returns {object} - 授权结果
*/
function checkAuthorizationSync(command) {
try {
const output = execSync(command, {
encoding: 'utf-8',
timeout: 10000 // 10秒超时
});
console.log('授权查询结果:', JSON.parse(output));
return JSON.parse(output);
} catch (error) {
console.error('执行失败:', error);
return { error: error.message, authorized: false };
}
}
// 使用示例
if (require.main === module) {
const exeDir = path.join(process.cwd(), 'exe');
const syncResult = checkAuthorizationSync(`.\\exe\\Test.exe 10303 ${exeDir}`);
}
module.exports = {
checkAuthorizationSync,
};

42
verify-icon.js Normal file
View File

@@ -0,0 +1,42 @@
const fs = require('fs');
const path = require('path');
console.log('========================================');
console.log('验证图标文件');
console.log('========================================\n');
const iconPath = path.join(__dirname, 'build', 'icon.ico');
// 检查文件是否存在
if (!fs.existsSync(iconPath)) {
console.error('[错误] 图标文件不存在:', iconPath);
console.log('\n请确保:');
console.log('1. build 文件夹存在');
console.log('2. icon.ico 文件在 build 文件夹中');
process.exit(1);
}
// 获取文件信息
const stats = fs.statSync(iconPath);
console.log('[OK] 图标文件存在');
console.log('路径:', iconPath);
console.log('大小:', stats.size, '字节');
// 检查文件内容ICO 文件头)
const buffer = Buffer.alloc(4);
const fd = fs.openSync(iconPath, 'r');
fs.readSync(fd, buffer, 0, 4, 0);
fs.closeSync(fd);
// ICO 文件头应该是 00 00 01 00
if (buffer[0] === 0x00 && buffer[1] === 0x00 &&
buffer[2] === 0x01 && buffer[3] === 0x00) {
console.log('[OK] 文件格式正确ICO');
} else {
console.error('[警告] 文件可能不是标准的 ICO 格式');
console.log('文件头:', buffer.toString('hex'));
}
console.log('\n========================================');
console.log('验证完成!');
console.log('========================================');