前言
最近一段時間研究谷歌瀏覽器內核。谷歌瀏覽器內核一直開源,並維護更新,它的開源項目中內核更新速度和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%\include\jni.h
%JAVA_HOME%\include\jawt.h
%JAVA_HOME%\include\jni_md.h
%JAVA_HOME%\include\jawt_md.h
%JAVA_HOME%\lib\jawt.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)