1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > web网页浏览器唤起wpf winform exe桌面程序 并跳转到指定页面

web网页浏览器唤起wpf winform exe桌面程序 并跳转到指定页面

时间:2019-11-18 02:55:53

相关推荐

web网页浏览器唤起wpf winform exe桌面程序 并跳转到指定页面

背景案例

我们看到网页上打开百度网盘,下载的时候会通过浏览器唤起百度网盘桌面端,并进入到指定页面,我们要做的,就是达到类似的效果

实现流程

1.写注册表

在桌面软件首次启动的时候,像注册表中写入程序的相关信息(部分系统可能需要用管理员身份启动)

void RegEdit(){try{//首次运行添加注册表using (RegistryKey key = Registry.ClassesRoot.OpenSubKey(@"WebOpenShell", true)){if (key == null){using (RegistryKey myKey = Registry.ClassesRoot.CreateSubKey(@"WebOpenShell")){myKey.SetValue("URL Protocol", "");myKey.SetValue("", "URL:WebOpenShell Protocol Handler");}}else{}}//DefaultIconusing (RegistryKey key = Registry.ClassesRoot.OpenSubKey(@"WebOpenShell\DefaultIcon", true)){if (key == null){using (RegistryKey myKey = Registry.ClassesRoot.CreateSubKey(@"WebOpenShell\DefaultIcon")){myKey.SetValue("", AppDomain.CurrentDomain.BaseDirectory + @"Cloud.Test.Wpf2.exe");}}else{}}//shellusing (RegistryKey key = Registry.ClassesRoot.OpenSubKey(@"WebOpenShell\shell", true)){if (key == null){using (RegistryKey myKey = Registry.ClassesRoot.CreateSubKey(@"WebOpenShell\shell")){}}else{}}//shell\openusing (RegistryKey key = Registry.ClassesRoot.OpenSubKey(@"WebOpenShell\shell\open", true)){if (key == null){using (RegistryKey myKey = Registry.ClassesRoot.CreateSubKey(@"WebOpenShell\shell\open")){}}else{}}//shell\open\commandusing (RegistryKey key = Registry.ClassesRoot.OpenSubKey(@"WebOpenShell\shell\open\command", true)){if (key == null){using (RegistryKey myKey = Registry.ClassesRoot.CreateSubKey(@"WebOpenShell\shell\open\command")){myKey.SetValue("", AppDomain.CurrentDomain.BaseDirectory + @"Cloud.Test.Wpf2.exe");}}else{}}}catch (Exception){throw;}}

检查注册表是否写入成功

注意路径是在HKEY_LOCAL_MACHINE\SOFTWARE\Classes\下

2.网页端调用

保存此文件为html格式即可

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>系统</title></head><body><div><a href="javascript:;" id="linkTo">我是唤起桌面系统的链接</a></div><script src="/media/frontResources/images/others/jquery.min.js"></script><script src="/media/frontResources/publicResource/clipboard.min.js"></script><script>let oLink='WebOpenShell://';//私有协议let position='fabandiaodu_28'; //位置let userName='best2134';//账户名let password='123456';//密码let stationId='450102001';//密码let url="Webshell|"+userName+"|"+password+"|"+stationId+"|"+position;//全参数let clipboard= new ClipboardJS('#linkTo', {text: function(e) {return url //粘贴板返回url}});$('#linkTo').click(function(){clipboard.on('success', function(e) {window.location.href=oLink;//跳转});})</script></body></html>

到这一步,已经可以正常通过网页唤起桌面程序了

但是还有问题没有解决和优化

1.打开程序时,并没有将信息传递给程序,只是单纯的做了一个启动

2.当程序已经在运行,再次打开会导致运行多个,应该保证只有一个程序在运行,并收来自网页的信息,做业务处理

网页唤起运用程序时将信息传递给桌面程序

由于唤起是通过注册表的形式运行了桌面程序,虽然注册表可以添加启动的额外信息,但是网页并不好实现修改注册表,考虑这个问题的时候,前端组大佬突然想到拼夕夕砍一刀复制粘贴链接,进入软件自动获取到相关页面,淘宝复制链接进入软件自动跳转到物品下单,没错,就是通过粘贴板来解决这个问题

1.网页写入粘贴板

let url="Webshell|"+userName+"|"+password+"|"+stationId+"|"+position;//全参数let clipboard= new ClipboardJS('#linkTo', {text: function(e) {return url //粘贴板返回url}});$('#linkTo').click(function(){clipboard.on('success', function(e) {window.location.href=oLink;//跳转});})

2.桌面端接收粘贴板信息

软件启动第一时间,获取粘贴板中的内容,当然,这里得和前端大佬约定以下,比如获取到得信息必须是以某某某开头得,才人为是网页传过来得信息,才进行后续流程,最后,记得把粘贴板中得内容清理以下,防止用户一不小心ctrl+v就暴露了信息

//获取剪切板内容IDataObject iData = Clipboard.GetDataObject();//判断是否有Text数据格式string articleUri = null;if (iData.GetDataPresent(DataFormats.Text)){articleUri = (string)iData.GetData(DataFormats.Text);Clipboard.Clear();}

3.程序单一启动

程序单一启动的方式有很多,百度一大堆,我的做法如下:

#region 只能运行一个string pName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name.ToString();Process[] temp = Process.GetProcessesByName(pName);//在所有已启动的进程中查找需要的进程;List<Process> tempList = new List<Process>();foreach (var item in temp){tempList.Add(item);}tempList = tempList.OrderByDescending(x => x.TotalProcessorTime.Ticks).ToList();//Log.Info("多开检测:" + tempList.Count + "(" + string.Join(",", tempList.Select(x => x.TotalProcessorTime.Ticks)) + ")");if (tempList.Count > 1)//如果查找到{System.Threading.Thread.Sleep(500);//睡眠500毫秒 然后关闭本程序Environment.Exit(0);}else{}#endregion

设置单一启动之后,遇到一个难点,当程序已经在运行,再次启动程序,应当把网页传过来的信息传递给正在运行的程序,再把自己给杀掉(当前进程)

string pName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name.ToString();Process[] temp = Process.GetProcessesByName(pName);//在所有已启动的进程中查找需要的进程;List<Process> tempList = new List<Process>();foreach (var item in temp){tempList.Add(item);}tempList = tempList.OrderByDescending(x => x.TotalProcessorTime.Ticks).ToList();Log.Info("多开检测:"+ tempList.Count+"("+string.Join(",",tempList.Select(x=>x.TotalProcessorTime.Ticks))+")");if (tempList.Count > 1)//如果查找到{Log.Info("程序正在运行中,阻止程序多开");IntPtr handle = tempList[0].MainWindowHandle;//这是老的handletempList = null;SwitchToThisWindow(handle, true); // 激活,显示在最前 if (articleUri != null && articleUri.StartsWith("Webshell")) {byte[] sarr = System.Text.Encoding.Default.GetBytes(articleUri);int len = sarr.Length;COPYDATASTRUCT cds2;cds2.dwData = (IntPtr)0;cds2.cbData = len + 1;cds2.lpData = articleUri;// 发送消息SendMessage(handle, WM_COPYDATA, IntPtr.Zero, ref cds2);}System.Threading.Thread.Sleep(500);//睡眠500毫秒 然后关闭本程序Environment.Exit(0);return false;}else{if (articleUri != null && articleUri.StartsWith("Webshell")) {LogonFree = articleUri;}return true;}

这里用到了win32发送,接收信息

4.信息发送

const int WM_COPYDATA = 0x004A; // 固定数值,不可更改public struct COPYDATASTRUCT{public IntPtr dwData; // 任意值public int cbData; // 指定lpData内存区域的字节数[MarshalAs(UnmanagedType.LPStr)]public string lpData; // 发送给目标窗口所在进程的数据}[DllImport("user32.dll")]private static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);// 导出SendMessage函数[DllImport("User32.dll", EntryPoint = "SendMessage")]private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref COPYDATASTRUCT lParam);// 导出FindWindow函数,用于找到目标窗口所在进程[DllImport("User32.dll", EntryPoint = "FindWindow")]private static extern int FindWindow(string lpClassName, string lpWindowName);

5.信息接收

在MainWindow.xaml.cs文件中

const int WM_COPYDATA = 0x004A; // 固定数值,不可更改public struct COPYDATASTRUCT{public IntPtr dwData; // 任意值public int cbData; // 指定lpData内存区域的字节数[MarshalAs(UnmanagedType.LPStr)]public string lpData; // 发送给目标窗口所在进程的数据}protected override void OnSourceInitialized(EventArgs e){base.OnSourceInitialized(e);HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;if (hwndSource != null){IntPtr handle = hwndSource.Handle;hwndSource.AddHook(new HwndSourceHook(WndProc));}}IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled){if (msg == WM_COPYDATA){COPYDATASTRUCT cds = (COPYDATASTRUCT)Marshal.PtrToStructure(lParam,typeof(COPYDATASTRUCT)); // 接收封装的消息string rece = cds.lpData; // 获取消息内容// 自定义行为if (rece.StartsWith("Webshell")){if (rece.Contains("|")){var arr = rece.Split('|');//自动跳转到指定页面var mainView = ServiceLocator.Current.GetInstance<MainViewModel>();if (CacheHelper.ActiveStation != null && CacheHelper.ActiveStation.EnterpriseCode != null){mainView.MenuManager.JumpByCode(arr[4]);}else {ServiceLocator.Current.GetInstance<LoginViewModel>().Login(new Data.Model.LoginRequest {UserName = arr[1], Password = arr[2], StationCode = arr[3] }, arr[4]);}}}}return hwnd;}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。