How do I access a VB DLL from Java? - java

i have some .dll files which has been used by some VB program now i have to port the application in java but the .dll file provider is not providing any jars or API for java platform.
since my requirement is to access the code written in those .dll files in java class , is there any solution to aceesss the .dll ??
thasks in advance...:)
i also got one solution
package jnahelloworldtest;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Platform;
import com.sun.jna.*;
/** Simple example of native library declaration and usage. */
public class Main {
public interface simpleDLL extends Library {
simpleDLL INSTANCE = (simpleDLL) Native.loadLibrary(
(Platform.isWindows() ? "simpleDLL" : "simpleDLLLinuxPort"), simpleDLL.class);
// it's possible to check the platform on which program runs, for example purposes we assume that there's a linux port of the library (it's not attached to the downloadable project)
byte giveVoidPtrGetChar(Pointer param); // char giveVoidPtrGetChar(void* param);
int giveVoidPtrGetInt(Pointer param); //int giveVoidPtrGetInt(void* param);
int giveIntGetInt(int a); // int giveIntGetInt(int a);
void simpleCall(); // void simpleCall();
}
public static void main(String[] args) {
simpleDLL sdll = simpleDLL.INSTANCE;
sdll.simpleCall(); // call of void function
int a = 3;
int result1 = sdll.giveIntGetInt(a); // calling function with int parameter&result
System.out.println("giveIntGetInt("+a+"): " + result1);
String testStr = "ToBeOrNotToBe";
Memory mTest = new Memory(testStr.length()+1); // '+1' remember about extra byte for \0 character!
mTest.setString(0, testStr);
String testReturn = mTest.getString(0); // you can see that String got properly stored in Memory object
System.out.println("String in Memory:"+testReturn);
Memory intMem = new Memory(4); // allocating space
intMem.setInt(0, 666); // setting allocated memory to an integer
Pointer intPointer = intMem.getPointer(0);
int int1 = sdll.giveVoidPtrGetInt(Pointer.NULL); // passing null, getting default result
System.out.println("giveVoidPtrGetInt(null):" + int1); // passing int stored in Memory object, getting it back
int int2 = sdll.giveVoidPtrGetInt(intMem);
//int int2 = sdll.giveVoidPtrGetInt(intPointer); causes JVM crash, use memory object directly!
System.out.println("giveVoidPtrGetInt(666):" + int2);
byte char1 = sdll.giveVoidPtrGetChar(Pointer.NULL); // passing null, getting default result
byte char2 = sdll.giveVoidPtrGetChar(mTest); // passing string stored in Memory object, getting first letter
System.out.println("giveVoidPtrGetChar(null):" + (char)char1);
System.out.println("giveVoidPtrGetChar('ToBeOrNotToBe'):" + (char)char2);
}
}
but this could not helped me out.

You can use DependencyWalker to figure out what functions are being exported from the DLL.
VB uses the stdcall calling convention, so you'll need to extend from StdCallLibrary rather than Library, and you'll probably need to initialize your library with a StdCallFunctionMapper, since your DLL's exported methods likely decorate the method names with a #NN suffix.
Map options = new Map();
options.put(Library.OPTION_FUNCTION_MAPPER, new StdCallFunctionMapper());
SimpleDLL dll = (SimpleDLL)Native.loadLibrary("simpleDLL", SimpleDLL.class, options);

Related

Getting base address of dll of specific process using JNA

Updated: See updates at the bot of the question
I would like to get base address of game.dll which is inside war3.exe process.
I'm trying to do it via JNA library version 5.9.0, but no success.
The issue I faced with: I can't get game.dll module from war3.exe process.
I tried to get it using:
int pid = getProcessId("Warcraft III");
openProcess(PROCESS_ALL_ACCESS, pid);
WinDef.HMODULE hMod = Kernel32.INSTANCE.GetModuleHandle("game.dll")
But the result of hMod is null.
Also I have tried to get all modules that war3.exe process has. As you may see it contains only 5 modules and it doesn't contain game.dll. But when I open war3.exe via Process Explorer I see definitely more than 5.
Executed with Intellij Idea:
Taken from Process Explorer:
Please, share your opinion and ideas why I get only 5 modules from IDE.
Any advice on how to get the game.dll module and its base address via JNA would be appreciated.
Updates:
As per Remy's answer I have made one more try with EnumProcessModules().
Here is my code snippet:
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Psapi;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HMODULE;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.ptr.IntByReference;
import java.util.Arrays;
import java.util.List;
import static com.sun.jna.platform.win32.WinNT.PROCESS_ALL_ACCESS;
import static handler.memory.MemoryHandler.openProcess;
public class MemoryHandler {
static final User32 user32 = User32.INSTANCE;
static final Psapi psapi = Psapi.INSTANCE;
public static void main(String[] args) {
int pid = getProcessId("Warcraft III");
HANDLE process = openProcess(PROCESS_ALL_ACCESS, pid);
HMODULE[] hMods = new HMODULE[1024];
psapi.EnumProcessModules(process, hMods, hMods.length, new IntByReference(1024));
List<HMODULE> hModList = Arrays.asList(hMods);
hModList.forEach(hMod ->
System.out.println(Pointer.nativeValue(hMod.getPointer())));
}
public static int getProcessId(String window) {
IntByReference pid = new IntByReference(0);
user32.GetWindowThreadProcessId(user32.FindWindow(null, window), pid);
return pid.getValue();
}
}
And here is the result:
As far as I understand I have got some pointers. But how should I understand which one from them is related to game.dll? I was assuming that I should get somehow the list on modules where I could see their names and base addresses.
Also if I change System.out.println(Pointer.nativeValue(hMod.getPointer()))); to hModList.forEach(System.out::println); I see the following pointers and a lot of nulls (about 1000).
Do these addresses contain the address of game.dll?
GetModuleHandle() looks in the calling process only. Since game.dll is not loaded in your own process, GetModuleHandle() can't find it.
To look for a module loaded in another process, you need to use either:
EnumProcessModules()/EnumProcessModulesEx(), using GetModuleFileNameEx() to get their file names. See Enumerating All Modules for a Process.
CreateToolhelp32Snapshot(TH32CS_SNAPMODULE|TH32CS_SNAPMODULE32), using Module32First()/Module32Next() to enumerate the snapshot. See Traversing the Module List.
Kernel32Utils.getModules() uses CreateToolhelp32Snapshot(TH32CS_SNAPMODULE), so if your Java app is running as a 64bit app then it will enumerate only 64bit modules. But war3.exe is running as a 32bit process in your screenshot, so if you use CreateToolhelp32Snapshot() in a 64bit process then you would need to use TH32CS_SNAPMODULE32 instead.
UPDATE:
As I mentioned above, if you go the EnumProcessModules() approach, you can use GetModuleFileNameEx() to determine the filename of each module. That way, you can find the module for game.dll.
More importantly:
you are lacking needed error handling of each system call. Always test return values for failures.
not an error per-se, but you really should not be requesting PROCESS_ALL_ACCESS rights with openProcess(). Request only the rights you actually need, no more. In this case, use PROCESS_QUERY_INFORMATION | PROCESS_VM_READ instead.
you are not looking at the output of the 4th parameter of EnumProcessModules() to know how many modules were actually stored in the array.
your input values of the 3rd and 4th parameters of EnumProcessModules() are wrong, they need to be expressed in bytes, not in elements.
Per the EnumProcessModules() documentation:
cb
The size of the lphModule array, in bytes.
lpcbNeeded
The number of bytes required to store all module handles in the lphModule array.
...
It is a good idea to specify a large array of HMODULE values, because it is hard to predict how many modules there will be in the process at the time you call EnumProcessModules. To determine if the lphModule array is too small to hold all module handles for the process, compare the value returned in lpcbNeeded with the value specified in cb. If lpcbNeeded is greater than cb, increase the size of the array and call EnumProcessModules again.
To determine how many modules were enumerated by the call to EnumProcessModules, divide the resulting value in the lpcbNeeded parameter by sizeof(HMODULE).
Finally I found solution, but not in Java or JNA.
I wrote this code using C++ and I will use it like dll in Java.
Here is my C++ code:
#include <conio.h>
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include <psapi.h>
using namespace std;
DWORD_PTR GetProcessBaseAddress(DWORD processID)
{
DWORD_PTR baseAddress = 0;
HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
HMODULE* moduleArray;
LPBYTE moduleArrayBytes;
DWORD bytesRequired;
if (processHandle)
{
if (EnumProcessModules(processHandle, NULL, 0, &bytesRequired))
{
if (bytesRequired)
{
moduleArrayBytes = (LPBYTE)LocalAlloc(LPTR, bytesRequired);
if (moduleArrayBytes)
{
unsigned int moduleCount;
moduleCount = bytesRequired / sizeof(HMODULE);
moduleArray = (HMODULE*)moduleArrayBytes;
if (EnumProcessModules(processHandle, moduleArray, bytesRequired, &bytesRequired))
{
baseAddress = (DWORD_PTR)moduleArray[0];
}
LocalFree(moduleArrayBytes);
}
}
}
CloseHandle(processHandle);
}
return baseAddress;
}
DWORD GetProcessId(LPCTSTR ProcessName) // non-conflicting function name
{
PROCESSENTRY32 pt;
HANDLE hsnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
pt.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hsnap, &pt)) { // must call this first
do {
if (!lstrcmpi(pt.szExeFile, ProcessName)) {
CloseHandle(hsnap);
return pt.th32ProcessID;
}
} while (Process32Next(hsnap, &pt));
}
CloseHandle(hsnap); // close handle on failure
return 0;
}
uintptr_t GetModuleBaseAddress(DWORD procId, const wchar_t* modName)
{
uintptr_t modBaseAddr = 0;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, procId);
if (hSnap != INVALID_HANDLE_VALUE)
{
MODULEENTRY32 modEntry;
modEntry.dwSize = sizeof(modEntry);
if (Module32First(hSnap, &modEntry))
{
do
{
if (!_wcsicmp(modEntry.szModule, modName))
{
modBaseAddr = (uintptr_t)modEntry.modBaseAddr;
break;
}
} while (Module32Next(hSnap, &modEntry));
}
}
CloseHandle(hSnap);
return modBaseAddr;
}
int main()
{
DWORD pid = GetProcessId(TEXT("war3.exe"));
cout << "Process ID of war3.exe: "<< pid << endl;
DWORD_PTR war3_exe_base_addr = GetProcessBaseAddress(pid);
cout <<"Base address of war3.exe: "<< war3_exe_base_addr << endl;
uintptr_t gameDllBaseAddress = GetModuleBaseAddress(pid, TEXT("game.dll"));
cout <<"Base address of game.dll: " << gameDllBaseAddress << endl;
}
The result is:

Is it possible to resume a continuation in Rhino multiple times?

I want to be able to capture a continuation and resume it several times, such that each such invocation would be independent of the others.
For example, in the following code, I'd want the 2 calls to context.resumeContinuation in the run method to result in the output: 1 1, rather than the current output of 1 2.
As far as I understand, the reason for the resulting output is that I always use the same scope object, which is being modified by the first continuation before being passed to the second one. So it seems that I should resume each continuation with a copy of the original scope, but type Scriptable has no clone method (or anything equivalent), and copying it using serialization/deserialization doesn't help either.
P.S. I am using Rhino version 1.7R5.
Example.java:
import org.mozilla.javascript.*;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
public class Example {
public void run() throws IOException {
Context context = Context.enter();
context.setOptimizationLevel(-2); // Use interpreter mode.
Scriptable scope = context.initStandardObjects();
scope.put("javaProxy", scope, Context.javaToJS(this, scope));
Object capturedContinuation = null;
try {
String scriptSource =
new String(Files.readAllBytes(Paths.get("example.js")));
String scriptName = "example";
int startLine = 1;
Object securityDomain = null;
Script script =
context.compileString(scriptSource, scriptName, startLine, securityDomain);
context.executeScriptWithContinuations(script, scope);
} catch (ContinuationPending continuationPending) {
capturedContinuation = continuationPending.getContinuation();
}
Object result = "";
context.resumeContinuation(capturedContinuation, scope, result);
context.resumeContinuation(capturedContinuation, scope, result);
Context.exit();
}
public void captureContinuation() {
Context context = Context.enter();
ContinuationPending continuationPending =
context.captureContinuation();
Context.exit();
throw continuationPending;
}
public void print(int i) {
System.out.print(i + " ");
}
public static void main(String[] args) throws IOException {
new Example().run();
}
}
example.js:
var i = 1;
javaProxy.captureContinuation();
javaProxy.print(i);
i = i + 1;
So I came up with a working solution:
Instead of copying the scope object, I should've copied the capturedContinuation object, so the 2 calls to resumeContinuation would be:
context.resumeContinuation(deepCopy(capturedContinuation), scope, result);
context.resumeContinuation(deepCopy(capturedContinuation), scope, result);
This question offers possible imeplementations of the deepCopy method.
A word of caution, though: instances of Rhino's NativeContinuation type (which is the dynamic type of capturedContinuation in the code above) seem to be quite large (~15KB and up when serialized to a byte array), so time/memory consumption implications should be considered when deep copying them in an application.
This is just a guess but I think this might work (Rhino 1.7.6):
NativeContinuation capturedContinuation = ...
final Object stackFrame = capturedContinuation.getImplementation();
// Invoke once...
context.resumeContinuation(capturedContinuation, scope, result);
// Put stack back where it was
capturedContinuation.initImplementation(stackFrame);
// Invoke twice...
context.resumeContinuation(capturedContinuation, scope, result);

JNA: How use CreateProcess for execute 32 bit version of a system native app in 64 bit systems?

I have a application that uses JNA for execute any other application using CreateProcess api. This works very fine, but when i need execute a 32 bit version of a system native app, is executing 64 bit version ( present on SysWOW64 folder ), for example: notepad.exe 64 bit.
So, exist some way for solve this troube?
I had tried use Wow64DisableWow64FsRedirection but seem don't is working.
My code:
Execute class:
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.WString;
import static com.sun.jna.platform.win32.WinBase.STARTF_USESHOWWINDOW;
import static com.sun.jna.platform.win32.WinUser.SW_HIDE;
import com.sun.jna.win32.StdCallLibrary;
import java.util.Arrays;
import java.util.List;
public class Execute {
public interface Kernel32 extends StdCallLibrary {
Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);
boolean CreateProcessA(
String lpApplicationName
, String lpCommandLine
, Structure lpProcessAttributes
, Structure lpThreadAttributes
, boolean bInheritHandles
, int dwCreationFlags
, Structure lpEnvironment
, String lpCurrentDirectory
, Structure lpStartupInfo
, Structure lpProcessInformation);
}
public static class ProcessInformation extends Structure {
public Pointer hProcess;
public Pointer hThread;
public int dwProcessId;
public int dwThreadId;
#Override
protected List getFieldOrder() {
return Arrays.asList(new String[] { "hProcess", "hThread", "dwProcessId", "dwThreadId" });
}
}
public static class StartupInfoA extends Structure {
public int cb;
public WString lpReserved;
public WString lpDesktop;
public WString lpTitle;
public int dwX;
public int dwY;
public int dwXSize;
public int dwYSize;
public int dwXCountChars;
public int dwYCountChars;
public int dwFillAttribute;
public int dwFlags;
public short wShowWindow;
public short cbReserved2;
public Pointer lpReserved2;
public Pointer hStdInput;
public Pointer hStdOutput;
public Pointer hStdError;
#Override
protected List getFieldOrder() {
return Arrays.asList(new String[] { "cb", "lpReserved", "lpDesktop", "lpTitle", "dwX", "dwY", "dwXSize", "dwYSize", "dwXCountChars", "dwYCountChars", "dwFillAttribute", "dwFlags", "wShowWindow", "cbReserved2", "lpReserved2", "hStdInput", "hStdOutput", "hStdError" });
}
}
public static void ExecuteProc(String software){
ProcessInformation processInformation = new ProcessInformation();
StartupInfoA startupInfo = new StartupInfoA();
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
startupInfo.wShowWindow = SW_HIDE;
Kernel32.INSTANCE.CreateProcessA(software, null
, null
, null
, true
, 0
, null
, "C:\\Windows\\System32\\"
, startupInfo
, processInformation);
}
}
Main:
public static native boolean Wow64DisableWow64FsRedirection(PointerByReference OldValue);
public static void Exec() {
PointerByReference lpBuffer = new PointerByReference();
Wow64DisableWow64FsRedirection(lpBuffer); // fails here
String sysdir = System.getenv("WINDIR") + "\\System32\\";
ExecuteProc(sysdir + "notepad.exe");
}
--- Updated because Windows is well, Windows ---
32 bit executables are in SysWOW64, while 64 bit executables are in System32. Apparently this bizarre choice makes sense to the people up in Redmond.
--- Rest of post follows, with updates ---
You are asking a 64 bit program to load and interface with 32 bit executables. The "compatibility" for 32 bit executables doesn't extend to linking them to 64 bit programs.
You need to launch a 32 bit JVM so the JNI interface will match the desired environment.
However, your example might not even need JNI. If you are launching 32-bit standalone programs, then you do not need to use JNI. Instead you could use ProcessBuilder and pass the command line arguments to the shell to effectively ensure you launch the 32 bit executable.
ProcessBuilder pb = new ProcessBuider(String.format("%s\\SysWOW64\\notepad.exe", System.getEnv("WINDIR"));
Process process = pb.start();
JNI is for when you need to link the JVM to native libraries, it is not the preferred way to launch native applications. ProcessBuilder is what you prefer to launch native applications.
This may not be appropriate for your set-up depending on what your needs are, but at my company we had this problem when we moved to a 64-bit operating system for our terminal server and we had an in-house Java application that required the 32-bit version of Java to run properly.
My solution was to go into the 64-bit java directory, rename java.exe and javaw.exe to java.exe.bak and javaw.exe.bak, respectively, then replace those files with stubs that launched the 32-bit version of Java instead. This was acceptable in our case since we only have one application that uses Java and we did not foresee ourselves writing any more applications in Java going forward:
// Stub file that launches the 32-bit java.exe
// Intended to replace the 64-bit java executable to ensure that (OUR APPLICATION) which only works with 32-bit java works correctly
// Author: Govind Parmar
#include <Windows.h>
#include <strsafe.h>
HRESULT __cdecl cat_argv(int argc, WCHAR *argv[], WCHAR *argstr, int cchargstr)
{
int i;
HRESULT hr;
ZeroMemory(argstr, sizeof(WCHAR) * cchargstr);
// first arg is program name; start with i=1 instead of 0
for (i = 1; i < argc; i++)
{
// Space out arguments
hr = StringCchCat(argstr, cchargstr, L" ");
if (FAILED(hr)) break;
hr = StringCchCat(argstr, cchargstr, argv[i]);
if (FAILED(hr)) break;
}
return hr;
}
int __cdecl wmain(int argc, WCHAR *argv[])
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
WCHAR args[8192];
DWORD dwExit;
ZeroMemory(&si, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
si.cb = sizeof(STARTUPINFO);
if (FAILED(cat_argv(argc, argv, args, 8192)))
{
_putws(L"Could not concatenate argument string!");
return 0;
}
CreateProcess(L"C:\\Program Files (x86)\\Java\\jre1.8.0_101\\bin\\java.exe", args, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, NULL, L"C:\\Program Files (x86)\\Java\\jre1.8.0_101\\bin\\", &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, &dwExit);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return dwExit;
}

JNA: EXCEPTION_ACCESS_VIOLATION

I'm using a C++ DLL via JNA.
I want to call the following method in Java, which writes into szVisor information that I want to read.
long FAR PASCAL DLL_GetLocalPortTS(char* szEquip,char* szVisor){
...
}
The Java interface implementation is the following:
public interface IE2E extends Library {
// Instancia de la dll, carga la librería
IE2E INSTANCE = (IE2E) Native.loadLibrary("e2e", IE2E.class);
...
int GetLocalPortTS(String equip, String[] equipInfo);
}
And the method call:
String equip = "equipID";
String equipInfo = "";
String[] rEquipInfo = {equipInfo};
IE2E sdll = IE2E.INSTANCE;
int ret = sdll.GetLocalPortTS(equip, rEquipInfo);
This execution nets me a fatal error in the JRE, but if I put both arguments as either String or String[] it doesn't. However, if I use both Strings it doesnt overwrite equipInfo and I don't get the info which I want; if I use both as arrays, the method doesn't get the equip value and doesn't operate.
Any insight on this will be welcome.
The problem is that the C code wants to write into szVisor, right? I guess it does something like this:
long GetLocalPortTS(char* szEquip,char* szVisor){
strcpy(szVisor, "I am C code result :)");
return 0;
}
If you pass in a String from the Java side, then the memory is owned by the JVM, so writing to it causes a crash. What you need is a Memory object, which is a wrapped malloc'd bit of memory that the C code can safely write to.
Your new JNA interface would be as follows. I've commented out the old version so you can compare:
public interface IE2E extends Library {
IE2E INSTANCE = (IE2E) Native.loadLibrary("e2e", IE2E.class);
//int GetLocalPortTS(String equip, String[] equipInfo);
int GetLocalPortTS(String equip, Memory equipInfo);
}
And the code to call it would be as follows, the 256 is a placeholder. Make sure you allocate enough to write the string to:
String equip = "equipID";
String equipInfo = "";
//String[] rEquipInfo = {equipInfo};
Memory rEquipInfo = new Memory(256);
IE2E sdll = IE2E.INSTANCE;
int ret = sdll.GetLocalPortTS(equip, rEquipInfo);
To use the result as a String, you'd do this:
rEquipInfo.getString(0);
As the documentation says, the Memory's finalize() method automatically calls free on the malloc'd memory so there's no need to worry about memory leaks.

Getting text data from C++ using JNI through std::ostream into Java

I have a class in C++ which takes an std::ostream as an argument in order to continuously output text (trace information). I need to get this text over to the Java side as efficiently as possible. What's the best way to do this? I was thinking of using a direct buffer, but another method would be to take all the function calls across to Java and do all the processing there, but it seems that I'd need a lot of JNI calls.
If an example could be shown of the exact implementation method, it would be very helpful, or if some code exists already to do this (perhaps part of another project). Another help would be to connect it up directly to a standard Java streaming construct, such that the entire implementation was completely transparent to the developer.
(Edit: I found Sharing output streams through a JNI interface which seems to be a duplicate, but not really of much help -- he didn't seem to find the answer he was looking for)
The std::ostream class requires a std::streambuf object for its output. This is used by the fstream and stringstream classes, which use the features of ostream by providing a custom implementation of the streambuf class.
So you can write your own std::streambuf implementation with an overwritten overflow method, buffer the incomming chars in an internal stringbuffer. Every x calls or on eof/newline generate an java-string and call the print method of your java PrintStream.
An incomplete example class:
class JavaStreamBuff : std::streambuf
{
std::stringstream buff;
int size;
jobject handle;
JNIEnv* env
//Ctor takes env pointer for the working thread and java.io.PrintStream
JavaStreamBuff(JNIEnv* env, jobject jobject printStream, int buffsize = 50)
{
handle = env->NewGlobalRef(printStream);
this->env = env;
this->size = size;
}
//This method is the central output of the streambuf class, every charakter goes here
int overflow(int in)
{
if(in == eof || buff.size() == size)
{
std::string blub = buff.str();
jstring do = //magic here, convert form current locale unicode then to java string
jMethodId id = env->(env->GetObjectClass(handle),"print","(java.lang.String)V");
env->callVoidMethod(id,handle,do);
buff.str("");
}
else
{buff<<in;}
}
virtual ~JavaStreamBuff()
{
env->DeleteGlobalRef(handle);
}
}
Missing:
Multithread support (the env pointer is only valid for the jvm thread)
Error handling (checking for java exceptions thrown)
Testing(written within the last 70 min)
Native java method to set the printstream.
On the java side you need a class to convert the PrintStream to a BufferedReader.
There have to be some bugs there, haven't spend enough time to work on them.
The class requires all access to be from the thread it was created in.
Hope this helps
Note
I got it to work with visual studio but I can't get it to work with g++, will try to debug that later.
Edit
Seems that I should have looked for a more official tutorial on this bevore posting my answer, the MSDN page on this topic derives the stringbuffer in a different way.
Sorry for posting this without testing it better :-(.
A small correction to the code above in a more or less unrelated point: Just implement InputStream with a custom class and push byte[] arrays instead of Strings from c++.
The InputStream has a small interface and a BufferedReader should do most of the work.
Last update on this one, since im unable to get it to work on linux, even with the comments on the std::streambuf class stating that only overflow has to be overwritten.
This implementation pushes the raw strings into an inputstream, which can be read from by an other thread. Since I am too stupid to get the debugger working its untested, again.
//The c++ class
class JavaStreamBuf :public std::streambuf
{
std::vector<char> buff;
unsigned int size;
jobject handle;
JNIEnv* env;
public:
//Ctor takes env pointer for the working thread and java.io.PrintStream
JavaStreamBuf(JNIEnv* env, jobject cppstream, unsigned int buffsize = 50)
{
handle = env->NewGlobalRef(cppstream);
this->env = env;
this->size = size;
this->setbuf(0,0);
}
//This method is the central output of the streambuf class, every charakter goes here
virtual int_type overflow(int_type in = traits_type::eof()){
if(in == std::ios::traits_type::eof() || buff.size() == size)
{
this->std::streambuf::overflow(in);
if(in != EOF)
buff.push_back(in);
jbyteArray o = env->NewByteArray(buff.size());
env->SetByteArrayRegion(o,0,buff.size(),(jbyte*)&buff[0]);
jmethodID id = env->GetMethodID(env->GetObjectClass(handle),"push","([B)V");
env->CallVoidMethod(handle,id,o);
if(in == EOF)
env->CallVoidMethod(handle,id,NULL);
buff.clear();
}
else
{
buff.push_back(in);
}
return in;
}
virtual ~JavaStreamBuf()
{
overflow();
env->DeleteGlobalRef(handle);
}
//The java class
/**
*
*/
package jx;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* #author josefx
*
*/
public class CPPStream extends InputStream {
List<Byte> data = new ArrayList<Byte>();
int off = 0;
private boolean endflag = false;
public void push(byte[] d)
{
synchronized(data)
{
if(d == null)
{
this.endflag = true;
}
else
{
for(int i = 0; i < d.length;++i)
{
data.add(d[i]);
}
}
}
}
#Override
public int read() throws IOException
{
synchronized(data)
{
while(data.isEmpty()&&!endflag)
{
try {
data.wait();
} catch (InterruptedException e) {
throw new InterruptedIOException();
}
}
}
if(endflag)return -1;
else return data.remove(0);
}
}
Sorry for wasting so much space^^(and time :-().
It sounds as though the deliverable here is a subclass of ostream. The immediate question I'd want to be clear about is, will this class be responsible for buffering data until Java calls into it to retrieve, or is it expected to immediately (synchronously?) call via JNI to pass it on? That will be the strongest guide to how the code will shape up.
If you can reasonably expect the text to appear as a series of lines, I'd think about presenting them to Java in one line per call: this seems a fair compromise between the number of JNI calls and not unduly delaying the passing on of the text.
On the Java side I think you're looking at creating a Reader so that clients can pick up the text via a familiar interface, or perhaps a subclass of BufferedReader.

Categories

Resources