C#程序模拟给浏览器中iframe页发送鼠标点击消息

发布于:2025-06-13 ⋅ 阅读:(18) ⋅ 点赞:(0)

最近在解决一个学习视频网站全自动播放问题。该网站有多个iframe,设置了同源安全控制策略,不同网址的iframe通过postMessage交换信息。

于是,在浏览器中编写js脚本,模拟主页给视频播放页postMessage消息,解决的问题有:自动选择并播放课程、自动切换章节、自动避开中途暂停(比如:离开或隐藏播放页或浏览器,随机答问、异常处理等)、自动刷新加载与播放,等等。但在测试不同浏览器时发现,Chrome和Edge浏览器(包括QQ浏览器)首次postMessage播放消息失败,但只要点击视频播放页iframe的任意位置,再postMessage消息就可以了。测试Firefox和360浏览器时,首次postMessage消息均成功。

造成上述问题的原因可能有:浏览器与同源安全策略的相关配置,网页需要激活才能接收postMessage消息(点击网页等价激活),或其它未知原因(排除播放iframe网页未下载完的原因)。

因为浏览器控制台中的js脚本模拟鼠标消息时,必须指定一个window、document或某个元素对象。由于有同源安全控制,在主页上不能给不同源iframe发送鼠标消息(当然,可以在控制台中进入iframe页,但这个不能编程实现,且需要人工干预)。

浏览器也是一个Window程序,视界面也是一个Window窗体,网页及其中的Iframe页只是浏览器Window窗体的某些可视控件而已。于是想到一个解决办法:脱离当前浏览器,在外部的win32/win64程序中,给浏览器视频播放iframe页所在的屏幕位置发送一个鼠标点击消息。基本思路为:通过浏览器名、视频播放iframe页标题(浏览器各TAB的标题)获取视频播放浏览器的进程及其window句柄,然后给该句柄和指定屏幕坐标(必须包含在视频播放iframe页中)发送鼠标点击消息,如下是C#写的控制台程序:

[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool SetCursorPos(int X, int Y);

[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

public const int WM_LBUTTONDOWN = 0x0201;  // 鼠标左键按下
public const int WM_LBUTTONUP = 0x0202;  // 鼠标左键放开

public static void Main()
{
	string browserName = "chrome";  // 浏览器名的关键字
	string windowTitleName = "视频播放页";  // 网站TAB页标题中的关键字

	var process = GetBrowserPageProcess(browserName.ToUpper(), windowTitleName.ToUpper());
	if (process == null) return;

	int x = 300;  // Window的坐标,必须位于指定iframe中
	int y = 300;  // 浏览器滚动时,该坐标可能不在指定iframe中
	
	SetCursorPos(x, y);// 移动鼠标到屏幕指定位置(指定iframe中)
	SendMessage(process.MainWindowHandle, WM_LBUTTONDOWN | WM_LBUTTONUP, IntPtr.Zero, new IntPtr(x + (y << 16)));  // 模拟左击鼠标
}

/// <summary>
/// 2025-06-12:根据进程名ProcessName、当前激活页MainWindowTitle获取播放窗口的Window进程
/// </summary>
private static System.Diagnostics.Process GetBrowserPageProcess(string procName, string winTitle)
{
	var processes = System.Diagnostics.Process.GetProcesses();
	foreach (var process in processes)
	{
		if (process.ProcessName.ToUpper().Contains(procName) && process.MainWindowTitle.ToUpper().Contains(winTitle) == true)  // 浏览器名、当前激活的TAB的标题名(关键字)
		{
			return process;
		}
	}
	return null;
}

测试表明,运行上述程序后,鼠标定位在指定位置,Chrome和Edge两个浏览器均首次发送播放消息成功,表明上述程序的确在指定iframe上模拟了鼠标点击。

显然,上述方法还可以应用于给浏览器指定页发送键盘和文本消息等等。此外,如果鼠标点击位置不在视频播放iframe页中,该鼠标点击消息将被浏览器的主页或其中的其它iframe页捕获。

除此之外,笔者还找到一个网页给window程序传递消息的办法,window程序可以获取网页的document.title,从而实现网页到window程序的单向通信。当然,还可以通过Clipboard等技术实现两者的双向通信,但Clipboard可能受到某些浏览器的限制使用(安全原因)。