2014-09-30 51 views
0

我在JNA中使用C++ .so。當我通過Netbeans運行它時,C++ cout的輸出直接打印在Netbeans控制檯中。例如,我想將這個輸出重定向到一個JFrame。正如我在網上發現的其他類似主題中所建議的,我使用freopen()將stdout重定向到文件。它工作正常,但我有這個解決方案的一個問題。只有在C++代碼執行停止後(或者至少我明白 - 也許我錯了),數據才能被讀取,然後重定向到JFrame。所以我想問問,是否真的有任何方法可以將C++重定向到某些可以從Java代碼實時訪問的地方?我的意思是在執行C++代碼期間,而不是在C++部分中的操作結束之後。謝謝。重定向共享庫輸出

+2

重定向到你是從讀一管。細節將在一定程度上取決於您的平臺。 – 2014-09-30 14:09:19

+1

您可以在寫入文件時讀取文件,但命名管道最有可能更好。 – 2014-09-30 14:11:30

+0

感謝您的答案。我嘗試使用我在此處找到的一個示例來執行此操作(讀取寫入的文件)。它在讀取文件的Java部分中使用available()方法,並在C部分中使用sleep()來查看它是否有效。它適用於此示例,但不適用於我的共享庫。你可以提出任何建議嗎?我切換到管道解決方案?謝謝。 – SpyrosR 2014-09-30 14:15:39

回答

3

天啊..重定向到一個管道是我能想到的最糟糕的一個想法..它寫入一個文件,然後閱讀它甚至比最糟糕..

它不適合您的「實時」或「實時」數據IMO要求..

這些選項只能在「C」不是「C++」 .. C++有做你想要什麼更好的辦法..

使用

你可以做的是創建你自己的流,通過JNI調用直接寫到任何你想要的地方。首先你需要繼承std::streambuf,然後創建一個std::ostream m您的自定義類。

重定向std::cout的rdbuf到您的自定義流。

這裏是我寫的:

#include "jni.h" 

#include <tuple> 
#include <vector> 
#include <iostream> 
#include <cstring> 

#if defined _WIN32 || defined _WIN64 
#include <windows.h> 
#else 
#include <sys/types.h> 
#endif 

#if defined _WIN32 || defined _WIN64 
#define JAVA_EXPORT __declspec(dllexport) 
#else 
#define JAVA_EXPORT 
#endif 


std::vector<std::tuple<std::ostream*, std::ios*, std::streambuf*>> streams; 

extern "C" { 
    JAVA_EXPORT void Java_natives_Natives_RedirectOutput(JNIEnv* env, jclass cls, jobject component); 
    JAVA_EXPORT void Java_natives_Natives_ResetOutput(JNIEnv* env, jclass cls); 
} 

class redir : public std::streambuf 
{ 
    private: 
     JNIEnv* env; 
     jobject comp; 
     jmethodID mID; 

     int_type overflow(int_type c = traits_type::eof()); 

    public: 
     redir(JNIEnv* e, jobject comp, jmethodID m) : env(env), comp(comp), mID(mID) {} 
     ~redir() {} 

     redir(const redir& other) = delete; 
     redir& operator = (const redir& other) = delete; 
}; 

redir::int_type redir::overflow(redir::int_type c) 
{ 
    if (c != traits_type::eof()) 
    { 
     jstring str = env->NewStringUTF((char*)&c); 
     env->CallVoidMethod(comp, mID, str); 
    } 

    return c; 
} 

class rdstream : public std::ostream 
{ 
    public: 
     rdstream(JNIEnv* env, jobject comp, jmethodID mID) : std::ostream(0), sbuf(env, comp, mID) {init(&sbuf);} 

    private: 
     redir sbuf; 
}; 



void Java_natives_Natives_RedirectOutput(JNIEnv* env, jclass cls, jobject component) 
{ 
    if (streams.empty()) 
    { 
     jclass txtcls = env->FindClass("Ljavax/swing/JTextArea;"); 

     if (txtcls) 
     { 
      jmethodID app = env->GetMethodID(txtcls, "append", "(Ljava/lang/String;)V"); 

      rdstream* ctrd = new rdstream(env, component, app); 
      rdstream* crrd = new rdstream(env, component, app); 
      streams.push_back(std::make_tuple(ctrd, &std::cout, std::cout.rdbuf())); 
      streams.push_back(std::make_tuple(crrd, &std::cerr, std::cerr.rdbuf())); 
      std::cout.rdbuf(ctrd->rdbuf()); 
      std::cerr.rdbuf(crrd->rdbuf()); 

      std::cout<<"TESTING OUTPUT REDIRECTION\n"; 
     } 
    } 
} 

void Java_natives_Natives_ResetOutput(JNIEnv* env, jclass cls) 
{ 
    for (std::vector<std::tuple<std::ostream*, std::ios*, std::streambuf*>>::iterator it = streams.begin(); it != streams.end(); ++it) 
    { 
     std::get<1>(*it)->rdbuf(std::get<2>(*it)); 
     delete std::get<0>(*it); 
    } 

    streams.clear(); 

    std::cout<<"TESTING OUTPUT RESET\n"<<std::flush; 
} 

void __attribute__((constructor)) load() 
{ 
    //Onload.. 
} 

void __attribute__((destructor)) unload() 
{ 
    //OnUnload.. 
    if (!streams.empty()) 
    { 
     Java_natives_Natives_ResetOutput(NULL, NULL); 
    } 
} 

然後在Java方面我做的事:

package natives; 

import java.awt.Component; 

/** 
* 
* @author Brandon 
*/ 
public class Natives { 
    static { 
     System.loadLibrary("Redirector"); 
    } 

    public native static void RedirectOutput(Component comp); 
    public native static void ResetOutput(); 
} 

和測試類:

package windowhandle; 

import java.awt.BorderLayout; 
import java.awt.Dimension; 
import javax.swing.JFrame; 
import javax.swing.JTextArea; 

/** 
* 
* @author Brandon 
*/ 
public class WindowHandle { 

    public static void main(String[] args) { 
     JFrame f = new JFrame("Test"); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.setVisible(true); 

     JTextArea t = new JTextArea(); 
     t.setPreferredSize(new Dimension(500, 100)); 
     f.setLayout(new BorderLayout(0, 0)); 
     f.getContentPane().add(t, BorderLayout.CENTER); 
     f.pack(); 

     natives.Natives.RedirectOutput(t);   
     natives.Natives.ResetOutput(); 
    } 
} 

現在的C++代碼可以定製任何你喜歡的方式。你可以在Java方面創建一個方法,追加到你想要的任何組件上,然後讓C++調用它,而不是找到文本區域並專門追加它。如果你這樣做,那麼它可以用於任何項目和任何組件,而不僅僅是一個textarea。

這甚至可以用於文件(std::fstream)。

反正..結果是:..

當你把它重定向: enter image description here

如果您重置: enter image description here

+1

PS ..你可以用BOOST iostreams來做到這一點..它可能更容易與提升,但meh ..我已經寫了上面,所以你不妨忘了現在提升.. – Brandon 2014-09-30 18:06:41

+1

更新了代碼..它現在更加優化和更快比以前。重定向的流將直接寫入Java的東西。 – Brandon 2014-09-30 18:26:25

+0

你好!真的非常感謝你的非常詳細的回答/解釋。因爲我現在正在尋找這幾天,所以我找不到這麼好的答案/解釋!我會盡力像JNA那樣做,因爲我正在與它合作,否則我會採取這一點!非常感謝! :) – SpyrosR 2014-09-30 18:46:13