Files
bansonic_beta_main/Assets/scripts/WebView/WebViewLauncher.cs
T
FloatGaming eb9b6ba78a 加入了浮动小游戏功能和设置界面
运行时请手动修改运行库目录fdBrowser
2025-12-24 06:02:06 +08:00

302 lines
9.6 KiB
C#

using System.Diagnostics;
using System.IO;
using UnityEngine;
using System;
using System.Collections;
using Debug = UnityEngine.Debug;
public class WebViewLauncher : MonoBehaviour
{
public static WebViewLauncher Instance { get; private set; }
[Header("Web Settings")]
public string startUrl = "https://www.baidu.com";
public Vector2 windowSize = new Vector2(960, 600);
public Vector2 windowPosition = new Vector2(100, 100);
[Header("Editor Only Path")]
public string editorExeRoot = @"E:\Unity\ban_total\fdBrowser";
[Header("H5 Folder Paths")]
public string h5EditorPath = @"E:\Unity\ban_total\ban_test\Bansonic_Data\StreamingAssets\h5LG";
public string officialDocsPath = "_officialDocs";
public string officialGamesPath = "_officialGames";
public string thirdPartyGamesPath = "_thirdPartyGames";
private Process webProcess;
// Event invoked when web process main window handle becomes available
public event Action WebViewReady;
void Awake()
{
if (Instance != null)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}
void Start()
{
// 扫描 H5 文件夹
string h5Folder = GetH5FolderPath();
if (!Directory.Exists(h5Folder))
{
Directory.CreateDirectory(h5Folder);
Debug.Log("Created H5 folder: " + h5Folder);
}
var h5SubFolders = ScanH5Folders(h5Folder);
if (h5SubFolders.Count > 0)
{
Debug.Log("Found H5 folders: " + string.Join(", ", h5SubFolders));
string selectedFolder = h5SubFolders[0]; // 选择第一个
startUrl = "file://" + Path.Combine(h5Folder, selectedFolder, "index.html");
Debug.Log("Selected H5 folder: " + selectedFolder + ", URL: " + startUrl);
}
else
{
Debug.LogWarning("No H5 subfolders with index.html found in: " + h5Folder);
}
LaunchWebView();
}
void OnDestroy()
{
KillWebView();
}
public void KillWebView()
{
if (webProcess != null && !webProcess.HasExited)
{
webProcess.Kill();
webProcess = null;
}
}
void LaunchWebView()
{
string exePath = GetExePath();
Debug.Log("WebView exe path: " + exePath);
if (!File.Exists(exePath))
{
Debug.LogError("fdBrowser.exe not found: " + exePath);
return;
}
// 初始参数可以给 0,后续由 PanelFollower 接管
string args = $"0 0 800 600 \"{startUrl}\"";
Debug.Log($"Launching: {exePath} {args}");
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = exePath,
Arguments = args,
UseShellExecute = true, // 改为 true 以确保 exe 能启动
WorkingDirectory = Path.GetDirectoryName(exePath)
};
try
{
webProcess = Process.Start(psi);
Debug.Log("WebView2Host started successfully. Process ID: " + webProcess.Id);
// Start coroutine to wait until main window handle is ready
StartCoroutine(WaitForMainWindowHandle());
}
catch (System.Exception e)
{
Debug.LogError("Failed to start WebView2Host: " + e.Message + "\nPath: " + exePath + "\nArgs: " + args);
}
}
IEnumerator WaitForMainWindowHandle()
{
if (webProcess == null)
yield break;
float timeout = 5f; // seconds
float elapsed = 0f;
while (elapsed < timeout)
{
try
{
webProcess.Refresh();
if (webProcess.MainWindowHandle != IntPtr.Zero)
{
Debug.Log($"WebViewLauncher: MainWindowHandle ready: 0x{webProcess.MainWindowHandle.ToInt64():X}");
WebViewReady?.Invoke();
yield break;
}
}
catch (Exception e)
{
Debug.LogWarning("WebViewLauncher.WaitForMainWindowHandle exception: " + e.Message);
}
yield return null;
elapsed += Time.deltaTime;
}
Debug.LogWarning("WebViewLauncher: timeout waiting for main window handle");
// still invoke event so follower can attempt positioning later
WebViewReady?.Invoke();
}
/// <summary>
/// 从外部请求打开一个 url。如果 webview 没有运行则启动;如果已运行则尝试通过 WM_COPYDATA 发送 url 给已有窗口,失败则重启。
/// 传入的 url 建议使用本地文件路径或以 file:// 开头的路径。
/// </summary>
/// <param name="url"></param>
public void OpenUrl(string url)
{
if (string.IsNullOrEmpty(url))
{
Debug.LogWarning("OpenUrl called with empty url");
return;
}
// Normalize to absolute file:// or keep as web URL
string final;
try
{
if (Uri.TryCreate(url, UriKind.Absolute, out var parsed) && !string.IsNullOrEmpty(parsed.Scheme))
{
// If it's already a file/http/https URI, use its AbsoluteUri
final = parsed.AbsoluteUri;
}
else
{
// Treat as local file path
string abs = Path.GetFullPath(url);
final = new Uri(abs).AbsoluteUri; // yields file:///C:/...
}
}
catch (Exception e)
{
Debug.LogWarning("OpenUrl: failed to normalize url '" + url + "' : " + e.Message);
final = url;
}
Debug.Log($"OpenUrl requested. original='{url}', normalized='{final}'");
startUrl = final;
// if process not running, launch; otherwise try to send url to existing window
if (webProcess == null || webProcess.HasExited)
{
Debug.Log("WebView process not running. Launching with URL: " + final);
LaunchWebView();
return;
}
try
{
Debug.Log($"WebView process running. PID={webProcess.Id}, MainWindowHandle=0x{webProcess.MainWindowHandle.ToInt64():X}");
}
catch { }
// 尝试通过 WM_COPYDATA 将 URL 发送给已有进程窗口(需要 fdBrowser 实现接收处理)
bool sent = WebViewWin32.TrySendUrlToWindow(webProcess, final);
Debug.Log($"TrySendUrlToWindow returned: {sent}");
if (sent)
{
Debug.Log("Sent URL to existing WebView process: " + final);
// Notify followers that webview content may have changed and they should reapply positioning
WebViewReady?.Invoke();
return;
}
// 发送失败,退回到重启进程打开 URL 的方式
Debug.Log("Failed to send URL to existing process, restarting to open: " + final);
KillWebView();
LaunchWebView();
// after relaunch, WaitForMainWindowHandle will invoke WebViewReady when handle ready
}
string GetExePath()
{
#if UNITY_EDITOR
return Path.Combine(editorExeRoot, "fdBrowser.exe");
#else
// 优先使用 StreamingAssets 路径
string streamingPath = Path.Combine(Application.streamingAssetsPath, "fdBrowser", "fdBrowser.exe");
if (File.Exists(streamingPath))
{
Debug.Log("Using StreamingAssets path: " + streamingPath);
return streamingPath;
}
// 回退到游戏根目录
string gameRoot = Path.GetDirectoryName(Application.dataPath);
string fallbackPath = Path.Combine(gameRoot, "fdBrowser", "fdBrowser.exe");
Debug.Log("Using fallback path: " + fallbackPath);
return fallbackPath;
#endif
}
string GetH5FolderPath()
{
#if UNITY_EDITOR
return h5EditorPath;
#else
return Path.Combine(Application.streamingAssetsPath, "h5LG");
#endif
}
System.Collections.Generic.List<string> ScanH5Folders(string folderPath)
{
var result = new System.Collections.Generic.List<string>();
if (!Directory.Exists(folderPath))
{
Debug.LogWarning("H5 folder does not exist: " + folderPath);
return result;
}
Debug.Log("Scanning H5 folders in: " + folderPath);
string[] targetFolders = { officialDocsPath, officialGamesPath, thirdPartyGamesPath };
foreach (string folderName in targetFolders)
{
string subFolderPath = Path.Combine(folderPath, folderName);
if (!Directory.Exists(subFolderPath))
{
Directory.CreateDirectory(subFolderPath);
Debug.Log("Created subfolder: " + subFolderPath);
}
string indexPath = Path.Combine(subFolderPath, "index.html");
if (!File.Exists(indexPath))
{
File.WriteAllText(indexPath, "<html><body><h1>" + folderName + "</h1></body></html>");
Debug.Log("Created index.html in: " + subFolderPath);
}
if (File.Exists(indexPath))
{
result.Add(folderName);
Debug.Log("Found H5 folder with index.html: " + folderName);
}
}
Debug.Log("Total H5 folders found: " + result.Count);
return result;
}
/// <summary>
/// 给 PanelFollower 调用
/// </summary>
public bool TryUpdateWebViewRect(int x, int y, int w, int h)
{
if (webProcess == null || webProcess.HasExited)
return false;
return WebViewWin32.TrySetWindowRect(webProcess, x, y, w, h);
}
}