Skip to content

add Java invoke sample. #11

@wjw465150

Description

@wjw465150

I implemented a Java sample!

/*
 * author: [wjw](https://github.com/wjw465150)
 * date:   2024年11月7日 14:43:06
 * note: 
 */

import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.text.MessageFormat;
import java.util.Base64;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;

public class MMMojoCallUsage {
  interface MMMojoCall extends Library {
    public static MMMojoCall MMMojoCallDLL = Native.load("./win32-x86-64/MMMojoCall.dll", MMMojoCall.class);

    /**
     * @brief 初始化 mmmojo(_64).dll
     * @param mmmojo_dll_path mmmojo(_64).dll所在路径
     * @return 是否初始化成功
     */
    boolean InitMMMojoDLLFuncs(String mmmojo_dll_path);

    /**
     * @brief 初始化全局MMMojo环境 (全局调一次) 
     * @param argc 当前进程的参数个数
     * @param argv 当前进程的参数
     * @return 是否初始化成功
     */    
    boolean InitMMMojoGlobal(int argc, String argv);

    /**
     * @brief 以纯C的方式导出XPluginMgr类 方便其他语言调用
     * @param mgr_type 0为XPluginManager, 1为OCRManager, 2为UtilityManager
     * @return XPluginMgr对象指针 失败返回NULL
     */    
    Pointer GetInstanceXPluginMgr(int mgr_type);

    /**
     * @brief 根据字符串调用相应的函数
     * @param class_ptr 类指针
     * @param mgr_type 类的类型
     * @param func_name 函数名
     * @param ret_ptr 存储返回值的指针 如果不使用返回值则传入NULL
     * @param ... 参数 一定要对应函数的正确参数 内部不做检查
     * @return 执行状态 0失败 1成功 2未知错误
     */    
    int CallFuncXPluginMgr(Pointer class_ptr, int mgr_type, String func_name, Pointer ret_ptr, Object... args);

    /**
     * @brief 销毁XPluginMgr类指针
     * @param XPluginMgr对象指针
     */
    void ReleaseInstanceXPluginMgr(Pointer mgr_ptr);

    
    /**
     * SetReadOnPush 回调函数的接口
     */
    interface SetResCallback extends Callback {
      
      /** 回调方法
       * 
       * @param pic_path 图片路径
       * @param data 数据
       * @param data_size 数据大小
       */
      void callback(String pic_path, Pointer data, int data_size);
      
      /**
       * 获取结果
       *
       * @param timeout the timeout
       * @param unit the unit
       * @return the string
       * @throws InterruptedException the interrupted exception
       * @throws ExecutionException the execution exception
       * @throws TimeoutException the timeout exception
       */
      String get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
    }
  }

  static String mmmojocallGetLastErrStr(Pointer ocrMgr) {
    Pointer retPtr = new Memory(8); // ULONGLONG的指针
    
    MMMojoCall.MMMojoCallDLL.CallFuncXPluginMgr(ocrMgr, 0, "GetLastErrStr", retPtr);
    return retPtr.getPointer(0).getString(0);
  }
  
  public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
    String wechatOcrDir = "C:\\Users\\wjw\\AppData\\Roaming\\Tencent\\WeChat\\XPlugin\\Plugins\\WeChatOCR\\7079\\extracted\\WeChatOCR.exe";
    String wechatDir = "C:\\Program Files\\Tencent\\WeChat\\[3.9.12.17]";
    String imgPath = ".\\test.png";

    //  加载mmmojo(_64).dll并获取导出函数, 只需要调用一次.
    if(!MMMojoCall.MMMojoCallDLL.InitMMMojoDLLFuncs(wechatDir)) {
      System.out.println("\033[31m[!] mmmojocall::InitMMMojoDLLFuncs ERR!\033[0m");
      System.exit(-1);
    } else {
      System.out.println("\033[31m[+] InitMMMojoDLLFuncs Over!\033[0m");
    }
    
    //  初始化MMMojo (包括ThreadPool等), 只需要调用一次. 
    MMMojoCall.MMMojoCallDLL.InitMMMojoGlobal(0, null);

    //导出 OCRManager
    Pointer ocrMgr = MMMojoCall.MMMojoCallDLL.GetInstanceXPluginMgr(1);
    try(Memory retPtr = new Memory(8) /* ULONGLONG的指针 */) {
      retPtr.clear(retPtr.size());
      
      MMMojoCall.MMMojoCallDLL.CallFuncXPluginMgr(ocrMgr, 1, "SetExePath", retPtr, wechatOcrDir);
      if(retPtr.getPointer(0) == null) {
        String errStr = mmmojocallGetLastErrStr(ocrMgr);
        System.out.println(MessageFormat.format("\033[31m[!] {0}\033[0m",errStr));
        System.exit(-1);
      }

      MMMojoCall.MMMojoCallDLL.CallFuncXPluginMgr(ocrMgr, 1, "SetUsrLibDir", retPtr, wechatDir);
      if(retPtr.getPointer(0) == null) {
        String errStr = mmmojocallGetLastErrStr(ocrMgr);
        System.out.println(MessageFormat.format("\033[31m[!] {0}\033[0m",errStr));
        System.exit(-1);
      }

      //设置传给用户回调函数的数据类型(protobuf/json)
      //use_json 如果为true则传递json字符串, 为false则传递序列化后的pb二进制数据
      MMMojoCall.MMMojoCallDLL.CallFuncXPluginMgr(ocrMgr, 1, "SetCallbackDataMode", retPtr, true);

      //设置接收结果的回调函数.
      SetResCallbackImp setResCallbackImp = new SetResCallbackImp();
      MMMojoCall.MMMojoCallDLL.CallFuncXPluginMgr(ocrMgr, 1, "SetReadOnPush", retPtr, setResCallbackImp);

      MMMojoCall.MMMojoCallDLL.CallFuncXPluginMgr(ocrMgr, 1, "StartWeChatOCR", retPtr);
      if(retPtr.getPointer(0) == null) {
        String errStr = mmmojocallGetLastErrStr(ocrMgr);
        System.out.println(MessageFormat.format("\033[31m[!] {0}\033[0m",errStr));
        System.exit(-1);
      }
      System.out.println("\033[31m[+] StartWeChatOCR OK!\033[0m");

      imgPath = Path.of(imgPath).toAbsolutePath().toString(); // 注意文件路径里不能有中文字符
      MMMojoCall.MMMojoCallDLL.CallFuncXPluginMgr(ocrMgr, 1, "DoOCRTask", retPtr, imgPath);
      if(retPtr.getPointer(0) == null) {
        String errStr = mmmojocallGetLastErrStr(ocrMgr);
        System.out.println(MessageFormat.format("\033[31m[!] {0}\033[0m",errStr));
        System.exit(-1);
      }
      
      String strOcr = setResCallbackImp.get(10, TimeUnit.SECONDS);
      System.out.println(strOcr);
      
      { // 转换成JsonObject
        JsonObject rawJsonObj = JsonParser.parseString(strOcr).getAsJsonObject();
        //System.out.println(rawJsonObj);
        JsonArray jsonArray = rawJsonObj.getAsJsonObject("ocr_result").getAsJsonArray("single_result");
        jsonArray.asList().forEach(jsonElement -> {
          String strBase64 = jsonElement.getAsJsonObject().get("single_str_utf8").getAsString();
          String strTxt = new String(Base64.getDecoder().decode(strBase64), StandardCharsets.UTF_8);
          System.out.println(strTxt);
        });
      }

      // 销毁MMMojo环境以及WeChatOCR.exe进程
      MMMojoCall.MMMojoCallDLL.CallFuncXPluginMgr(ocrMgr, 1, "KillWeChatOCR", retPtr);
    } finally {
      // 销毁XPluginMgr类指针
      MMMojoCall.MMMojoCallDLL.ReleaseInstanceXPluginMgr(ocrMgr);
    }
  }
  
  static final class SetResCallbackImp implements MMMojoCall.SetResCallback {
    CompletableFuture<String> futurePdf = new CompletableFuture<>();

    @Override
    public void callback(String pic_path, Pointer data, int data_size) {
      if (pic_path == null) {
        futurePdf.complete(null);
      } else {
        byte[] byteArray = data.getByteArray(0, data_size);
        futurePdf.complete(new String(byteArray, StandardCharsets.UTF_8));
      }
    }

    @Override
    public String get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
      return futurePdf.get(timeout, unit);
    }
  }

}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions