276 lines
8.2 KiB
C#
276 lines
8.2 KiB
C#
// NOTE: Windows-only. Uses user32.dll P/Invoke, which does not compile on Android.
|
|
// Wrapped in UNITY_STANDALONE_WIN so the Windows build/logic is unchanged while
|
|
// Android simply compiles this class out.
|
|
#if UNITY_STANDALONE_WIN
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Runtime.InteropServices;
|
|
using UnityEngine;
|
|
|
|
public static class WebViewWin32
|
|
{
|
|
[DllImport("user32.dll")]
|
|
static extern bool SetWindowPos(
|
|
IntPtr hWnd,
|
|
IntPtr hWndInsertAfter,
|
|
int X, int Y, int cx, int cy,
|
|
uint uFlags);
|
|
|
|
[DllImport("user32.dll")]
|
|
static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
|
|
|
[DllImport("user32.dll")]
|
|
static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint);
|
|
|
|
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
|
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct RECT
|
|
{
|
|
public int Left;
|
|
public int Top;
|
|
public int Right;
|
|
public int Bottom;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct POINT
|
|
{
|
|
public int X;
|
|
public int Y;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
private struct COPYDATASTRUCT
|
|
{
|
|
public IntPtr dwData;
|
|
public int cbData;
|
|
public IntPtr lpData;
|
|
}
|
|
|
|
// SetWindowPos flags
|
|
const uint SWP_NOZORDER = 0x0004;
|
|
const uint SWP_NOACTIVATE = 0x0010;
|
|
const uint SWP_NOMOVE = 0x0002;
|
|
const uint SWP_NOSIZE = 0x0001;
|
|
|
|
// HWND insert-after special values
|
|
static readonly IntPtr HWND_TOP = new IntPtr(0);
|
|
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
|
|
static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
|
|
static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
|
|
|
|
const uint WM_COPYDATA = 0x004A;
|
|
|
|
// ShowWindow commands
|
|
const int SW_HIDE = 0;
|
|
const int SW_SHOW = 5;
|
|
const int SW_MINIMIZE = 6;
|
|
const int SW_RESTORE = 9;
|
|
|
|
public static bool TrySetWindowRect(Process process, int x, int y, int width, int height)
|
|
{
|
|
if (process == null || process.HasExited)
|
|
return false;
|
|
|
|
if (process.MainWindowHandle == IntPtr.Zero)
|
|
{
|
|
process.Refresh();
|
|
if (process.MainWindowHandle == IntPtr.Zero)
|
|
return false;
|
|
}
|
|
|
|
SetWindowPos(
|
|
process.MainWindowHandle,
|
|
IntPtr.Zero,
|
|
x,
|
|
y,
|
|
width,
|
|
height,
|
|
SWP_NOZORDER | SWP_NOACTIVATE
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Place the given process window directly above the target process window in Z-order
|
|
/// and set its position/size. This ensures the webview sits just above the Unity window.
|
|
/// </summary>
|
|
public static bool TrySetWindowRectAboveProcess(Process process, Process targetProcess, int x, int y, int width, int height)
|
|
{
|
|
if (process == null || process.HasExited || targetProcess == null || targetProcess.HasExited)
|
|
return false;
|
|
|
|
if (process.MainWindowHandle == IntPtr.Zero)
|
|
{
|
|
process.Refresh();
|
|
if (process.MainWindowHandle == IntPtr.Zero)
|
|
return false;
|
|
}
|
|
|
|
if (targetProcess.MainWindowHandle == IntPtr.Zero)
|
|
{
|
|
targetProcess.Refresh();
|
|
if (targetProcess.MainWindowHandle == IntPtr.Zero)
|
|
return false;
|
|
}
|
|
|
|
// Place process window after targetProcess window in Z-order (i.e. on top of target)
|
|
// Do not use SWP_NOZORDER so that z-order is changed.
|
|
bool ok = SetWindowPos(
|
|
process.MainWindowHandle,
|
|
targetProcess.MainWindowHandle,
|
|
x,
|
|
y,
|
|
width,
|
|
height,
|
|
SWP_NOACTIVATE
|
|
);
|
|
|
|
return ok;
|
|
}
|
|
|
|
public static bool TryGetWindowRect(Process process, out RECT rect)
|
|
{
|
|
rect = new RECT();
|
|
if (process == null || process.HasExited)
|
|
return false;
|
|
|
|
if (process.MainWindowHandle == IntPtr.Zero)
|
|
{
|
|
process.Refresh();
|
|
if (process.MainWindowHandle == IntPtr.Zero)
|
|
return false;
|
|
}
|
|
|
|
return GetWindowRect(process.MainWindowHandle, out rect);
|
|
}
|
|
|
|
public static bool TryGetClientScreenRect(Process process, out RECT rect)
|
|
{
|
|
rect = new RECT();
|
|
if (process == null || process.HasExited)
|
|
return false;
|
|
|
|
if (process.MainWindowHandle == IntPtr.Zero)
|
|
{
|
|
process.Refresh();
|
|
if (process.MainWindowHandle == IntPtr.Zero)
|
|
return false;
|
|
}
|
|
|
|
POINT point = new POINT { X = 0, Y = 0 };
|
|
if (!ClientToScreen(process.MainWindowHandle, ref point))
|
|
return false;
|
|
|
|
rect.Left = point.X;
|
|
rect.Top = point.Y;
|
|
rect.Right = point.X + UnityEngine.Screen.width;
|
|
rect.Bottom = point.Y + UnityEngine.Screen.height;
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Try to show or hide the given process main window.
|
|
/// </summary>
|
|
public static bool TryShowWindow(Process process, bool show)
|
|
{
|
|
if (process == null || process.HasExited)
|
|
return false;
|
|
|
|
if (process.MainWindowHandle == IntPtr.Zero)
|
|
{
|
|
process.Refresh();
|
|
if (process.MainWindowHandle == IntPtr.Zero)
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!show)
|
|
{
|
|
// Minimize instead of hide
|
|
return ShowWindow(process.MainWindowHandle, SW_MINIMIZE);
|
|
}
|
|
else
|
|
{
|
|
// Restore and bring to top
|
|
bool r = ShowWindow(process.MainWindowHandle, SW_RESTORE);
|
|
try
|
|
{
|
|
// bring to top without making it topmost
|
|
SetWindowPos(process.MainWindowHandle, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
}
|
|
catch { }
|
|
return r;
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
UnityEngine.Debug.LogWarning("TryShowWindow failed: " + e.Message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Documentation text normalized.
|
|
/// Documentation text normalized.
|
|
/// Documentation text normalized.
|
|
/// </summary>
|
|
public static bool TrySendUrlToWindow(Process process, string url)
|
|
{
|
|
if (process == null || process.HasExited || string.IsNullOrEmpty(url))
|
|
return false;
|
|
|
|
if (process.MainWindowHandle == IntPtr.Zero)
|
|
{
|
|
process.Refresh();
|
|
if (process.MainWindowHandle == IntPtr.Zero)
|
|
return false;
|
|
}
|
|
|
|
IntPtr hWnd = process.MainWindowHandle;
|
|
|
|
IntPtr ptrString = IntPtr.Zero;
|
|
IntPtr ptrCds = IntPtr.Zero;
|
|
try
|
|
{
|
|
// Allocate Unicode string (null-terminated)
|
|
ptrString = Marshal.StringToHGlobalUni(url);
|
|
var cds = new COPYDATASTRUCT
|
|
{
|
|
dwData = IntPtr.Zero,
|
|
cbData = (url.Length + 1) * 2,
|
|
lpData = ptrString
|
|
};
|
|
|
|
int size = Marshal.SizeOf(typeof(COPYDATASTRUCT));
|
|
ptrCds = Marshal.AllocHGlobal(size);
|
|
Marshal.StructureToPtr(cds, ptrCds, false);
|
|
|
|
IntPtr res = SendMessage(hWnd, WM_COPYDATA, IntPtr.Zero, ptrCds);
|
|
long resVal = res.ToInt64();
|
|
UnityEngine.Debug.Log($"WebViewWin32.TrySendUrlToWindow: Sent WM_COPYDATA to hWnd=0x{hWnd.ToInt64():X}, url={url}, SendMessage result={resVal}");
|
|
|
|
// Treat non-zero as handled/accepted by receiver. If receiver returns zero, consider not handled.
|
|
return resVal != 0;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
UnityEngine.Debug.LogWarning("WebViewWin32.TrySendUrlToWindow exception: " + e.Message);
|
|
return false;
|
|
}
|
|
finally
|
|
{
|
|
if (ptrCds != IntPtr.Zero) Marshal.FreeHGlobal(ptrCds);
|
|
if (ptrString != IntPtr.Zero) Marshal.FreeHGlobal(ptrString);
|
|
}
|
|
}
|
|
}
|
|
#endif
|