Skip to content
This repository has been archived by the owner on Jun 23, 2023. It is now read-only.

如何调用酷Q插件事件

Mr.喜 edited this page Aug 16, 2020 · 2 revisions

酷Q规定,插件dll均需要使用stdcall的形式来暴露各个事件的接口。所以作为启动器的此程序,需要分发事件之后调用这些dll的事件即可

如何查看dll供使用的接口有哪些

  • 插件同名json的json的event字段中保存了此dll使用的所有事件id以及函数名称
  • 使用外部程序
  1. 各种查壳工具 ExeInfoPe、PEId等 查壳预览
  2. DllExport 命令行:.\DllExport -pe-exp-list **.dll DllExport

如何查看某个函数的参数表

参考酷Q官方的易语言插件demo,没有保留的可以来这里下载
image
可以看到这个私聊函数需要传递五个参数

动态调用外部dll函数代码示例(C#)

using Launcher.Sdk.Cqp.Enum;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;

namespace DllDemo
{
    public class Dll
    {
        [DllImport("kernel32.dll")]
        private extern static IntPtr LoadLibrary(String path);
        [DllImport("kernel32.dll")]
        private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);
        [DllImport("kernel32.dll")]
        public extern static int GetLastError();

        private IntPtr hLib;
        private delegate int Type_PrivateMsg(int subType, int msgId, long fromQQ, IntPtr msg, int font);//这里提供的参数表需要与要调用函数的参数表完全一致
        private Type_PrivateMsg PrivateMsg;

        public IntPtr Load(string filepath, JObject json)
        {
            if (hLib == null || hLib == (IntPtr)0)
            {
                try { hLib = LoadLibrary(filepath); }
                catch
                {
                    Console.WriteLine($"Error,插件 {filepath} 载入失败,LoadLibrary :GetLastError={GetLastError()}");//0表示正常,126表示缺少依赖等等
                }
            }
            if (hLib != (IntPtr)0)
            {
                PrivateMsg = (Type_PrivateMsg)Invoke("_eventPrivateMsg", typeof(Type_PrivateMsg));//使用特定的函数名寻找函数,这个名字可以使用变量实现动态变化
                return hLib;
            }
            return (IntPtr)0;
        }
        //将要执行的函数转换为委托
        public Delegate Invoke(string APIName, Type t)
        {
            IntPtr api = GetProcAddress(hLib, APIName);
            if (api == (IntPtr)0)
                return null;
            return Marshal.GetDelegateForFunctionPointer(api, t);
        }

        //调用库中事件
        public int CallFunction(string ApiName, params object[] args)//外部可以通过这个函数完成对特定函数的调用
        {
            switch (ApiName)
            {
                case "PrivateMsg":
                    return PrivateMsg(Convert.ToInt32(args[0]), Convert.ToInt32(args[1]), Convert.ToInt64(args[2]), (IntPtr)args[3], 1);
                default:
                    Console.WriteLine("未找到或者未实现的事件");
                    return -1;
            }
        }
    }
}

注意事项

DLLExport在传递string参数时,需要使用指针也就是IntPtr来进行传递 二者转换的方法如下:

  • string转换IntPtr: IntPtr ptr = Marshal.StringToHGlobalAnsi(str);
  • IntPtr转换string: string str = Marshal.PtrToStringAnsi(ptr);