Thanks for your considered reply!
>> piOut = new PipedInputStream();
>> poOut = new PipedOutputStream(piOut);
>> System.setOut(new PrintStream(poOut));
>
> (Are you the same person who was asking about this for interfacing with CLIPS a
> little while ago?)
Nope.
> I doubt if that -- or anything else -- will work.
Right. I think I've come around to that view...
> Consider: when a C program writes something to stdout, the output is put into
> a data structure which is local to that program. When the buffer is flushed,
[quoted text clipped - 10 lines]
> the OS in any way, and it is completely invisible to a DLL which is written in
> a different language and which has its own IO mechanisms.
But that doesn't explain why it is possible to redirect from any
_process_, regardless of what it's implemented in. The
Process.get<In|Output|Error>Stream functions work fine. It seems only
sensible that they would try to redirect at the OS level rather than
tinker with the abstractions introduced for any given language.
> The only way that this sort of thing could be done is if on-the-fly redirection
> were built into the OS itself (and the JVM knew how to use it), or if the DLL
> itself provides a way to tell it to use a different output stream. The first
> possibility isn't available on any OS/JVM combination that I know of. The
> second is quite common in well-designed libraries.
Ah, yes. If only we all were able to deal with just well-designed
libraries (sigh).
Thanks again,
josef
> -- chris
Arne Vajhøj - 17 Sep 2006 04:52 GMT
>> I doubt if that -- or anything else -- will work.
>
> Right. I think I've come around to that view...
If you have access to the same C/C++ compiler
as the DLL you want to capture from, then there
may be some possibilities.
Look at this example. I am using Windows and
Mingw C compiler.
First we emulate the current DLL you have.
Nat1.java:
public class Nat1 {
public native void hello();
static {
System.loadLibrary("Nat1");
}
}
Nat1.c:
#include <stdio.h>
#include <jni.h>
#include "Nat1.h"
JNIEXPORT void JNICALL Java_Nat1_hello(JNIEnv *cntx, jobject me)
{
printf("Hello world\n");
}
A true classic.
Now to capture the output from this DLL we create a new DLL.
Nat2.java:
public class Nat2 {
public native void init();
public native String done();
static {
System.loadLibrary("Nat2");
}
}
Nat2.c:
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <jni.h>
#include "Nat1.h"
#define MAXBUF 20480
static int oldstdout;
static int pipehandles[2];
static char buf[MAXBUF];
JNIEXPORT void JNICALL Java_Nat2_init(JNIEnv *cntx, jobject me)
{
oldstdout = _dup(_fileno(stdout));
_pipe(pipehandles,MAXBUF,_O_BINARY);
_dup2(pipehandles[1],_fileno(stdout));
}
JNIEXPORT jstring JNICALL Java_Nat2_done(JNIEnv *cntx, jobject me)
{
int n;
fflush(stdout);
_dup2(oldstdout,_fileno(stdout));
n = _read(pipehandles[0],buf,sizeof(buf));
buf[n] = '\0';
return (*cntx)->NewStringUTF(cntx,buf);
}
Now we just need a test program.
TestProgram.java:
public class TestProgram {
public static void main(String[] args) throws Exception {
Nat2 n2 = new Nat2();
n2.init();
Nat1 n1 = new Nat1();
n1.hello();
n1.hello();
n1.hello();
System.out.println("#" + n2.done() + "#");
}
}
Building and running:
javac -classpath . Nat1.java
javah -classpath . -jni Nat1
gcc -c -I\sunjava\jdk1.5.0\include -I\sunjava\jdk1.5.0\include\win32
Nat1.c -o Nat1.obj
gcc -s -shared -Wl,--export-all,--kill-at Nat1.obj -o Nat1.dll
javac -classpath . Nat2.java
javah -classpath . -jni Nat2
gcc -c -I\sunjava\jdk1.5.0\include -I\sunjava\jdk1.5.0\include\win32
Nat2.c -o Nat2.obj
gcc -s -shared -Wl,--export-all,--kill-at Nat2.obj -o Nat2.dll
javac -classpath . TestProgram.java
path=.;%PATH%
java -classpath . TestProgram
And the output:
#Hello world
Hello world
Hello world
#
The method should work with C++ IO as well (cout instead of stdout).
This technique requires:
- DLL's being dynamicly linked against the C/C++ RTL
- DLL's being compiled with the same compiler
So it may not be possible for you.
But on the other hand it may be possible !
Arne