天天看点

通过exe启动class

昨天下载了号称纯java版的网游《海天英雄传》, 发现其将包括jvm.dll在内的所有组件及所有class全部封装使用,觉得这种方式比较可行,既保证了理论上的纯java开发,又避免了核心代码被反 编译的风险;于是自己也尝试着写了点类似方法,摘录其中一个直接以exe文件调用main函数的发表。

本方法通过jni方式实现。

/**

 * 直接通过exe启动class(免外部配置)

 *

 * project:loonframework

 * author:chenpeng

 * email:[email protected]

 */

// 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);

            lstrcat(SubKey, "未找到!");

            MessageBox(SubKey);

        feedback_Length = sizeof(feedback) * sizeof(feedback[0]);

        result = RegQueryValueEx(hk, "RuntimeLib", NULL, NULL, (LPBYTE)feedback, &feedback_Length);

            lstrcat(SubKey, "Runtime Lib读取失败!");

        //获得JVM.DLL路径  

        lstrcpy(JVMPath, feedback);

    }

        //获得JVM.DLL启动实体

        JVM_DLL = LoadLibrary(JVMPath);

        if (JVM_DLL == NULL) {

            lstrcpy(SubKey, JVMPath);

            lstrcat(SubKey, "载入失败!");

    //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启动失败!");

    //查找目的类

    jclass clazz = env->FindClass(AppClass);

    if (clazz == 0) {

        lstrcpy(SubKey, AppClass);

        lstrcat(SubKey, "类没有找到!");

        MessageBox(SubKey);

    //取得入口主函数序列号

    jmethodID mid = env->GetStaticMethodID(clazz, MainName, "([Ljava/lang/String;)V");

    if (mid == 0) {

        lstrcat(SubKey, "没有找到主函数!");

    //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

本文转自 cping 51CTO博客,原文链接:http://blog.51cto.com/cping1982/130186