우선, 저는이 일을 재미로만하고 어떤 식 으로든 프로가 아닙니다. 따라서 내 코드가 약간 엉성하더라도 놀라지 않을 것입니다!
콘솔 응용 프로그램을 위해 Java 11에서 GUI 래퍼를 작성하려고합니다. 내 계획은 BufferedReader를 사용하여 프로세스에서 stdOut 및 stdErr을 캡처하고 JTextArea에 표시하는 것이 었습니다. 명령 줄 매개 변수로 ArrayList를 채운 후 주 GUI 스레드에서이 스레드를 실행하고 있습니다. Ubuntu 또는 Fedora에서 완벽하게 작동하지만 Windows에서는 제대로 작동하지 않습니다. 크로스 컴파일 된 Windows 버전의 콘솔 응용 프로그램을 실행하려고하면 내 응용 프로그램은 콘솔 응용 프로그램이 닫힌 후에 만 출력을 표시합니다. 또한 C (일반적으로 Hello를 표시하고 5 초 동안 기다린 다음 World를 표시 함)로 된 매우 간단한 Hello World 응용 프로그램으로 대체하려고 시도했으며 동일한 작업을 수행합니다. 그러나 ping.exe -t 8.8.8.8을 실행하도록 ArrayList를 변경하면 제대로 작동합니다.
내가 의심하는 것은 while 루프가 스레드를 차단하고 있지만 Linux에서 어떻게 작동하는지 그리고 Windows에서 ping.exe를 사용하는지 이해하지 못한다는 것입니다. 또한 Java의 Redirect stdin 및 stdout에서 코드를 시도하고 ProcessBuilder 에서 언급 한 inheritIO : 기본 스레드를 차단하지 않고 시작된 프로세스의 stdout 및 stderr 전달 하지만 동일한 문제가 있습니다. 어떤 아이디어?
public class RunThread extends Thread {
@Override
public void run(){
// Create process with the ArrayList we populated above
ProcessBuilder pb = new ProcessBuilder(allArgs);
pb.redirectErrorStream(true);
// Clear the console
txtConsoleOutput.setText("");
// Try to start the process
try {
Process p = pb.start();
// Get the PID of the process we just started
pid = p.pid();
// Capture the output
String cmdOutput;
BufferedReader inputStream = new BufferedReader(new InputStreamReader(p.getInputStream()));
// Get stdOut/stdErr of the process and display in the console
while ((cmdOutput = inputStream.readLine()) != null) {
txtConsoleOutput.append(cmdOutput + "\n");
}
inputStream.close();
}
catch (IOException ex) {
JOptionPane.showMessageDialog(null,
"An error (" + ex + ") occurred while attempting to run.", AppName, JOptionPane.ERROR_MESSAGE);
}
// Clear the ArrayList so we can run again with a fresh set
allArgs.clear();
}
}
업데이트 @ControlAltDel에서 제공 한 코드와 @Holger의 조언을 기반으로 스레드로부터 안전하도록 다시 작성했지만 최종 결과는 동일합니다.
SwingWorker <Void, String> RunTV = new SwingWorker <Void, String> () {
@Override
protected Void doInBackground() {
// Create process with the ArrayList we populated above
ProcessBuilder pb = new ProcessBuilder(allArgs);
pb.directory(new File(hacktvDirectory));
pb.redirectErrorStream(true);
// Try to start the process
try {
Process p = pb.start();
// Get the PID of the process we just started
pid = p.pid();
// Capture the output
DataFetcher df = new DataFetcher(p.getInputStream(), new byte[1024], 0);
FetcherListener fl = new FetcherListener() {
@Override
public void fetchedAll(byte[] bytes) {}
@Override
public void fetchedMore(byte[] bytes, int start, int end) {
publish(new String (bytes, start, end-start));
}
};
df.addFetcherListener(fl);
new Thread(df).start();
} catch (IOException ex) {
ex.printStackTrace();
}
return null;
} // End doInBackground
// Update the GUI from this method.
@Override
protected void done() {
// Revert button to say Run instead of Stop
changeStopToRun();
// Clear the ArrayList so we can run again with a fresh set
allArgs.clear();
}
// Update the GUI from this method.
@Override
protected void process(List<String> chunks) {
// Here we receive the values from publish() and display
// them in the console
for (String o : chunks) {
txtConsoleOutput.append(o);
txtConsoleOutput.repaint();
}
}
};
RunTV.execute();
}
업데이트 10/11/2020 kriegaex의 게시물에 이어 이것을 다시 살펴 보았습니다. 안타깝게도 샘플 코드는 동일한 작업을 수행했지만 "예를 들어 샘플 프로그램이 println () 대신 System.out.print ()를 사용하면 출력이 버퍼링되기 때문에 콘솔에서 아무것도 볼 수 없습니다 . " 라는 주석 이 있습니다. 나와 함께 종을 울렸다.
래핑중인 프로그램의 소스에 액세스 할 수 있으며 C로 작성되었습니다. 콘솔에 비디오 해상도를 인쇄하는 다음 코드가 있습니다.
void vid_info(vid_t *s)
{
fprintf(stderr, "Video: %dx%d %.2f fps (full frame %dx%d)\n",
s->active_width, s->conf.active_lines,
(double) s->conf.frame_rate_num / s->conf.frame_rate_den,
s->width, s->conf.lines
);
fprintf(stderr, "Sample rate: %d\n", s->sample_rate);
}
fflush (stderr); 추가하면 두 번째 fprintf 문 아래에서 내 코드를 수정하지 않고 콘솔에 이러한 줄이 표시됩니다. 나는 이것이 왜 이것이없이 리눅스에서 작동하는지 이해하지 못한다. 그러나 적어도 나는 답을 알고있다.
내 댓글 관련 정보 :
또 다른 생각 : Swing없이 이것을 재현하고 스트림에서 읽은 내용을 프로그램의 텍스트 콘솔로 덤핑 해 보셨습니까?
ping
다른 테스트 프로그램과는 작동 하지 않지만 다른 테스트 프로그램과는 작동 하지 않는 문제 는 후자가 단순히 버퍼링 된 스트림에 쓰기 만하고 가끔 (예 : 종료 할 때) 플러시되므로 자신의 프로그램에 대해 읽을 내용이 없다는 것입니다. 짧은 문자열보다 훨씬 큰 버퍼를 가진 스트림에 "Hello"+ "world"를 쓰면 그러한 동작이 발생할 수 있다고 생각합니다.ping
그러나 직접 쓰고 플러시 할 수 있습니다.
예를 들어 샘플 프로그램이 System.out.print()
대신을 사용 하는 println()
경우 출력이 버퍼링되기 때문에 콘솔에 아무것도 표시되지 않습니다. 삽입 ( println()
호출을 암시) BufferedWriter.flushBuffer()
하거나 작성자의 버퍼를 직접 플러시 한 후에 만 첫 번째 프로세스의 콘솔에서 읽는 다른 프로그램이 무언가를 읽습니다.
콘솔에 쓰는 대상 애플리케이션 :
import java.util.Random;
public class TargetApp {
public static void main(String[] args) throws InterruptedException {
System.out.print("Hello ");
Thread.sleep(1500);
System.out.println("world!");
Random random = new Random();
for (int i = 0; i < 250; i++) {
System.out.print("#");
if (random.nextInt(20) == 0)
System.out.println();
Thread.sleep(50);
}
}
}
컨트롤러 애플리케이션, 대상 애플리케이션의 콘솔 출력 읽기 :
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;
public class ControllerApp extends Thread {
List<String> allArgs = Arrays.asList(
//"ping", "-n", "3", "google.de"
"java", "-cp", "bin", "TargetApp"
);
@Override
public void run() {
try (
BufferedReader inputStream = new BufferedReader(
new InputStreamReader(
new ProcessBuilder(allArgs)
.redirectErrorStream(true)
.start()
.getInputStream()
)
)
) {
String cmdOutput;
while ((cmdOutput = inputStream.readLine()) != null) {
System.out.println(cmdOutput);
}
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
public static void main(String[] args) throws InterruptedException {
new ControllerApp().start();
}
}
를 실행 ControllerApp
하면 "Hello"및 "world!"가 표시되지 않습니다. 별도로하지만 한 번에. "#"문자열도 마찬가지입니다. 다른 프로그램의 버퍼 플러시 동작에 따라 청크 단위로 읽습니다. 또한 다음 프로토콜을 50ms마다 "#"문자의 연속 스트림이 아닌 중지 및 이동 방식으로 작성하고 있음을 알 수 있습니다.
Hello world!
#####
#################
############
####
#############
##########################################
###############
################
#################
######
##########
######
#######
############################################
#########
#################
##########
따라서 이것이 귀하의 문제 TargetApp
라면 ControllerApp
. 그러면 스윙과도 관련이 없습니다.
업데이트 :TargetApp
다음 두 줄을 주석 처리하여 종료 후에 만 마지막 출력을 보는 동작을 에뮬레이트 할 수 있다는 것을 언급하는 것을 잊었습니다 .
// if (random.nextInt(20) == 0)
// System.out.println();
그러면 콘솔 로그는 다음과 같이 표시됩니다. 긴 "#"줄은 TargetApp
종료 될 때만 인쇄 됩니다.
Hello world!
##########################################################################################################################################################################################################################################################
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다