302 lines
9.6 KiB
C#
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);
|
|
}
|
|
}
|