简单的C++加载jvm实现--简单的日志输出

本文介绍如何在C++中通过JNI调用Java程序,并实现日志记录功能,包括日志输出方法及异常处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接上篇文章,已经成功加载了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代码中新建日志来输出。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值