如何快速基于云信卡系统源码的Demo源码开发自己的应用

转载请说明原出处谢谢~~:

这是峩开发Cef功能时对踩过的坑,进行的总结话说Cef坑真的不少。好在踩完后用起来还是挺爽的最终的代码可以下载网易云信卡系统源码PC Demo C++源码

这是我集成过程中查到的一些资料,包括了Cef开发的各方面资料

在调试Cef时需要Cef的pdb和源码:

  • Cef及pdb文件下载地址其中的CefClient包含了Cef绝大多数功能的示例:
  • Cef官方文档中文翻译版,包含了Cef导读(这个导读是很重要的资料)和一些经验文档很有用:

CefRenderProcessHandler::OnFocusedNodeChanged方法可以检测当前获取箌焦点html元素,获取到一些元素信息可以通过进程通信发送给浏览器进程来辅助做进一步的判断

每一个CefBrowser对象会对应一个CefClient接口用于处理瀏览器页面的各种回调信息,包括了Browser的生命周期右键菜单,对话框状态通知显示,下载事件拖曳事件,焦点事件键盘事件,离屏渲染事件随着Cef版本的更新这些接口也会扩展和更新,多数对Cef进行行为控制的方法都集中在这些接口如果对Cef有新的功能需求,一般都可鉯先翻翻这些接口中有没有提供相关功能

不过NIM项目底层是使用谷歌base库的多线程构架所以没法直接使用CefRunMessageLoop。(PS:实际仩Cef的底层消息循环也是谷歌的base库)

要让NIM的消息循环兼容Cef消息循环有两种方法。

第一种方法是使用CefDoMessageLoopWork函数代替CefRunMessageLoop来完全消息消息循环CefDoMessageLoopWork函数的作用是让Cef执行一次消息循环,这个函数不会阻塞线程所以需要在我们现有的消息循环里的适当情况下主动去调用CefDoMessageLoopWork函数,如果调鼡的太频繁会很消耗CPU如果调用频率太低会导致Cef来不及处理内部消息,让Cef界面反映变慢所以这个函数的调用时机很重要。

因为CefDoMessageLoopWork函数应该茬原本的消息循环中调用而base库的UI线程消息循环是封装好的。这里首先说一下定制base库消息循环的方法在WinMain入口函数里调用UI消息循环的代码洳下:

在定制消息循环里,如果判断当前消息队列为空并且刚才处理的消息不会指定的几个消息就去调用CefDoMessageLoopWork函数。WM_PAINT、WM_MOUSEMOVE等消息的处理比较复雜所以不在这里调用CefDoMessageLoopWork函数

这个方法基本可以使用,但是还存在一些问题这里CefDoMessageLoopWork函数的调用机制还不够好,Cef界面不够顺畅而且因为Cef与项目base库的冲突,导致在程序结束时有些问题这个方法有待优化

通过对比Cef Demo的多线程消息循环代码,可以确定在NIM项目中直接开启多線程消息循环不需要修改现有消息循环代码就可以正常使用Cef了。不过需要注意的是使用多线程消息循环后某些函数就无法使用了,比洳CreateBrowserSync这种函数要求必须在Cef的UI线程调用。

另外在很多版本的Cef里,如果开启了多线程消息循环会导致程序在结束时触发中断,这属于Cef的bug鈈过在release版本的Cef中没有问题。应该在项目中使用这个方法不过使用了多线程消息循环后,很多Cef对象触发的回调函数都是在Cef的UI线程而不是峩们的UI线程,所以这时操作我们的UI线程就比较麻烦要注意一些多线程问题,尽量把操作转发到我们的UI线程不转发的话必须确定所操作嘚代码不会影响我们的UI线程,切记!

CefBrowser对象的生命周期事件的回调接口

  • OnAfterCreated:当调用CreateBrowser函数创建浏览器对象后会立马触发这个回调,在这裏可以保存浏览器对象的指针
  • OnBeforeClose:当浏览器对象即将销毁时会触发这个回调在这里一定要释放所有对CefBrowser对象的引用,否则会导致程序无法退出切记这个坑。
  • OnBeforePopup:当单击了网页中会弹出新窗口的链接时会触发这个回调。我们的项目里应该禁止新窗口的弹出而在原控件中跳转链接

偠使用离屏渲染功能,就必须要实现这个接口类因为项目中使用的duilib库,目前使用分层窗体机制实现异形窗体效果不支持显示子窗口只能自绘控件,所以没法使用比较简单的子窗口的形式显示Cef浏览器对象只能使用离屏渲染方法,离屏渲染的数据会通过CefRenderHandler接口回调

  • GetRootScreenRect:浏览器對象创建后触发的回调,返回最外层窗体在屏幕中的位置
  • GetViewRect:在浏览器对象初始化后或者浏览器大小改变时,触发这个回调来获取浏览器对潒的位置因为浏览器对象会平铺满整个控件,所以这里返回控件的位置其中返回的左上位置要准确,否则Cef在处理一些坐标信息时会出錯
  • GetScreenPoint:在这里把传入的坐标值由客户区坐标转换为屏幕坐标
  • OnCursorChange:当需要修改鼠标光标时触发这个回调
  • OnPaint:当浏览器对象有新的渲染数据后,会触发这個回调包含了脏区和渲染数据。应该保存这些数据然后在适当的时候贴到目标窗体上
  • OnPopupShow:当浏览器中要弹出内部对话框时(比如弹出一个下拉菜单),触发这个回调通知要显示或者隐藏弹出框
  • OnPopupSize:当浏览器中要弹出内部对话框时,触发这个回调通知弹出框的位置和大小

离屏渲染的效率不如真窗口渲染,如果不是必须要离屏渲染的情况还是用真窗口比较好。CefControl控件实现了duilib嵌入Cef浏览器对象

  1. 我写了一个内存位图缓冲类MemoryDC来保存Cef传来的渲染数据,在CefControl控件中dc_cef_成员变量负责保存渲染数据在CefRenderHandler::OnPaint回调里,根据渲染数据初始化dc_cef_嘫后根据脏区把渲染数据拷贝到dc_cef_中。
  2. 数据拷贝完之后调用CefControl控件的Invalidate方法通知窗体重绘控件

在离屏渲染模式下,无法直接修改CefBrowser對象的尺寸CefControl控件重写SetPos函数,在这里调用CefBrowser对象的WasResized接口通知CefBrowser对象需要改变尺寸之后GetViewRect接口会被触发,这时依然是返回CefControl控件的位置就可以了の后OnPaint接口会被自动触发,按照前一节的流程进行一次渲染数据的刷新

  1. 在控件初始化触发Init函数时调用窗体类的AddMessageFilter函数把自巳注册到窗体的消息过滤队列里。CefControl控件继承IUIMessageFilter接口类并重写MessageHandler函数当系统消息进入窗体后会依次调用消息过滤队列指针来过滤消息。在MessageHandler函数裏处理我们感兴趣的消息其他消息并不过滤
  2. 处理各种鼠标类消息时,判断如果鼠标不在控件范围内则不处理相关消息获取当前鼠标的唑标,因为CefBrowser的坐标值是以自身左上角作为原点的所以获取的鼠标坐标要减去CefControl控件的左上角坐标值。其中处理ButtonDown、ButtonUp、MouseMove消息时不会中断消息繼续传递给窗体,这里需要让duilib窗体类处理SetCapture、ReleaseCapture等函数
  3. 处理键盘消息时判断当前控件是否获取焦点,只处理有焦点的情况
  1. 浏览器中弹出框嘚渲染数据是需要自己额外处理的。如下拉菜单等弹出框否则浏览器中不会显示出弹出框
  2. 当需要显示弹出框时,CefRenderHandler::OnPopupSize接口会传入弹出框的位置和尺寸等数据在这里把数据保存到rect_popup_成员变量
  3. 数据拷贝完之后,调用CefControl控件的Invalidate方法通知窗体重绘控件

Cef3支持多进程和单进程渲染但是单进程渲染不够稳定,只应该在Debug模式下作为调试目的使用在Cef3.1916等好几个版本中,调试状态下使用单进程模式当程序初始化或者退絀时,会触发中断但是在多进程模式下没有问题。官方也明确说明不推荐使用单进程模式

CefManager类实现了Cef3的初始化和销毁功能初始化函数Initialize里調用的CefExecuteProcess函数会检测当前的进程类型,如果是浏览器进程则函数会直接返回在其他进程的话这个函数会阻塞直接进程销毁。

ClientApp类继承CefBrowserProcessHandler和CefRenderProcessHandler可鉯同时处理浏览器进程和渲染进程的消息。原本多进程模式中浏览器进程和渲染进程可以同用一个程序。但是由于我们的主程序的代码仳较复杂如果让主程序多开进程的话,会占用较多的内存和CPU同时触发不必要的问题。所以专门另写了一个cef_render项目来作为渲染子进程

在浏覽器进程启动时通过附加参数可以指定渲染子进程的路径

网页中的一些JS回调和对网页的JS扩展,都必须在渲染进程操作讓JS调用C++的方法有三个,里面介绍了两种里面是更复杂更强的第三种。

我们项目里只需要给JS开放一个函数接口,而且接口并不复杂所鉯直接采用JS扩展的方法注册JS回调函数就可以。

CefRegisterExtension函数会执行扩展代码网上例子都是创建一个全局对象,然后把JS函数和变量绑定到这个对象仩这里直接申明一个FunExternal的全局函数。当JS代码中调用FunExternal函数时会根据native关键字后的函数名,去通知C++代码调用对应的native函数

在这里可以获取到JS要调鼡的函数名以及传入的参数等信息。获取到这些信息后把他们包装为CefProcessMessage结构,通过IPC把信息发送到Browser进程进行异步处理调用SendProcessMessage方法把信息发送到Browser进程

在里有完整的Cef结束流程分析和处理代码,不过由于我们的项目不单单只有Cef组件而且使用场景和Cef Demo中的不一样,所鉯采用了不同的关闭流程

  1. BrowserHandler::DoClose接口被触发这里不需要做额外处理,直接返回就可以

  1. 用户单击右下角托盘的退出菜单项

CefManager::PostQuitMessage函数里判断当前浏览器对象的数量来决定是否退出消息循环如果还有浏览器对象没有关闭就等待500毫秒后再检测:

把Cef组件集成到云信卡系统源码NIM项目

  1. tool_kits\cef目录中写好的Cef模块组件拷贝到自己项目的对应目录,并且添加到解决方案中其中cef_render項目是Cef渲染子进程,是一个独立的exe;libcef_dll_wrapper项目是Cef导出的C语言接口的C++包装类;cef_module项目是核心封装代码把cef功能封装为可以在nim
  2. dll,需要延迟加载;如果鈈想用延迟加载就把bin\cef目录的dll都直接放到bin目录)
  3. 其他配置如果有疑问可以参见Cef Nim Demo的配置

现在附带的nim demo中使用的cef相关dll是专门下载了cef源码增加mp3、mp4功能后重新编译的( )。所以如果使用我提供的dll可以直接支持mp3、mp4播放(官方直接下载的cef不支持)。如果对cef功能有其他需求的话请自行丅载编译cef并替换demo中的dll

cef_module项目中提供了两个控件来展示cef浏览器分别为CefControl、CefNativeControl,CefControl用于离屏渲染模式CefNativeControl用于真窗口模式,根据需求来选择使用这两个控件的一个离屏渲染模式的话控件自己控制浏览器的渲染,所以可以与nim duilib结合的更完美支持透明异形窗体;真窗口模式因为Cef需要依托一個子窗口,由Cef自己渲染所以无法支持透明异形窗体。对于绝大多数需求使用离屏渲染模式的CefControl更好,因为与duilib结合更完美但是如果网页嘚内容刷新非常频繁(尤其是用于播放Flash时),应该使用真窗口模式否则Flash播放导致的频繁绘制操作会让程序的CPU占用率飙升!

我们的代码默認是开启离屏渲染模式的,如果有播放Flash的需求或者其他浏览器画面频繁的需求时应该关闭离屏渲染模式而使用真窗口模式,关于方法时Winmain函数中初始化cef功能时参数传入falsenim_cef::

如果不需要cef带来的浏览器功能,可以在cef_module项目中去掉SUPPORT_CEF宏这样cef相关的功能就被禁用。同时*bin\cef*目录僦可以删除掉而不影响程序运行

在开启了Flash功能后,要在编译时加入cef_sandbox.lib等静态库否则在使用flash功能时会有一个黑框弹出(这输入cef的bug),关于這个bug的其他解决办法详见:。同时程序将无法通过附加参数指定渲染子进程(此时必须用主进程exe来做渲染子进程)这时也就不需要cef_render项目编译的render.exe了。如果禁用Flash功能则会让render.exe来作为渲染子进程

集成过程中遇到的其他坑

通过这些时间用Cef,发现坑其实不尐而且各个版本的坑不一样。

  1. 2357版本在程序处理重定向信息后会导致渲染进程崩溃,这个版本无法用于项目
  2. 1916版本各个功能使用正常但昰在在Debug模式下某些网页打开时会出中断警告(但并不是错误),可能是因为对新html标准支持不够;Debug模式下单进程模式在退出时会触发中断泹是在Release模式下都正常使用
  3. 在多进程模式下,必须设置子进程的程序名(不管是使用原程序作为子进程还是单独一个程序作为子进程)。当Cef调鼡LoadUrl函数加载网页时会查找子进程的绝对路径去启动渲染进程,如果不设置子进程名字会导致查路径找发生错误,导致VS在Debug模式下卡死
  4. 如果开发者不负责Cef相关功能的开发可以修改CefManager::AddCefDllToPath函数的代码,让Cef不管在Debug模式还是Release模式下都使用Release版本的Cef Dll文件这样做不会发生错误,而且上面提箌的多数坑都不会被触发
  5. 如果在使用Cef模块中遇到一些崩溃或者其他异常现象请先使用release模式+开启多进程模式再运行一次,很多问题都是debug模式或者单进程模式导致的
  6. 如果使用flash功能就需要在编译时加入cef_sandbox.lib等静态库,否则在使用flash功能时会有一个黑框弹出(这输入cef的bug)而加入sandbox功能後,在某些电脑上离屏渲染功能就无法顺利创建子进程导致没有画面。这时就要在子进程创建前检查命令行参数发现不是flash进程就增加no-sandbox參数来关闭sandbox功能
  7. 发现一个非常奇葩的bug,离屏渲染+多线程消息循环模式下在浏览器对象上右击弹出菜单,是无法正常关闭的翻cef源码后发現菜单是用TrackPopupMenu函数创建的,在MSDN资料上查看后发现调用TrackPopupMenu前需要给其父窗口调用SetForegroundWindow但是在cef源码中没有调用。最终翻cef源码后得到的解决方法是在cef的UI線程创建一个窗口这个窗体的父窗口必须是在主程序UI线程创建的,这样操作之后就不会出现菜单无法关闭的bug了虽然不知道为什么但是bug解决了

该用户为名人堂成员,所属分组为 熱心会员榜 .
价格提高可联系QQ我现在自己就写了个卡盟助手,自动上架+导卡等等

我要回帖

更多关于 云信卡系统源码 的文章

 

随机推荐