使用C# CEFSharp在WPF中开发桌面程序实现同一网站多开功能

在网络商业运营领域,同时运营多个淘宝店铺的现象屡见不鲜。为了满足这一需求,实现同一网址的多开功能变得尤为关键。这一需求虽然实用,但实现起来却面临诸多挑战。在这个过程中,技术人员们也经历了不少喜怒哀乐。

开发经历回顾

阿峰,今年44岁,是个久未涉足桌面软件开发的程序员。自2018年那次经历后,他最近又接手了一个WPF项目,任务是实现网址的多开功能。回想起那时候,从接到任务的那一刻起,他就感受到了满满的未知和挑战。一开始,看到官方提供的示例,他以为成功在望,因为示例中不同网站可以互不干扰。然而,实际操作起来,问题却一个接一个。

开发这一过程,会遇到不少代码难题。由于缺乏相关经验,诸多事项需从头开始探索。在搜集资料、修正代码的过程中,时间悄然流逝,精力也在持续消耗。

搜索与趟坑

初次搜索解决方案时,搜索结果呈现了答案,但当时并未意识到这些信息的价值。经过一番曲折,才意识到先前忽略的部分才是最珍贵的。在探索过程中,不断跨越一个又一个代码难题。有时是逻辑上的错误,有时是语法上的小疏忽,这些错误耗费了我大量时间去排查。

每次以为找到了对策,结果操作时却问题频发,挫败感不断累积。于是,我调整心态,重新寻找出错的地方,反复尝试各种可能性。

关键实现

直接引入了必要的核心代码。代码页面涵盖了点击切换浏览器代码以及主程序启动时的初始化代码等。每一行代码都凝聚了多次试验的成果。编写这些代码时,必须保证各项功能的正常运作,并持续检验其稳定性。

cs 复制代码
/// 

        /// 初始化浏览器集合
        /// 

        public void InitDictionary()
        {
            foreach (ChatUser user in LocalAccount)
            {
                if (!Dict.ContainsKey(user.id))
                {
                    #region 初始化ChromiumWebBrowser
                    string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Cache\Cache_" + user.id.ToString());
                    if (!Directory.Exists(path))
                        Directory.CreateDirectory(path);
                    var setting = new RequestContextSettings()
                    {
                        CachePath = path,
                        PersistSessionCookies = true,
                        PersistUserPreferences = true
                    };
                    var context = new RequestContext(setting);
                    var cookieManager = context.GetCookieManager(null);
                    //这样设置的cookie不是全局的,只有当前browser才能访问
                    cookieManager.SetCookie("dxl.cn", new Cookie
                    {
                        Name = $"cookie{user.nickname}",
                        Value = $"cookievalue{user.nickname}",
                        Path = path
                    });
                    ChromiumWebBrowser chromeBrowser = new ChromiumWebBrowser()
                    {
                        Address = config.AppSettings.Settings["ChatUrl"].Value,
                        RequestContext = context,
                        MenuHandler = new NullMenuHandler(),
                        LifeSpanHandler = new LifeSpanHandler(),
                        Visibility = Visibility.Hidden
                    };
                    chromeBrowser.JavascriptObjectRepository.Settings.LegacyBindingEnabled = true;
                    chromeBrowser.JavascriptObjectRepository.Register("bound", obj, false);
                    #endregion
                    obj.WebBrowserCallback += Obj_WebBrowserCallback;
                    user.msgNum = null;
                    chromeBrowser.LoadUrl(config.AppSettings.Settings["ChatUrl"].Value.Replace("login", "sso") + $"?token={user.token}");
                    Dict.Add(user.id, chromeBrowser);
                    mainWindow.MainGrid.Children.Add(chromeBrowser);
                }
            }
            AccountCountText = $"{LocalAccount.Count}/{config.AppSettings.Settings["MaxAccountCount"].Value}";
            if (AccountPlusVisibility != Visibility.Visible)
            {
                AccountPlusVisibility = Visibility.Hidden;
            }
        }

精心编排代码逻辑,努力优化代码架构,目的都是为了使程序运行得更加出色。即便如此,即便在取得一定进展后,仍然面临着更多挑战。

cs 复制代码
<Border Grid.Row="1" BorderBrush="Transparent" Background="#f0f0f0" BorderThickness="0" >
<Grid x:Name="MainGrid"/>
            

优化程序

WPF的数据驱动挺受欢迎,而且对高DPI设备的支持也不错。但最初打包后的文件有300多兆,老板对此难以接受。于是开始了漫长的优化过程。经过不懈努力,文件大小被压缩到了100兆以下。在这一过程中,我们不断尝试新的方法,对程序进行了精简。

cs 复制代码
MainGrid就是多个ChromeBrowser的父容器,在切换账户的使用显示当前账户对应的ChromeBrowser其他的隐藏。
cs 复制代码
public void ChangeBrowser(object xaml_obj)
        {
            var array = xaml_obj as object[];
            if (array != null && array.Length == 2)
            {
                Grid mainGrid = array[0] as Grid;
                ChatUser user = array[1] as ChatUser;
                if (user != null)
                {
                    CurrentBrowser = Dict[user.id];
                    foreach (var chromeBrowser in mainGrid.Children)
                    {
                        if (chromeBrowser == CurrentBrowser)
                        {
                            ((ChromiumWebBrowser)chromeBrowser).Visibility = Visibility.Visible;
                        }
                        else
                        {
                            ((ChromiumWebBrowser)chromeBrowser).Visibility = Visibility.Hidden;
                        }
                    }
                }
            }
        }
cs 复制代码
  /// 

        /// Cef 参数
        /// 

        /// 
        public static CefSettings GetCefSettings()
        {
            var settings = new CefSettings
            {
                Locale = "zh-CN",
                AcceptLanguageList = "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
                RemoteDebuggingPort = 8088,
                LogSeverity = LogSeverity.Disable,
                PersistSessionCookies = true,//Persistent Cookies会被保存在一个浏览器的一个子文件夹中
                UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36",
                IgnoreCertificateErrors = true,
                MultiThreadedMessageLoop = true,//让浏览器的消息循环在一个单独的线程中执行,建议设置成true,具体含义看看浏览器消息处理。
                WindowlessRenderingEnabled = true,//如果不开启好多用那种JSUI的控件的网站 都有问题
                CachePath = AppDomain.CurrentDomain.BaseDirectory + "/Cache",//缓存目录
            };
            settings.CefCommandLineArgs.Add("disable-application-cache", "1");//不要缓存
            settings.CefCommandLineArgs.Add("disable-gpu", "1");//去掉gpu,否则chrome显示有问题
            settings.CefCommandLineArgs.Add("enable-media-stream", "1"); //Enable WebRTC
            settings.CefCommandLineArgs.Add("proxy-auto-detect", "0");
            settings.CefCommandLineArgs.Add("no-proxy-server", "1");
            settings.CefCommandLineArgs.Add("allow-running-insecure-content", "1");
            //settings.CefCommandLineArgs.Add("ppapi-flash-version", "34.0.0.118"); //设置flash插件版本
            //string flashFilePath = Application.StartupPath + @"Pluginspepflashplayer.dll";
            //settings.CefCommandLineArgs.Add("ppapi-flash-path", flashFilePath);
            //settings.CefCommandLineArgs.Add("enable-system-flash", "1");
            settings.CefCommandLineArgs.Add("enable-npapi", "1");
            settings.CefCommandLineArgs.Add("ppapi-out-of-process", "1");
            settings.CefCommandLineArgs.Add("plugin-policy", "allow");

            return settings;
        }
相关推荐
程序喵大人40 分钟前
【C++入门】编译链接模型 - 07 链接器怎样把符号对上号
开发语言·c++·编译器·编译链接·链接器
微三云 - 廖会灵 (私域系统开发)1 小时前
供应链系统实战:多渠道订单聚合与分布式库存一致性方案总结
开发语言·分布式·php
学逆向的1 小时前
C 数据溢出与精度丢失
c语言·开发语言·网络安全
霸道流氓气质2 小时前
AgentFlow 入门与实战:Task-Based 并发、EWMA 动态调度与四道门禁的 Java 生产级编排
java·开发语言
释厄6232 小时前
错误无误公理——错误正确本同源都迭代定律——逆转有价值
android·开发语言·kotlin
2601_962885723 小时前
如何用 Python计算 CR能量指标(中间意愿指标)?
开发语言·python
临床数据科学和人工智能兴趣组3 小时前
在R语言的生态系统中,字符串处理一直是一个重要且复杂的任务
开发语言·正则表达式·r语言·r语言-4.2.1
蜗牛互联网3 小时前
Python调用Gemini Structured Outputs实现工单路由门禁
java·开发语言·人工智能·python
SEO_juper3 小时前
给网页做可引用性评分:判断内容够不够让 AI 引用(GEO 工具,附代码)
开发语言·人工智能·爬虫·python·seo·独立站
Wang's Blog4 小时前
Java 项目实战: 交友项目 - 产品定位、功能模块与客户端原型解读
java·开发语言·交友