昨天下載了號稱純java版的網游《海天英雄傳》, 發現其將包括jvm.dll在內的所有組件及所有class全部封裝使用,覺得這種方式比較可行,既保證了理論上的純java開發,又避免了核心代碼被反編譯的風險;于是自己也嘗試著寫了點類似方法,摘錄其中一個直接以exe文件調用main函數的發表。
本方法通過jni方式實現。
/**
* 直接通過exe啟動class(免外部配置)
*
* project:loonframework
* author:chenpeng
* email:ceponline@yahoo.com.cn
*/
// PS:好長時間不寫C/C++,已然快不會用了,順便復習一下……有錯大家提,大家幫忙優化……
#include "stdafx.h"
#include "jni.h"
//用于提示框顯示
void MessageBox(LPCTSTR text);
//用于路徑過濾
char* DirPath(char * path);
//MessageBox標題名稱。
static const char MessageBoxTitle[] = "Loonframework提供";
//本程序默認的jvm.dll相對路徑位置。
const static char _DEFAULT_JVM[]="http://jre//bin//client//jvm.dll";
//主函數名,也可改為其他名稱,JVM以此查詢啟動接口。
const char MainName[] ="main";
//虛擬機啟動參數總數。
const int JVMOptionCount = 5;
//JVM編譯器設定,none為使用默認編譯器。
static char Compiler[] = "-Djava.compiler=NONE";
//最小內存
static char MinMB[] = "-Xms256M";
//最大內存
static char MaxMB[] = "-Xmx512M";
//jar包中主函數class所在路徑。
static char AppClass[] = "org/loon/framework/game/Main";
//需要執行的jar包所在路徑,'./'為當前路徑簡寫,多jar包以';'分割。
static char ClassPath[] = "-Djava.class.path=./loonlangrisser0.01.jar";
static char LibraryPath[] = "-Djava.library.path=./";
typedef jint (WINAPI* JNICreateJavaVM)(JavaVM**, JNIEnv**, void *);
/**
* Win主函數
**/
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
//JVM路徑(PS:本寫法不支持中文路徑)。
char JVMPath[MAX_PATH];
/**
*本程序通過注冊表查找JVM.DLL位置。如未注冊安裝虛擬機,本程序將在執行文件相對路徑下直接獲得。)
*/
//設定空間大小
char SubKey[MAX_PATH * 2];
//將注冊表路徑字符串拷貝到SubKey
lstrcpy(SubKey, "Software//JavaSoft//Java Runtime Environment");
HKEY hk;
//查詢注冊表,并返回結果
LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SubKey, 0, KEY_READ, &hk);
//承載JVM.DLL句柄
HMODULE JVM_DLL;
//當注冊表中不存在Software//JavaSoft//Java Runtime Environment時,則從本地讀取JVM.DLL
if (result != ERROR_SUCCESS) {
//PS:此處用于從本地路徑直接獲得JVM.DLL時的其他處理。
//獲得文件所在絕對路徑,PathLength為返回的路徑長度
int PathLength = GetModuleFileName(NULL, JVMPath, MAX_PATH);
//拼裝實際路徑
lstrcat(DirPath(JVMPath),_DEFAULT_JVM);
//MessageBox("Software//JavaSoft//Java Runtime Environment不存在!");
// return -1;
//注冊表中已存在時
}else{
char feedback[MAX_PATH];
//獲得空間大小
DWORD feedback_Length = sizeof(feedback) * sizeof(feedback[0]);
result = RegQueryValueEx(hk, "CurrentVersion", NULL, NULL, (LPBYTE)feedback, &feedback_Length);
//關閉注冊表
RegCloseKey(hk);
if (result != ERROR_SUCCESS) {
MessageBox("Software//JavaSoft//Java Runtime Environment中CurrentVersion讀取失敗!");
return -1;
}
//獲得路徑(lstrcat用于將第二個串和第一個連起來賦值給第一個字符串)
lstrcat(SubKey, "http://");
lstrcat(SubKey, feedback);
//查詢注冊表
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SubKey, 0, KEY_READ, &hk);
if (result != ERROR_SUCCESS) {
lstrcat(SubKey, "未找到!");
MessageBox(SubKey);
return -1;
}
feedback_Length = sizeof(feedback) * sizeof(feedback[0]);
result = RegQueryValueEx(hk, "RuntimeLib", NULL, NULL, (LPBYTE)feedback, &feedback_Length);
RegCloseKey(hk);
if (result != ERROR_SUCCESS) {
lstrcat(SubKey, "Runtime Lib讀取失敗!");
MessageBox(SubKey);
return -1;
}
//獲得JVM.DLL路徑
lstrcpy(JVMPath, feedback);
}
//獲得JVM.DLL啟動實體
JVM_DLL = LoadLibrary(JVMPath);
if (JVM_DLL == NULL) {
lstrcpy(SubKey, JVMPath);
lstrcat(SubKey, "載入失敗!");
MessageBox(SubKey);
return -1;
}
//JVM內部函數JNI_CreateJavaVM讀取
JNICreateJavaVM createJavaVM = (JNICreateJavaVM)GetProcAddress(JVM_DLL, "JNI_CreateJavaVM");
if (createJavaVM == NULL) {
MessageBox("JNI_CreateJavaVM函數讀取失敗!");
return -1;
}
//指向本地方法調用接口
JNIEnv* env;
//表示Java虛擬機
JavaVM* jvm;
//設定JVM啟動參數
JavaVMInitArgs vm_args;
JavaVMOption options[JVMOptionCount];
/**
*jtl(Java Tools Language)設定:JVM的缺省行為是用“即時”編譯器(或JIT[字節代碼編譯器])執行字節碼。
*當加載類時,JIT將類字節碼轉換成本機代碼。使用JIT會導致在每個類加載后有短暫延遲,
*但可提高程序的總體性能。在某些情況下,執行時間可縮短十分之一。
*可用Compiler指定jtl,如將Compiler=foo后,該例中虛擬機將查找名為foo.dll的JIT編譯器。
*搜索其它編譯器是在jre/bin目錄中和系統的PATH上進行的。
*若找不到這樣的編譯器,虛擬機將缺省使用解釋器。
*/
options[0].optionString = Compiler;
//類地址
options[1].optionString = ClassPath;
//lib地址
options[2].optionString = LibraryPath;
//最小內存
options[3].optionString = MinMB;
//最大內存
options[4].optionString = MaxMB;
//PS:此參數用于設定跟蹤運行時的信息,暫不需要。
//options[3].optionString = "-verbose:jni";
//使用的jni版本,目前最高為JNI_VERSION_1_6。
vm_args.version = JNI_VERSION_1_4;
vm_args.options = options;
vm_args.nOptions = JVMOptionCount;
vm_args.ignoreUnrecognized = JNI_TRUE;
//啟動JVM,并返回結果
int res = createJavaVM(&jvm, &env, &vm_args);
if (res < 0) {
MessageBox("JVM啟動失敗!");
return -1;
}
//查找目的類
jclass clazz = env->FindClass(AppClass);
if (clazz == 0) {
lstrcpy(SubKey, AppClass);
lstrcat(SubKey, "類沒有找到!");
MessageBox(SubKey);
return -1;
}
//取得入口主函數序列號
jmethodID mid = env->GetStaticMethodID(clazz, MainName, "([Ljava/lang/String;)V");
if (mid == 0) {
lstrcpy(SubKey, AppClass);
lstrcat(SubKey, "沒有找到主函數!");
MessageBox(SubKey);
return -1;
}
//main啟動
env->CallStaticVoidMethod(clazz, mid, NULL);
//JVM釋放
jvm->DestroyJavaVM();
return 0;
}
/**
* 提示框,封裝原有MessageBox
*/
void MessageBox(LPCTSTR text) {
MessageBox(NULL, text, MessageBoxTitle, MB_ICONEXCLAMATION | MB_APPLMODAL | MB_OK | MB_SETFOREGROUND);
}
/**
* 過濾文件所在絕對路徑,去掉最后'/'后字符。
*/
char* DirPath(char * path)
{
char *ph = path;
char *tag = ph;
while (*ph)
{
if ( (*ph) == '//' )
tag = ph;
++ph;
}
*tag = '/0';
return path;
}
運行效果圖如下:

源碼如下,請該后綴為.rar
本方法通過jni方式實現。
/**
* 直接通過exe啟動class(免外部配置)
*
* project:loonframework
* author:chenpeng
* email:ceponline@yahoo.com.cn
*/
// PS:好長時間不寫C/C++,已然快不會用了,順便復習一下……有錯大家提,大家幫忙優化……
#include "stdafx.h"
#include "jni.h"
//用于提示框顯示
void MessageBox(LPCTSTR text);
//用于路徑過濾
char* DirPath(char * path);
//MessageBox標題名稱。
static const char MessageBoxTitle[] = "Loonframework提供";
//本程序默認的jvm.dll相對路徑位置。
const static char _DEFAULT_JVM[]="http://jre//bin//client//jvm.dll";
//主函數名,也可改為其他名稱,JVM以此查詢啟動接口。
const char MainName[] ="main";
//虛擬機啟動參數總數。
const int JVMOptionCount = 5;
//JVM編譯器設定,none為使用默認編譯器。
static char Compiler[] = "-Djava.compiler=NONE";
//最小內存
static char MinMB[] = "-Xms256M";
//最大內存
static char MaxMB[] = "-Xmx512M";
//jar包中主函數class所在路徑。
static char AppClass[] = "org/loon/framework/game/Main";
//需要執行的jar包所在路徑,'./'為當前路徑簡寫,多jar包以';'分割。
static char ClassPath[] = "-Djava.class.path=./loonlangrisser0.01.jar";
static char LibraryPath[] = "-Djava.library.path=./";
typedef jint (WINAPI* JNICreateJavaVM)(JavaVM**, JNIEnv**, void *);
/**
* Win主函數
**/
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
//JVM路徑(PS:本寫法不支持中文路徑)。
char JVMPath[MAX_PATH];
/**
*本程序通過注冊表查找JVM.DLL位置。如未注冊安裝虛擬機,本程序將在執行文件相對路徑下直接獲得。)
*/
//設定空間大小
char SubKey[MAX_PATH * 2];
//將注冊表路徑字符串拷貝到SubKey
lstrcpy(SubKey, "Software//JavaSoft//Java Runtime Environment");
HKEY hk;
//查詢注冊表,并返回結果
LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SubKey, 0, KEY_READ, &hk);
//承載JVM.DLL句柄
HMODULE JVM_DLL;
//當注冊表中不存在Software//JavaSoft//Java Runtime Environment時,則從本地讀取JVM.DLL
if (result != ERROR_SUCCESS) {
//PS:此處用于從本地路徑直接獲得JVM.DLL時的其他處理。
//獲得文件所在絕對路徑,PathLength為返回的路徑長度
int PathLength = GetModuleFileName(NULL, JVMPath, MAX_PATH);
//拼裝實際路徑
lstrcat(DirPath(JVMPath),_DEFAULT_JVM);
//MessageBox("Software//JavaSoft//Java Runtime Environment不存在!");
// return -1;
//注冊表中已存在時
}else{
char feedback[MAX_PATH];
//獲得空間大小
DWORD feedback_Length = sizeof(feedback) * sizeof(feedback[0]);
result = RegQueryValueEx(hk, "CurrentVersion", NULL, NULL, (LPBYTE)feedback, &feedback_Length);
//關閉注冊表
RegCloseKey(hk);
if (result != ERROR_SUCCESS) {
MessageBox("Software//JavaSoft//Java Runtime Environment中CurrentVersion讀取失敗!");
return -1;
}
//獲得路徑(lstrcat用于將第二個串和第一個連起來賦值給第一個字符串)
lstrcat(SubKey, "http://");
lstrcat(SubKey, feedback);
//查詢注冊表
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SubKey, 0, KEY_READ, &hk);
if (result != ERROR_SUCCESS) {
lstrcat(SubKey, "未找到!");
MessageBox(SubKey);
return -1;
}
feedback_Length = sizeof(feedback) * sizeof(feedback[0]);
result = RegQueryValueEx(hk, "RuntimeLib", NULL, NULL, (LPBYTE)feedback, &feedback_Length);
RegCloseKey(hk);
if (result != ERROR_SUCCESS) {
lstrcat(SubKey, "Runtime Lib讀取失敗!");
MessageBox(SubKey);
return -1;
}
//獲得JVM.DLL路徑
lstrcpy(JVMPath, feedback);
}
//獲得JVM.DLL啟動實體
JVM_DLL = LoadLibrary(JVMPath);
if (JVM_DLL == NULL) {
lstrcpy(SubKey, JVMPath);
lstrcat(SubKey, "載入失敗!");
MessageBox(SubKey);
return -1;
}
//JVM內部函數JNI_CreateJavaVM讀取
JNICreateJavaVM createJavaVM = (JNICreateJavaVM)GetProcAddress(JVM_DLL, "JNI_CreateJavaVM");
if (createJavaVM == NULL) {
MessageBox("JNI_CreateJavaVM函數讀取失敗!");
return -1;
}
//指向本地方法調用接口
JNIEnv* env;
//表示Java虛擬機
JavaVM* jvm;
//設定JVM啟動參數
JavaVMInitArgs vm_args;
JavaVMOption options[JVMOptionCount];
/**
*jtl(Java Tools Language)設定:JVM的缺省行為是用“即時”編譯器(或JIT[字節代碼編譯器])執行字節碼。
*當加載類時,JIT將類字節碼轉換成本機代碼。使用JIT會導致在每個類加載后有短暫延遲,
*但可提高程序的總體性能。在某些情況下,執行時間可縮短十分之一。
*可用Compiler指定jtl,如將Compiler=foo后,該例中虛擬機將查找名為foo.dll的JIT編譯器。
*搜索其它編譯器是在jre/bin目錄中和系統的PATH上進行的。
*若找不到這樣的編譯器,虛擬機將缺省使用解釋器。
*/
options[0].optionString = Compiler;
//類地址
options[1].optionString = ClassPath;
//lib地址
options[2].optionString = LibraryPath;
//最小內存
options[3].optionString = MinMB;
//最大內存
options[4].optionString = MaxMB;
//PS:此參數用于設定跟蹤運行時的信息,暫不需要。
//options[3].optionString = "-verbose:jni";
//使用的jni版本,目前最高為JNI_VERSION_1_6。
vm_args.version = JNI_VERSION_1_4;
vm_args.options = options;
vm_args.nOptions = JVMOptionCount;
vm_args.ignoreUnrecognized = JNI_TRUE;
//啟動JVM,并返回結果
int res = createJavaVM(&jvm, &env, &vm_args);
if (res < 0) {
MessageBox("JVM啟動失敗!");
return -1;
}
//查找目的類
jclass clazz = env->FindClass(AppClass);
if (clazz == 0) {
lstrcpy(SubKey, AppClass);
lstrcat(SubKey, "類沒有找到!");
MessageBox(SubKey);
return -1;
}
//取得入口主函數序列號
jmethodID mid = env->GetStaticMethodID(clazz, MainName, "([Ljava/lang/String;)V");
if (mid == 0) {
lstrcpy(SubKey, AppClass);
lstrcat(SubKey, "沒有找到主函數!");
MessageBox(SubKey);
return -1;
}
//main啟動
env->CallStaticVoidMethod(clazz, mid, NULL);
//JVM釋放
jvm->DestroyJavaVM();
return 0;
}
/**
* 提示框,封裝原有MessageBox
*/
void MessageBox(LPCTSTR text) {
MessageBox(NULL, text, MessageBoxTitle, MB_ICONEXCLAMATION | MB_APPLMODAL | MB_OK | MB_SETFOREGROUND);
}
/**
* 過濾文件所在絕對路徑,去掉最后'/'后字符。
*/
char* DirPath(char * path)
{
char *ph = path;
char *tag = ph;
while (*ph)
{
if ( (*ph) == '//' )
tag = ph;
++ph;
}
*tag = '/0';
return path;
}
運行效果圖如下:

源碼如下,請該后綴為.rar
浙公網安備 33010602011771號