[rust] 用 napi-rs 这个库写 nodejs addon 获取 windows 进程命令行, 如何在函数中直接返回 JsString

238 天前
 SHF

想写一个函数获取 windows 某个进程的命令行

得到了 cmdline 变量,是一个 Vec<u16>,想直接通过 Ok(env.create_string_utf16(&cmdline)?) 这样返回,但是 [napi] 宏展开报错了

error[E0277]: the trait bound `Env: NapiValue` is not satisfied
  --> lib/addon/addon.rs:15:1
   |
15 | #[napi]
   | ^^^^^^^ the trait `NapiValue` is not implemented for `Env`
   |
   = help: the following other types implement trait `NapiValue`:
             JsArrayBuffer
             JsTypedArray
             JsDataView
             JsBuffer
             Date
             JsFunction
             JsGlobal
             JsTimeout
           and 10 others
   = note: required for `Env` to implement `FromNapiValue`
   = note: this error originates in the attribute macro `napi` (in Nightly builds, run with -Z macro-backtrace for more info)

我的理解是不支持返回 JsString 这个类型?那怎么办?

下面这样写是可以的,但是效率不高,这里会把 utf-16 decode 成 utf-8, 然后 node.js 里面又要把 utf-8 转为 utf-16, 是没有必要的开销

pub fn get_process_cmdline (env: napi::Env, pid: u32) -> anyhow::Result<String> { ...

Ok(String::from_utf16_lossy(&cmdline));

}

use std::{ mem, ptr, mem::transmute, slice, ffi::c_void };

use napi_derive::napi;
use napi::{ bindgen_prelude::Utf16String, JsString };

use anyhow::format_err;

use windows::core::s;


#[napi]
pub fn get_process_cmdline (env: napi::Env, pid: u32) -> anyhow::Result<JsString> {
    use windows::{
        Win32::{ System::{ 
            Threading::{
                OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_BASIC_INFORMATION, PROCESS_VM_READ, RTL_USER_PROCESS_PARAMETERS
            },
            Diagnostics::Debug::ReadProcessMemory,
        },},
        Wdk::System::Threading::{ NtQueryInformationProcess, ProcessBasicInformation }
    };
    
    let handle = unsafe { OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid) }?;
    
    // https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
    let mut pbi: PROCESS_BASIC_INFORMATION = Default::default();
    let status = unsafe { NtQueryInformationProcess(
        handle,
        ProcessBasicInformation,
        &mut pbi as *mut _ as *mut c_void,
        mem::size_of_val(&pbi) as u32, 
        ptr::null_mut())}?;
    
    // ReadProcessMemory(进程 handle, 要读取的内存地址, 输出 buffer, 要读取的字节数, 已读取的字节数 (可选, 传入指针))
    // https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-readprocessmemory
    // 将 pbi.PebBaseAddress 读到 peb 中
    let mut peb: RTL_USER_PROCESS_PARAMETERS = Default::default();
    unsafe { ReadProcessMemory(
        handle, 
        pbi.PebBaseAddress as *mut c_void, 
        &mut peb as *mut _ as *mut c_void,
        mem::size_of_val(&peb),
        None) }?;
    
    let mut cmdline = vec![0u16; (peb.CommandLine.Length / 2) as usize];
    unsafe { ReadProcessMemory(
        handle, 
        peb.CommandLine.Buffer.as_ptr() as *mut c_void,
        cmdline.as_mut_ptr() as *mut _, 
        peb.CommandLine.Length as usize, 
        None) }?;
    
    // 这里会把 utf-16 decode 成 utf-8, 然后 node.js 里面又要把 utf-8 转为 utf-16, 是没有必要的开销
    // Ok(String::from_utf16_lossy(&cmdline));
    
    // 想这样直接 return utf16 string
    Ok(env.create_string_utf16(&cmdline)?)
}
908 次点击
所在节点    Rust
7 条回复
PTLin
237 天前
JsString 没实现 ToNapiValue 所以不能返回,Utf16String 转换也有开销,要是你想要无开销的可以自己实现个类型然后实现 ToNapiValue ,在里面调用 napi_create_string_utf16
SHF
237 天前
@PTLin 好的谢谢,我再研究下
PTLin
237 天前
@SHF 我看错了,JsString 实现 ToNapiValue 了,你的代码可能是返回了 anyhow:Error 导致的。
SHF
236 天前
已解决,参数列表里类型不能写 env: napi::Env, 否则宏展开会报错,直接写 Env 就可以了,现在功能已经完美可用

```rust
#[napi]
pub fn get_process_cmdline (env: Env, pid: u32) -> anyhow::Result<JsString> {
...

Ok(env.create_string_utf16(&cmdline)?)
}
```
SHF
236 天前
@PTLin 解决了,看 #4
SHF
236 天前
需要在文件前面提前导入

use napi::Env;
PTLin
235 天前
@SHF 这个宏也太白痴了吧。。。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/973900

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX