using System; using System.Collections.Generic; using System.Collections.Concurrent; using System.Runtime.InteropServices; using UnityEngine; using LitJson; namespace WeChatWASM { #region Message Protocol Models /// /// PC高性能方案通信协议 - 请求消息 /// C# -> DLL -> 内核 -> 基础库 /// [Serializable] public class PCHPRequestMessage { /// /// 消息类型: "request" | "event_register" | "event_unregister" /// public string type; /// /// 请求ID,用于匹配回调 /// public string requestId; /// /// API名称,如 "showToast", "login" 等 /// public string api; /// /// API参数,JSON格式 /// public string data; /// /// 时间戳 /// public long timestamp; } /// /// PC高性能方案通信协议 - 响应消息 /// 基础库 -> 内核 -> DLL -> C# /// [Serializable] public class PCHPResponseMessage { /// /// 消息类型: "response" | "event" /// public string type; /// /// 请求ID,与请求消息匹配 /// public string requestId; /// /// 回调类型: "success" | "fail" | "complete" /// public string callbackType; /// /// API名称(事件类型时使用) /// public string api; /// /// 响应数据,JSON格式 /// public string data; /// /// 错误信息(失败时) /// public string errMsg; /// /// 时间戳 /// public long timestamp; } /// /// 通用回调结果 /// [Serializable] public class PCHPGeneralCallbackResult { public string errMsg; } /// /// ShowToast 参数 /// [Serializable] public class PCHPShowToastOption { public string title; public string icon; public string image; public int duration; public bool mask; } /// /// ShowModal 参数 /// [Serializable] public class PCHPShowModalOption { public string title; public string content; public bool showCancel; public string cancelText; public string cancelColor; public string confirmText; public string confirmColor; public bool editable; public string placeholderText; } /// /// ShowModal 成功回调结果 /// [Serializable] public class PCHPShowModalSuccessCallbackResult { public bool confirm; public bool cancel; public string content; public string errMsg; } #endregion /// /// PC高性能小游戏初始化脚本 /// 负责与宿主程序的 direct_applet_sdk.dll 进行交互 /// public class WXPCHPInitScript : MonoBehaviour { #region DLL Imports private const string DLL_NAME = "direct_applet_sdk.dll"; // 初始化SDK [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] private static extern bool InitEmbeddedGameSDK(); // 注册异步消息处理器 [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] private static extern void RegisterAsyncMsgHandler(AsyncMsgHandlerDelegate handler); // 建立Mojo连接 [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] private static extern bool EstablishConnection(); // 初始化游戏窗口 - 传入窗口句柄 [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] private static extern bool InitGameWindow(ulong hwnd); // 异步发送消息 [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] private static extern bool SendMsgAsync(IntPtr data, int len); // 清理资源 [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] private static extern bool Cleanup(); // 获取当前活动窗口句柄 [DllImport("user32.dll")] private static extern IntPtr GetActiveWindow(); // Windows MessageBox [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type); #endregion #region Delegate Definition // 异步消息处理器委托 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void AsyncMsgHandlerDelegate(IntPtr data, int len); // 保持委托引用,防止被GC回收 private static AsyncMsgHandlerDelegate asyncMsgHandler; #endregion #region Singleton private static WXPCHPInitScript instance; public static WXPCHPInitScript Instance => instance; #endregion #region Callback Management /// /// 回调信息封装 /// private class CallbackInfo { public Action OnSuccess; public Action OnFail; public Action OnComplete; public string ApiName; public long Timestamp; } // 待处理的回调字典 private readonly Dictionary _pendingCallbacks = new Dictionary(); // 事件监听器字典 >> private readonly Dictionary>> _eventListeners = new Dictionary>>(); // 请求ID计数器 private int _requestIdCounter = 0; // 线程安全的消息队列,用于主线程处理 private readonly ConcurrentQueue _messageQueue = new ConcurrentQueue(); #endregion #region Events // 收到异步消息时触发的事件(原始字节) public event Action OnMessageReceived; #endregion #region Properties // SDK是否已初始化 public bool IsInitialized { get; private set; } // 是否已连接 public bool IsConnected { get; private set; } // 窗口句柄 public IntPtr WindowHandle { get; private set; } #endregion #region Unity Lifecycle private void Awake() { Debug.Log("[WXPCHPInitScript] ========== Awake 被调用 =========="); Debug.Log($"[WXPCHPInitScript] GameObject 名称: {gameObject.name}"); Debug.Log($"[WXPCHPInitScript] 场景名称: {UnityEngine.SceneManagement.SceneManager.GetActiveScene().name}"); if (instance != null && instance != this) { Debug.LogWarning("[WXPCHPInitScript] 检测到重复实例,销毁当前对象"); Destroy(gameObject); return; } instance = this; DontDestroyOnLoad(gameObject); Debug.Log("[WXPCHPInitScript] 单例创建成功,已设置 DontDestroyOnLoad"); // 初始化SDK Initialize(); } private void Update() { // 在主线程中处理消息队列 ProcessMessageQueue(); } private void OnDestroy() { if (instance == this) { CleanupSDK(); instance = null; } } private void OnApplicationQuit() { CleanupSDK(); } #endregion #region Public Methods - SDK Lifecycle /// /// 初始化SDK并建立连接 /// public void Initialize() { if (IsInitialized) { Debug.LogWarning("[WXPCHPInitScript] SDK已经初始化"); return; } Debug.Log("[WXPCHPInitScript] ========== 开始初始化 =========="); Debug.Log($"[WXPCHPInitScript] 当前工作目录: {System.IO.Directory.GetCurrentDirectory()}"); Debug.Log($"[WXPCHPInitScript] DLL 搜索路径: {DLL_NAME}"); ShowStepInfo("SDK 初始化开始", "即将执行 PC 高性能模式 SDK 初始化流程...\n\n共 5 个步骤:\n1. InitEmbeddedGameSDK\n2. RegisterAsyncMsgHandler\n3. EstablishConnection\n4. GetActiveWindow\n5. InitGameWindow"); try { // 1. 初始化SDK Debug.Log("[WXPCHPInitScript] Step 1: 调用 InitEmbeddedGameSDK"); ShowStepInfo("步骤 1/5 - InitEmbeddedGameSDK", "正在初始化嵌入式游戏 SDK..."); if (!InitEmbeddedGameSDK()) { ShowError("InitEmbeddedGameSDK 返回 false"); return; } ShowStepInfo("步骤 1/5 - InitEmbeddedGameSDK ✅", "InitEmbeddedGameSDK 调用成功!"); // 2. 注册消息处理器 Debug.Log("[WXPCHPInitScript] Step 2: 调用 RegisterAsyncMsgHandler"); ShowStepInfo("步骤 2/5 - RegisterAsyncMsgHandler", "正在注册异步消息处理器..."); asyncMsgHandler = HandleAsyncMessage; RegisterAsyncMsgHandler(asyncMsgHandler); ShowStepInfo("步骤 2/5 - RegisterAsyncMsgHandler ✅", "异步消息处理器注册成功!"); // 3. 建立连接 Debug.Log("[WXPCHPInitScript] Step 3: 调用 EstablishConnection"); ShowStepInfo("步骤 3/5 - EstablishConnection", "正在建立 Mojo 连接..."); if (!EstablishConnection()) { ShowError("EstablishConnection 返回 false"); IsConnected = false; return; } IsConnected = true; ShowStepInfo("步骤 3/5 - EstablishConnection ✅", "Mojo 连接建立成功!"); // 4. 获取窗口句柄并初始化游戏窗口 Debug.Log("[WXPCHPInitScript] Step 4: 获取窗口句柄"); ShowStepInfo("步骤 4/5 - GetActiveWindow", "正在获取游戏窗口句柄..."); WindowHandle = GetActiveWindow(); if (WindowHandle == IntPtr.Zero) { ShowError("GetActiveWindow 返回空句柄"); return; } Debug.Log($"获取窗口句柄成功: 0x{WindowHandle.ToInt64():X}"); ShowStepInfo("步骤 4/5 - GetActiveWindow ✅", $"窗口句柄获取成功: 0x{WindowHandle.ToInt64():X}"); // 5. 通知内核获取窗口句柄 Debug.Log("[WXPCHPInitScript] Step 5: 调用 InitGameWindow"); ShowStepInfo("步骤 5/5 - InitGameWindow", $"正在初始化游戏窗口...\n窗口句柄: 0x{WindowHandle.ToInt64():X}"); if (!InitGameWindow((ulong)WindowHandle.ToInt64())) { ShowError("InitGameWindow 返回 false"); return; } ShowStepInfo("步骤 5/5 - InitGameWindow ✅", "游戏窗口初始化成功!"); IsInitialized = true; Debug.Log("[WXPCHPInitScript] ========== 初始化完成 =========="); ShowStepInfo("🎉 SDK 初始化完成", "PC 高性能模式 SDK 所有步骤均已成功完成!\n\n✅ InitEmbeddedGameSDK\n✅ RegisterAsyncMsgHandler\n✅ EstablishConnection\n✅ GetActiveWindow\n✅ InitGameWindow"); } catch (DllNotFoundException e) { ShowError($"找不到DLL: {e.Message}\n\n请确保 {DLL_NAME} 在以下位置之一:\n- 与 .exe 同级目录\n- System32 目录\n- PATH 环境变量包含的路径"); Debug.LogError($"[WXPCHPInitScript] DLL 加载失败,请确保 {DLL_NAME} 在以下位置之一:"); Debug.LogError($" - 与 .exe 同级目录"); Debug.LogError($" - System32 目录"); Debug.LogError($" - PATH 环境变量包含的路径"); } catch (EntryPointNotFoundException e) { ShowError($"找不到函数入口: {e.Message}\n\n可能是 DLL 版本不匹配"); Debug.LogError($"[WXPCHPInitScript] 函数入口点错误,可能是 DLL 版本不匹配"); } catch (Exception e) { ShowError($"初始化异常: {e.Message}\n{e.StackTrace}"); Debug.LogError($"[WXPCHPInitScript] 未知异常: {e}"); } } #endregion #region Public Methods - WX API Calls /// /// 调用微信API(通用方法) /// /// API名称,如 "showToast" /// API参数对象 /// 成功回调 /// 失败回调 /// 完成回调 /// 请求ID public string CallWXAPI(string apiName, object data, Action onSuccess = null, Action onFail = null, Action onComplete = null) { if (!IsInitialized || !IsConnected) { Debug.LogWarning($"[WXPCHPInitScript] SDK未初始化或未连接,无法调用 {apiName}"); onFail?.Invoke(JsonMapper.ToJson(new PCHPGeneralCallbackResult { errMsg = "SDK not initialized" })); onComplete?.Invoke(JsonMapper.ToJson(new PCHPGeneralCallbackResult { errMsg = "SDK not initialized" })); return null; } string requestId = GenerateRequestId(); string dataJson = data != null ? JsonMapper.ToJson(data) : "{}"; // 注册回调 var callbackInfo = new CallbackInfo { OnSuccess = onSuccess, OnFail = onFail, OnComplete = onComplete, ApiName = apiName, Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }; _pendingCallbacks[requestId] = callbackInfo; // 构建请求消息 var request = new PCHPRequestMessage { type = "request", requestId = requestId, api = apiName, data = dataJson, timestamp = callbackInfo.Timestamp }; string requestJson = JsonMapper.ToJson(request); Debug.Log($"[WXPCHPInitScript] 发送API请求: {apiName}, requestId: {requestId}"); if (!SendMessageInternal(requestJson)) { _pendingCallbacks.Remove(requestId); onFail?.Invoke(JsonMapper.ToJson(new PCHPGeneralCallbackResult { errMsg = "Failed to send message" })); onComplete?.Invoke(JsonMapper.ToJson(new PCHPGeneralCallbackResult { errMsg = "Failed to send message" })); return null; } return requestId; } /// /// 显示消息提示框 /// public void ShowToast(PCHPShowToastOption option, Action success = null, Action fail = null, Action complete = null) { CallWXAPI("showToast", option, res => success?.Invoke(JsonMapper.ToObject(res)), res => fail?.Invoke(JsonMapper.ToObject(res)), res => complete?.Invoke(JsonMapper.ToObject(res)) ); } /// /// 隐藏消息提示框 /// public void HideToast(Action success = null, Action fail = null, Action complete = null) { CallWXAPI("hideToast", null, res => success?.Invoke(JsonMapper.ToObject(res)), res => fail?.Invoke(JsonMapper.ToObject(res)), res => complete?.Invoke(JsonMapper.ToObject(res)) ); } /// /// 显示模态对话框 /// public void ShowModal(PCHPShowModalOption option, Action success = null, Action fail = null, Action complete = null) { CallWXAPI("showModal", option, res => success?.Invoke(JsonMapper.ToObject(res)), res => fail?.Invoke(JsonMapper.ToObject(res)), res => complete?.Invoke(JsonMapper.ToObject(res)) ); } /// /// 显示 loading 提示框 /// public void ShowLoading(string title, bool mask = false, Action success = null, Action fail = null, Action complete = null) { CallWXAPI("showLoading", new { title, mask }, res => success?.Invoke(JsonMapper.ToObject(res)), res => fail?.Invoke(JsonMapper.ToObject(res)), res => complete?.Invoke(JsonMapper.ToObject(res)) ); } /// /// 隐藏 loading 提示框 /// public void HideLoading(Action success = null, Action fail = null, Action complete = null) { CallWXAPI("hideLoading", null, res => success?.Invoke(JsonMapper.ToObject(res)), res => fail?.Invoke(JsonMapper.ToObject(res)), res => complete?.Invoke(JsonMapper.ToObject(res)) ); } #endregion #region Public Methods - Event Listeners /// /// 注册事件监听器 /// /// 事件名称,如 "onShow", "onHide" /// 回调函数 public void RegisterEventListener(string eventName, Action callback) { if (!_eventListeners.ContainsKey(eventName)) { _eventListeners[eventName] = new List>(); // 发送事件注册消息到基础库 var request = new PCHPRequestMessage { type = "event_register", requestId = GenerateRequestId(), api = eventName, data = "{}", timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }; SendMessageInternal(JsonMapper.ToJson(request)); } _eventListeners[eventName].Add(callback); Debug.Log($"[WXPCHPInitScript] 注册事件监听: {eventName}"); } /// /// 移除事件监听器 /// /// 事件名称 /// 要移除的回调函数,为null则移除所有 public void UnregisterEventListener(string eventName, Action callback = null) { if (!_eventListeners.ContainsKey(eventName)) { return; } if (callback == null) { _eventListeners.Remove(eventName); } else { _eventListeners[eventName].Remove(callback); if (_eventListeners[eventName].Count == 0) { _eventListeners.Remove(eventName); } } // 如果没有监听器了,通知基础库取消注册 if (!_eventListeners.ContainsKey(eventName)) { var request = new PCHPRequestMessage { type = "event_unregister", requestId = GenerateRequestId(), api = eventName, data = "{}", timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }; SendMessageInternal(JsonMapper.ToJson(request)); } Debug.Log($"[WXPCHPInitScript] 移除事件监听: {eventName}"); } #endregion #region Public Methods - Raw Message /// /// 发送原始消息字符串 /// /// 消息内容 /// 是否发送成功 public bool SendRawMessage(string message) { return SendMessageInternal(message); } /// /// 发送原始消息字节数组 /// /// 消息数据 /// 是否发送成功 public bool SendMessage(byte[] data) { if (!IsInitialized || !IsConnected) { Debug.LogWarning("[WXPCHPInitScript] SDK未初始化或未连接"); return false; } if (data == null || data.Length == 0) { Debug.LogWarning("[WXPCHPInitScript] 发送的数据为空"); return false; } try { IntPtr ptr = Marshal.AllocHGlobal(data.Length); try { Marshal.Copy(data, 0, ptr, data.Length); return SendMsgAsync(ptr, data.Length); } finally { Marshal.FreeHGlobal(ptr); } } catch (Exception e) { Debug.LogError($"[WXPCHPInitScript] 发送消息异常: {e.Message}"); return false; } } #endregion #region Private Methods /// /// 生成唯一请求ID /// private string GenerateRequestId() { return $"pchp_{++_requestIdCounter}_{DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}"; } /// /// 内部发送消息方法 /// private bool SendMessageInternal(string message) { if (!IsInitialized || !IsConnected) { Debug.LogWarning("[WXPCHPInitScript] SDK未初始化或未连接"); return false; } try { byte[] data = System.Text.Encoding.UTF8.GetBytes(message); return SendMessage(data); } catch (Exception e) { Debug.LogError($"[WXPCHPInitScript] 发送消息异常: {e.Message}"); return false; } } /// /// 显示步骤信息弹窗(Windows 使用 MessageBox,其他平台使用 Debug.Log) /// private void ShowStepInfo(string title, string message) { Debug.Log($"[WXPCHPInitScript] [{title}] {message}"); #if UNITY_STANDALONE_WIN try { // MB_OK | MB_ICONINFORMATION = 0x40 MessageBox(IntPtr.Zero, message, $"PC高性能模式 - {title}", 0x40); } catch (System.Exception e) { Debug.LogWarning($"[WXPCHPInitScript] MessageBox 调用失败: {e.Message}"); } #endif } /// /// 显示错误弹窗(仅 Windows) /// private void ShowError(string message) { Debug.LogError($"[WXPCHPInitScript] {message}"); #if UNITY_STANDALONE_WIN try { // MB_OK | MB_ICONERROR = 0x10 MessageBox(IntPtr.Zero, message, "WXPCHPInitScript Error", 0x10); } catch (System.Exception e) { Debug.LogWarning($"[WXPCHPInitScript] MessageBox 调用失败: {e.Message}"); } #endif } /// /// 清理SDK资源 /// private void CleanupSDK() { if (!IsInitialized) { return; } try { // 清理待处理回调 _pendingCallbacks.Clear(); _eventListeners.Clear(); Cleanup(); Debug.Log("[WXPCHPInitScript] SDK清理完成"); } catch (Exception e) { Debug.LogError($"[WXPCHPInitScript] 清理异常: {e.Message}"); } finally { IsInitialized = false; IsConnected = false; } } /// /// 在主线程中处理消息队列 /// private void ProcessMessageQueue() { while (_messageQueue.TryDequeue(out var response)) { try { ProcessResponse(response); } catch (Exception e) { Debug.LogError($"[WXPCHPInitScript] 处理响应消息异常: {e.Message}"); } } } /// /// 处理响应消息 /// private void ProcessResponse(PCHPResponseMessage response) { if (response.type == "response") { // 处理API回调 if (_pendingCallbacks.TryGetValue(response.requestId, out var callbackInfo)) { Debug.Log($"[WXPCHPInitScript] 收到API响应: {callbackInfo.ApiName}, callbackType: {response.callbackType}"); switch (response.callbackType) { case "success": callbackInfo.OnSuccess?.Invoke(response.data ?? "{}"); break; case "fail": callbackInfo.OnFail?.Invoke(response.data ?? $"{{\"errMsg\":\"{response.errMsg}\"}}"); break; case "complete": callbackInfo.OnComplete?.Invoke(response.data ?? "{}"); // complete 后移除回调 _pendingCallbacks.Remove(response.requestId); break; } } else { Debug.LogWarning($"[WXPCHPInitScript] 未找到对应的回调: requestId={response.requestId}"); } } else if (response.type == "event") { // 处理事件通知 string eventName = response.api; if (_eventListeners.TryGetValue(eventName, out var listeners)) { Debug.Log($"[WXPCHPInitScript] 收到事件: {eventName}"); foreach (var listener in listeners.ToArray()) { try { listener?.Invoke(response.data ?? "{}"); } catch (Exception e) { Debug.LogError($"[WXPCHPInitScript] 事件回调异常: {eventName}, {e.Message}"); } } } } } /// /// 异步消息处理回调(从DLL回调,可能在非主线程) /// [AOT.MonoPInvokeCallback(typeof(AsyncMsgHandlerDelegate))] private static void HandleAsyncMessage(IntPtr data, int len) { if (data == IntPtr.Zero || len <= 0) { return; } try { byte[] buffer = new byte[len]; Marshal.Copy(data, buffer, 0, len); if (instance != null) { // 触发原始消息事件 instance.OnMessageReceived?.Invoke(buffer); // 解析消息 string message = System.Text.Encoding.UTF8.GetString(buffer); Debug.Log($"[WXPCHPInitScript] 收到原始消息: {message}"); try { // 尝试解析为响应消息 var response = JsonMapper.ToObject(message); if (response != null && !string.IsNullOrEmpty(response.type)) { // 加入消息队列,在主线程中处理 instance._messageQueue.Enqueue(response); } } catch (Exception parseEx) { Debug.LogWarning($"[WXPCHPInitScript] 消息解析失败,可能是非标准格式: {parseEx.Message}"); } } } catch (Exception e) { Debug.LogError($"[WXPCHPInitScript] 处理消息异常: {e.Message}"); } } #endregion } /// /// PC高性能小游戏管理器 /// 提供类似 wx.getPCHighPerformanceManager() 的接口 /// public class WXPCHighPerformanceManager { private static WXPCHighPerformanceManager _instance; private WXPCHPInitScript _initScript; /// /// 获取 PC 高性能管理器实例 /// public static WXPCHighPerformanceManager GetInstance() { if (_instance == null) { _instance = new WXPCHighPerformanceManager(); } return _instance; } private WXPCHighPerformanceManager() { _initScript = WXPCHPInitScript.Instance; } /// /// 是否支持PC高性能模式 /// public bool IsSupported => _initScript != null && _initScript.IsInitialized && _initScript.IsConnected; /// /// 调用微信API /// public string CallWXAPI(string apiName, object data, Action onSuccess = null, Action onFail = null, Action onComplete = null) { if (_initScript == null) { Debug.LogError("[WXPCHighPerformanceManager] InitScript 未初始化"); return null; } return _initScript.CallWXAPI(apiName, data, onSuccess, onFail, onComplete); } /// /// 显示 Toast /// public void ShowToast(PCHPShowToastOption option, Action success = null, Action fail = null, Action complete = null) { _initScript?.ShowToast(option, success, fail, complete); } /// /// 隐藏 Toast /// public void HideToast(Action success = null, Action fail = null, Action complete = null) { _initScript?.HideToast(success, fail, complete); } /// /// 显示模态对话框 /// public void ShowModal(PCHPShowModalOption option, Action success = null, Action fail = null, Action complete = null) { _initScript?.ShowModal(option, success, fail, complete); } /// /// 显示 Loading /// public void ShowLoading(string title, bool mask = false, Action success = null, Action fail = null, Action complete = null) { _initScript?.ShowLoading(title, mask, success, fail, complete); } /// /// 隐藏 Loading /// public void HideLoading(Action success = null, Action fail = null, Action complete = null) { _initScript?.HideLoading(success, fail, complete); } /// /// 注册事件监听 /// public void On(string eventName, Action callback) { _initScript?.RegisterEventListener(eventName, callback); } /// /// 移除事件监听 /// public void Off(string eventName, Action callback = null) { _initScript?.UnregisterEventListener(eventName, callback); } /// /// 发送原始消息 /// public bool SendRawMessage(string message) { return _initScript?.SendRawMessage(message) ?? false; } } }