接上篇文章,已经成功加载了jvm后,考虑到需要做成windowservice运行到后台,那么需要对运行过程中的状态进行日志输出。
一,新建一个OutLog.cpp文件:
#include "stdafx.h"
using namespace std;
/**
输出服务启动日志
**/
void outLog(char* dataBuffer) {
//分配一块内存空间
char* buff = new char[1024];
//复制内容
strcpy_s(buff, 1024, dataBuffer);
//回车换行符
const char* enterLine = "\r\n";
//计算总长度
int len = strlen(buff) + strlen(enterLine) + 1;
//合并字符串,len为合并字符串后的总长度,会报错
strcat_s(buff,len,enterLine);
HANDLE hAppend;
DWORD dwBytesRead = (DWORD)strlen(buff);
DWORD dwBytesWritten = 0;
//打开一个存在的文件,如果不存在则创建它
hAppend = CreateFile(TEXT("LoadJVMDemoC.log"),
FILE_APPEND_DATA,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
//判断是否成功读取
if (hAppend == INVALID_HANDLE_VALUE) {
cout << "could not open LoadJVMDemoC.log" << endl;
return;
}
//写入日志
WriteFile(hAppend, buff, dwBytesRead, &dwBytesWritten, NULL);
//关闭流
CloseHandle(hAppend);
}
/**
输出包含错误码的日志
*/
void outLog(char* dataBuffer,DWORD lastError) {
//分配一块内存空间
char* buff = new char[1024];
//复制内容
strcpy_s(buff, 1024, dataBuffer);
//判断是否有错误编号
if(lastError != NULL){
//添加提示字段
strcat_s(buff,1024,"---------error char:");
//获取错误结果字符
long lastErrorLong = lastError;
char lastErrorChar[10];
//总长度
_ltoa_s(lastErrorLong,lastErrorChar,10);
int errorLen = strlen(buff) + strlen(lastErrorChar) + 1;
strcat_s(buff,errorLen,lastErrorChar);
}
outLog(buff);
}
输出包含错误吗的日志主要是考虑到dll、类或者方法加载错误后,错误原因输出的问题,输出逻辑为:outLog("加载启动方法失败",GetLastError());
暂时直接保存到当前文件夹下LoadJVMDemoC.log文件中。
同时添加一个用于声明的outlog.h文件:
#ifndef outlog_h
#define outlog_h
#include <windows.h>
//输出日志
void outLog(char* dataBuffer);
//输出日志
void outLog(char* dataBuffer,DWORD lastError);
#endif // outlog_h;
引入到头文件中:
// stdafx.h : 标准系统包含文件的包含文件,
// 或是经常使用但不常更改的
// 特定于项目的包含文件
//
#pragma once
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <iostream>
#include <string>
#include <windows.h>
// TODO: 在此处引用程序需要的其他头文件
#include "outlog.h"
二,对之前的工程进行改造
// LoadJVMDemo.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include "jni.h"
using namespace std;
//加载JVM启动java程序
bool startJVM();
typedef jint(JNICALL *JNICREATEPROC)(JavaVM **, void **, void *);
int _tmain(int argc, _TCHAR* argv[]){
outLog("main start");
cout<<"main start"<<endl;
startJVM();
return 0;
}
//启动java虚拟机
bool startJVM(){
//获取jvm动态库的路径
//const char jvmPath[] = "E:\eclipse-jee-kepler-SR2-win32\Java\jre\bin\client\jvm.dll";
TCHAR* jvmPath = _T("E://eclipse-jee-kepler-SR2-win32//Java//jre//bin//client//jvm.dll");
//java虚拟机启动时接收的参数,每个参数单独一项
const int nOptionCount = 3;
JavaVMOption vmOption[nOptionCount];
//设置jvm将不接收控制台的控制handler
vmOption[0].optionString = "-Xrs";
//设置JVM最大允许分配的堆内存,按需分配
vmOption[1].optionString = "-Xmx256M";
//设置classpath
vmOption[2].optionString = "-Djava.class.path=./HelloWorld.jar";
JavaVMInitArgs vmInitArgs;
vmInitArgs.version = JNI_VERSION_1_6;
vmInitArgs.options = vmOption;
vmInitArgs.nOptions = nOptionCount;
//忽略无法识别jvm的情况
vmInitArgs.ignoreUnrecognized = JNI_TRUE;
//设置启动类,注意分隔符为"/"
const char startClass[] = "test/HelloWorld";
//启动方法,一般是main函数,当然可以设置成其他函数
const char startMethod[] = "main";
//日志输出文件
const char *logoutFile = "LoadJVMDemoJ.log";
//传入参数,如果有
//int nParamcount = 2;
//const char * params[2] = {"aa","bb"};
//加载JVM,注意需要传入的字符串为LPCWSTR,指向一个常量Unicode字符串的32位指针,相当于const wchar_t*
outLog("开始加载VM");
HINSTANCE jvmDLL = LoadLibrary(jvmPath);
if(jvmDLL == NULL){
outLog("加载JVM动态库错误",GetLastError());
return false;
}
//初始化jvm物理地址
JNICREATEPROC jvmProcAddress = (JNICREATEPROC)GetProcAddress(jvmDLL, "JNI_CreateJavaVM");
if(jvmDLL == NULL){
FreeLibrary(jvmDLL);
outLog("初始化JVM物理地址失败",GetLastError());
return false;
}
//创建JVM
JNIEnv *env;
JavaVM *jvm;
jint jvmProc = (jvmProcAddress)(&jvm, (void **)&env, &vmInitArgs);
if(jvmProc < 0 || jvm == NULL ||env == NULL){
FreeLibrary(jvmDLL);
outLog("创建JVM错误",GetLastError());
return false;
}
//加载启动类
jclass mainclass = env ->FindClass(startClass);
if(env -> ExceptionCheck() == JNI_TRUE || mainclass == NULL){
env -> ExceptionDescribe();
env -> ExceptionClear();
FreeLibrary(jvmDLL);
outLog("加载启动类失败",GetLastError());
return false;
}
//加载启动方法
jmethodID methedID = env ->GetStaticMethodID(mainclass, startMethod, "([Ljava/lang/String;)V");
if(env -> ExceptionCheck() == JNI_TRUE || methedID == NULL){
env -> ExceptionDescribe();
env -> ExceptionClear();
FreeLibrary(jvmDLL);
outLog("加载启动方法失败",GetLastError());
return false;
}
outLog("开始执行");
env ->CallStaticVoidMethod(mainclass, methedID, NULL);
outLog("执行结束");
//jvm释放
jvm -> DestroyJavaVM();
return true;
}
运行结果
java程序执行结果可以使用C++调用System.setOut(new PrintStream(new FileOutputStream("xxx.log")));来输出,调用过程类似上面调用main函数的过程,不过考虑到这种方式输出时格式的问题,决定在java代码中新建日志来输出。