【问题标题】:Calling C++ dll from renderer process using node ffi-napi electron使用节点 ffi-napi electron 从渲染器进程调用 C++ dll
【发布时间】:2021-07-30 23:50:03
【问题描述】:

我正在使用电子创建一个 Windows 应用程序。我想使用 ffi-napi 调用 C++ .dll 方法。为此,我编写了以下代码(共享 C++ 和电子代码)。我一直停留在“Passing_Dll.js”文件上,该文件没有在单击开始和停止按钮时更新 id="GenName" 的标签。我的意思是 ces.start() 或 ces.stop() 函数在“Passing_Dll.js”文件中不起作用。我试图将这一行 document.getElementById('GenName').innerHTML = "Dummy string" 放在 "Passing_Dll.js" 文件中的行 var ffi =require("ffi-napi") 之后,并意识到它甚至没有显示虚拟字符串也在GUI中。但是如果我在 ces.start(() 和 ces.stop() 之前将同一行 document.getElementById('GenName').innerHTML = "Dummy string" 放在“Passing_Dll.js”文件的启动和停止函数中,那么它会显示“虚拟字符串”。有人可以向我建议我做错了什么吗?为什么它没有正确执行“Passing_Dll.js”。我们是否需要对 main.js 或 package.json 文件做一些事情。请建议。如果您可以发送一些工作代码sn-p,那将非常有用。

dll.h

#ifdef DLL1_EXPORTS
#define DLL1_API __declspec(dllexport)
#else
#define DLL1_API __declspec(dllimport)
#endif

enum GUIENUM
{
    START = 0,
    STOP,
    DEBUG,
    STATUS,
};

typedef bool(*Callback)(bool, const char*);
extern "C" DLL1_API BOOL Connect(const char *userName, const char *password, Callback callback);

typedef void(*callbackMethod)(int statustype, const char* status);
extern "C" DLL1_API void GetCallMethod(callbackMethod callback);

extern "C" DLL1_API void Start();
extern "C" DLL1_API void Stop();

dll.cpp

#include "pch.h"
#include "framework.h"
#include "Dll1.h"
callbackMethod m_callback;

// This is an example of an exported variable
DLL1_API BOOL Connect(const char *userName, const char *password, Callback callback)
{
    callback(true, "User is connected");

    return true;
}

DLL1_API void GetCallMethod(callbackMethod callback)
{
    m_callback = callback;
    m_callback(START, "Initializaition is done");
}

DLL1_API void Start()
{
    m_callback(STATUS, "Start is pressed");
}

DLL1_API void Stop()
{
    m_callback(DEBUG, "stop is pressed");
}

index.html

<html>
 <head>
 <meta charset="UTF-8">
 <title>SIMS_R 5.2.10.2 GEN1.2 -AppFLR</title>
 </head>
 <body>
    <label for="name" id ="GenName">R 16_31_80D7_99</label><br>
    <button type="button" onclick="startFunction()" value="Display" >Start</button><br>
    <button type="button" onclick="stopFunction()" value="Display2">Stop</button><br>   
    <script src="Passing_Dll.js"></script>
 </body>
 </html>

ma​​in.js

const{BrowserWindow} =require("electron");
const app=require('electron').app;

function createWindow() {
    // Create the browser window.
   mainWindow = new BrowserWindow({width:600, height: 650});
  
   // Disable menu
   mainWindow.setMenu(null)
    
   // and load the index.html of the app.
   mainWindow.loadURL('file://'+__dirname+'/index.html')
}
app.on('ready',createWindow);

Passing_Dll.js

var ffi =require("ffi-napi");
var ref =require("ref-napi");
var Enum = require('enum')
var constString = ref.types.CString;
var bool = ref.types.bool;
var int = ref.types.int;
var enumCallback = new Enum({  'START': 0,
  'STOP': 1,
  'DEBUG': 2,
  'STATUS': 3
}); 

var callback = ffi.Callback(bool, [bool, constString], function (sec, data) {
    console.log(sec)
    console.log(data);
  
  });

var callback2 =ffi.Callback(ref.types.void, [int, constString], function (sec, data) {
    switch(sec)
    {
      case enumCallback.START.value: 
       console.log(sec);
       console.log(data);
       document.getElementById('GenName').innerHTML = data;
       break;
       case enumCallback.STOP.value: 
       console.log(sec);
       console.log(data);
       document.getElementById('GenName').innerHTML = data;
       break;
       case enumCallback.STATUS.value: 
       console.log(sec);
       console.log(data);
       document.getElementById('GenName').innerHTML = data;
       break;
       case enumCallback.DEBUG.value: 
       console.log(sec);
       console.log(data);
       document.getElementById('GenName').innerHTML = data
       break;
       default:
        console.log(sec);
        console.log(data);
        document.getElementById('GenName').innerHTML = data
        break;
    }
});

var libloc = "./Dll1.dll";
  
const ces = ffi.Library(libloc, {
  'Connect': [bool, [constString, constString, 'pointer']],
  'GetCallMethod':[ref.types.void, ['pointer']],
  'Start': [ref.types.void,[ ]],
  'Stop': [ref.types.void,[ ]],
  
});

module.exports = ces;

var maxLength = 200;
var nameBuffer1 = Buffer.alloc(maxLength);
nameBuffer1.fill(0); 
nameBuffer1.write("user", 0, "utf-8"); 


var nameBuffer2 = Buffer.alloc(maxLength);
nameBuffer2.fill(0); 
nameBuffer2.write("password", 0, "utf-8");

ces.Connect(nameBuffer1,nameBuffer2,callback)
ces.GetCallMethod(callback2)
  
function startFunction()
{
    ces.Start()
}
  
function stopFunction()
{
    ces.Stop()
}

【问题讨论】:

    标签: javascript c++ node.js electron


    【解决方案1】:

    electron 中的渲染过程无法访问您的原生库,因此您无法直接从 index.html 中链接的 js 调用原生函数。而是需要使用electron提供的ipc调用native函数,并将native函数返回的数据传递给renderer进程。

    ipc.js

    const { ipcRenderer } = require("electron")
    
    // send message to main process to run start function
    function startFunction() {
      ipcRenderer.send("start")
    }
    
    function stopFunction() {
      ipcRenderer.send("stop")
    }
    
    ipcRenderer.on("callback-data", function(e, data) {
      document.getElementById('GenName').innerHTML = data;
    })
    

    ma​​in.js

    const{BrowserWindow, ipcMain} =require("electron");
    const app=require('electron').app;
    const {ces, startFunction} = require("./Passing_Dll")
    
    function createWindow() {
        // Create the browser window.
       mainWindow = new BrowserWindow({width:600, height: 650});
      
       // Disable menu
       mainWindow.setMenu(null)
        
       // and load the index.html of the app.
       mainWindow.loadURL('file://'+__dirname+'/index.html')
    }
    app.on('ready',createWindow);
    
    ipcMain.on("start", function (e) {
      startFunction(e)
    })
    
    ipcMain.on("stop", function (e) {
      ces.Stop()
    })
    

    Passing_Dll.js

    var ffi =require("ffi-napi");
    var ref =require("ref-napi");
    var Enum = require('enum')
    var constString = ref.types.CString;
    var bool = ref.types.bool;
    var int = ref.types.int;
    var enumCallback = new Enum({  'START': 0,
      'STOP': 1,
      'DEBUG': 2,
      'STATUS': 3
    }); 
    
    var callback = ffi.Callback(bool, [bool, constString], function (sec, data) {
        console.log(sec)
        console.log(data);
      
      });
    
    var libloc = "./Dll1.dll";
      
    const ces = ffi.Library(libloc, {
      'Connect': [bool, [constString, constString, 'pointer']],
      'GetCallMethod':[ref.types.void, ['pointer']],
      'Start': [ref.types.void,[ ]],
      'Stop': [ref.types.void,[ ]],
      
    });
    
    
    var maxLength = 200;
    var nameBuffer1 = Buffer.alloc(maxLength);
    nameBuffer1.fill(0); 
    nameBuffer1.write("user", 0, "utf-8"); 
    
    
    var nameBuffer2 = Buffer.alloc(maxLength);
    nameBuffer2.fill(0); 
    nameBuffer2.write("password", 0, "utf-8");
    
    ces.Connect(nameBuffer1,nameBuffer2,callback)
    
    function startFunction (e)
    {
      // register a new callback each time you call the function in order to use the ipc context
      var callback2 =ffi.Callback(ref.types.void, [int, constString], function (sec, data) {
        console.log(sec);
        // data buffer may need to be converted to a js string using readCString()
        console.log(data);
        e.reply("callback-data", data)
      });
    
      ces.GetCallMethod(callback2)
      ces.Start()
    }
    
    module.exports = {ces, startFunction};
    

    您还需要存储回调缓冲区,以免它们被垃圾收集。 ffi教程建议使用process.on("exit", function() { callback }),但我发现在电子应用打包后不起作用,所以我经常在全局对象中引用它们。

    【讨论】:

      猜你喜欢
      • 2019-03-21
      • 2020-10-15
      • 2017-02-06
      • 2021-06-26
      • 1970-01-01
      • 2020-04-09
      • 2016-06-13
      • 2016-07-05
      • 2020-07-13
      相关资源
      最近更新 更多