昨天下载了号称纯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【】="//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, "//");
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编译器。
//代码效果参考:http://www.zidongmutanji.com/bxxx/487882.html
搜索其它编译器是在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 //代码效果参考:http://www.zidongmutanji.com/zsjx/106065.html
= 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