前言
最近一段时间研究谷歌浏览器内核。谷歌浏览器内核一直开源,并维护更新,它的开源项目中内核更新速度和Chrome浏览器版本更新进度一样!而且它不同于WebKit(值得一题的是谷歌浏览器已不使用WebKit内核了),它提供的不仅仅是页面渲染,而是提供一整浏览器解决方案和插件规则。
使用方便:我们给它一个“窗体”(操作系统或系统资源管理器中的本地窗体,本系列都使用Win32平台作为示例)和一些配置参数,它就能将你需要渲染的页面在给定窗口中完美地展示。
插件支持:Adobe和Google联合开发的pepperflashplayer功能完善,而且我们作为进程外插件安装的话可以不用考虑它的自动升级给用户造成困扰或我们开发中的版本变化。而你只需要一句代码即可完成插件的启用,获取和升级插件方式也很简单(先在电脑上装一个chrome浏览器,去安装目录下copy:-_-)。谷歌对pdf的插件也可以这样。
这个随笔系列主要使用Java给谷歌浏览套一个壳。因为cef(即“谷歌浏览器内核chromium embedded framework”,后文都使用cef作为简称,并且本系列都使用cef3)使用c/c++编写,并未直接提供Java语言API,虽然有Java版的一个维护版本,但本人认为并不好用。
获取AWT窗体句柄
我们今天要做的跟cef内核还没太大关系,我们先解决一个问题:获取Java窗体的句柄。
我们都知道:Java语言提供的GUI支持是建立在操作系统资源管理系统(或者桌面环境)的支持上的(在Java的2D/GUI中,最外层的窗体肯定是操作系统相关的),那么很简单的道理,我们可以使用JNI的一些API来获取窗体句柄。
- jni?
JNI是Java语言提供的本地化代码调用接口(在Java虚拟机里实际上不在乎下一个方法入口是内部指针还是外部-操作系统指针),我们可以写一个c/c++的函数去找到窗体句柄,然后返回给Java虚拟机,让我们在虚拟机内部也知晓某个Java窗体被操作系统分配的句柄。
- jawt
Java官方已经考虑到我们这种需求了,它提供了一个接口:jawt。包括一系列c/c++包含(头文件.h,平台相关)和一系列c/c++静态库文件。
具体包括jawt.h、jawt_md.h、jawt.lib(另外jni.h以及jni_md.h是使用jni必须的)
- javah
编写动态链接库(dll)需要使用c/c++头文件与c/++源文件联合编译。我们先使用JDK自带的javah工具(javah.exe)生成一个头文件并实现它。
当然,我们先写一个Java类,标注其native方法。
1 /* 2 * Copyright 2014 JootMir Project 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 7 http://www.apache.org/licenses/LICENSE-2.0 8 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 * 15 * Support: http://www.cnblogs.com/johness 16 */ 17 package johness.jcef3.util; 18 19 import javax.swing.JFrame; 20 21 /** 22 * AWT工具集 23 * 24 * @author ShawRyan 25 */ 26 public final class AWTUtil { 27 /** 28 * 获取某个窗体句柄(在Windows平台下) 29 * 30 * @param window 31 * 需要获取句柄的窗体对象 32 * @return 窗体应用句柄 33 */ 34 public static native int getWindowHandleInWindows(JFrame window); 35 }
然后使用javah生成其对应头文件。
头文件内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class johness_jcef3_util_AWTUtil */ #ifndef _Included_johness_jcef3_util_AWTUtil #define _Included_johness_jcef3_util_AWTUtil #ifdef __cplusplus extern "C" { #endif /* * Class: johness_jcef3_util_AWTUtil * Method: getWindowHandleInWindows * Signature: (Ljavax/swing/JFrame;)I */ JNIEXPORT jint JNICALL Java_johness_jcef3_util_AWTUtil_getWindowHandleInWindows (JNIEnv *, jclass, jobject); #ifdef __cplusplus } #endif #endif
jni细则我就不赘述了。
- visual studio
接下来我们使用c++代码来实现接口函数并编译为动态链接库。
经典的VC++6.0或者优秀的Dev C++我都不喜欢使用,我就用Visual Studio 2012来编写并编译吧。
建立项目
删除一切我们不需要(我们就没有需要的-_-)文件
复制jni和jawt相关头文件及库文件到项目中(值得一题的是不是说让你复制粘贴到vs窗体内,而是真正复制到你c++项目文件夹内)
%JAVA_HOME%includejni.h
%JAVA_HOME%includejawt.h
%JAVA_HOME%includejni_md.h
%JAVA_HOME%includejawt_md.h
%JAVA_HOME%libjawt.lib
你项目中的那个头文件
配置项目
配置项目为Release
配置项目使用jawt.lib静态库
更改头文件,把#include <jni.h>改为#include "jni.h"
(后面的竖线是光标)
写源文件
(创建好源文件并准备开始编写代码之前取消源文件预编译头)
开始编码(jawt.h里有使用示例,我们照着改)
1 #include "jni.h" 2 #include "jawt_md.h" 3 #include "johness_jcef3_util_AWTUtil.h" 4 5 JNIEXPORT jint JNICALL Java_johness_jcef3_util_AWTUtil_getWindowHandleInWindows (JNIEnv *env, jclass sender, jobject window) { 6 HWND hwnd = NULL; 7 8 JAWT_DrawingSurface *ds; 9 JAWT_DrawingSurfaceInfo *dsi; 10 JAWT_Win32DrawingSurfaceInfo *win; 11 12 JAWT awt; 13 awt.version = JAWT_VERSION_1_3; 14 15 jboolean result = JAWT_GetAWT(env, &awt); 16 if (result == JNI_TRUE) { 17 ds = awt.GetDrawingSurface(env, window); 18 jint lock = ds -> Lock(ds); 19 if (lock != JAWT_LOCK_ERROR) { 20 dsi = ds -> GetDrawingSurfaceInfo(ds); 21 win = (JAWT_Win32DrawingSurfaceInfo *) dsi -> platformInfo; 22 23 hwnd = win -> hwnd; 24 25 ds -> FreeDrawingSurfaceInfo(dsi); 26 ds -> Unlock(ds); 27 awt.FreeDrawingSurface(ds); 28 return jint(hwnd); 29 } 30 return 0; 31 } 32 return 0; 33 }
编译生成
- 使用或测试
把生成的dll复制到Java项目中,在项目配置中配置librarypath,然后编写测试代码。
测试代码也很简单:
package johness.jcef3.util; import javax.swing.JFrame; public class Main { public static void main(String[] args) { System.loadLibrary("jawt"); System.loadLibrary("JCEF3"); JFrame frame = new JFrame(); frame.setSize(400, 300); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); System.out.println(AWTUtil.getWindowHandleInWindows(frame)); } }
最后总结
使用visual studio编译时注意不使用预编译文件头。当然如果你稍微了解vc++的话可能也不用这么麻烦。
使用visual studio编译生成的dll需要运行时环境,比如msvcr110.dll之类的。如果你机器上没有安装过的话可能会爆出“Can't find dependent libraries”这种错,可以把这些个运行时环境的库打包过去。
写于2016-03-29
本来我想自己做java-cef,不过官方已经做了
写于2019-11-28
java-cef已经不维护了,有很多bug。几年前我自己维护了一个maven版本的项目 https://mvnrepository.com/artifact/org.bitbucket.johness/java-cef/49.87.win32.2 https://mvnrepository.com/artifact/org.bitbucket.johness/java-cef/49.87.win64.2。但也不更新了,注意,这也是几年前的东西了,请大家不要用,尽量使用时兴的开源组件。如果一个项目你看它超过一个月没活跃(发布版本,提交代码等),那你最好别用。
联系我,一起交流
(最后编辑时间2016-03-29 10:26:41)