Search code examples
javascriptcnode-ffi

Node ffi napi, call return true but no result in pointer


Need to use some ffi napi in electron, and i trying to get it work, starting from small. Trying to understand and get ffi works, but cant get result. I know that i pass pointer to struct, into which the result will be written. But call return True and no result in the specified pointer. There is no new data. Please help with this.

const ffi = require("ffi-napi");
const ref = require("ref-napi");
const Struct = require("ref-struct-di")(ref);

const ABM_NEW = 0;
const ABM_QUERYPOS = 0x2;
const ABM_GETTASKBARPOS = 5; // 0x00000005
const ABM_GETSTATE = 0x4;

const ABEdgeLeft = 0;

const RECT_Struct = Struct({
  left: "long",
  top: "long",
  right: "long",
  bottom: "long",
});

const APPBARDATA_Struct = Struct({
  cbSize: "uint32",
  hWnd: "int",
  uCallbackMessage: "uint32",
  uEdge: "uint32",
  rc: RECT_Struct,
  lParam: "int64",
});

export const shell32 = ffi.Library("shell32.dll", {
  SHAppBarMessage: ["long", ["int", APPBARDATA_Struct]],
});

export const user32 = ffi.Library("user32.dll", {
  GetWindowRect: ["bool", ["long", RECT_Struct]],
});

const data = new APPBARDATA_Struct();
data.cbSize = APPBARDATA_Struct.size;

const result = shell32.SHAppBarMessage(ABM_GETTASKBARPOS, data);

const rect = new RECT_Struct();
const result2 = user32.GetWindowRect(0x20674, rect);
console.log(`result: ${JSON.stringify(result)}: ${JSON.stringify(data)}`);
console.log(`result2: ${JSON.stringify(result2)}: ${JSON.stringify(rect)}`);

with result

result: 1: {"cbSize":40,"hWnd":0,"uCallbackMessage":0,"uEdge":0,"rc":{"left":0,"top":0,"right":0,"bottom":0},"lParam":0}
result2: true: {"left":0,"top":0,"right":0,"bottom":0}

As i test, function call works - it return 1 (true) with hwnd existed window, and 0 if close. But i cant get result data from buffer and its make me mad.


Solution

  • First, I don't know the pointer well. However, I was interested in the same problem, so I tried and found a solution. I will revise it when I know the exact principle.

    The GetWindowRect and SHAppBarMessage functions you used receive struct pointer type parameters from C++. These functions have the function of notifying the successful or not by the return value and recording the data on the received struct. So, when testing the function in C++, an error occurs when building a structure type parameter without adding the Ampsand(ex. &rect or &data) symbol.

    An example of ref-struct-di shows an example of using a struct pointer to a parameter. I thought this was delivering a struct pointer to a function and it worked when the code was modified as follows. The part that uses struct as a parameter and the part that defines the function with ffi must be modified. Please refer to the comments in code!

    // GetWindowRect Example
    
    const RECT = Struct({
      left: ref.types.long,
      top: ref.types.long,
      right: ref.types.long,
      bottom: ref.types.long,
    });
    
    const user32 = ffi.Library('user32.dll', {
      GetWindowRect: ['bool', ['long', ref.refType(RECT)]], // RECT_Struct -> ref.refType(RECT_Struct) or 'pointer'
    });
    
    const rect = new RECT;
    console.log(user32.GetWindowRect(0x007B0F10, rect.ref())); // 0x007B0F10 is one of my windows that I found with Spy++.
    
    console.log('left', rect.left);
    console.log('right', rect.right);
    console.log('top', rect.top);
    console.log('bottom', rect.bottom);
    
    // SHAppBarMessage Example
    
    const ABM_GETTASKBARPOS = 5;
    const APPBARDATA = Struct({
      cbSize: ref.types.uint32,
      hWnd: ref.types.long,
      uCallbackMessage: ref.types.uint32,
      uEdge: ref.types.uint32,
      rc: RECT,
      lParam: ref.types.int64,
    });
    
    const shell32 = ffi.Library('shell32.dll', {
      SHAppBarMessage: ['long', ['int', ref.refType(APPBARDATA)]], // APPBARDATA_Struct -> ref.refType(APPBARDATA_Struct) or 'pointer'
    });
    
    const data = new APPBARDATA();
    data.cbSize = APPBARDATA.size;
    
    console.log(shell32.SHAppBarMessage(ABM_GETTASKBARPOS, data.ref()));
    
    console.log('cbSize', data.cbSize);
    console.log('hWnd', data.hWnd);
    console.log('uCallbackMessage', data.uCallbackMessage);
    console.log('uEdge', data.uEdge);
    console.log('rc.left', data.rc.left);
    console.log('rc.right', data.rc.right);
    console.log('rc.top', data.rc.top);
    console.log('rc.bottom', data.rc.bottom);
    console.log('lParam', data.lParam);