V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
SHF
V2EX  ›  Rust

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

  •  
  •   SHF · 2023-09-15 00:09:21 +08:00 · 1127 次点击
    这是一个创建于 419 天前的主题,其中的信息可能已经有所发展或是发生改变。

    想写一个函数获取 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)?)
    }
    
    7 条回复    2023-09-17 07:35:22 +08:00
    PTLin
        1
    PTLin  
       2023-09-15 09:34:54 +08:00
    JsString 没实现 ToNapiValue 所以不能返回,Utf16String 转换也有开销,要是你想要无开销的可以自己实现个类型然后实现 ToNapiValue ,在里面调用 napi_create_string_utf16
    SHF
        2
    SHF  
    OP
       2023-09-15 13:08:44 +08:00 via Android
    @PTLin 好的谢谢,我再研究下
    PTLin
        3
    PTLin  
       2023-09-15 15:58:01 +08:00
    @SHF 我看错了,JsString 实现 ToNapiValue 了,你的代码可能是返回了 anyhow:Error 导致的。
    SHF
        4
    SHF  
    OP
       2023-09-16 23:00:28 +08:00
    已解决,参数列表里类型不能写 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
        5
    SHF  
    OP
       2023-09-16 23:00:42 +08:00
    @PTLin 解决了,看 #4
    SHF
        6
    SHF  
    OP
       2023-09-16 23:01:35 +08:00
    需要在文件前面提前导入

    use napi::Env;
    PTLin
        7
    PTLin  
       2023-09-17 07:35:22 +08:00
    @SHF 这个宏也太白痴了吧。。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4599 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 05:35 · PVG 13:35 · LAX 21:35 · JFK 00:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.