2012-11-12 2 views
7

This post은 Windows에서 실행중인 프로세스 목록을 검색하는 솔루션을 제공합니다. 본질적으로 :Windows의 프로세스 목록을 charset-safe 방식으로 가져옵니다.

String cmd = System.getenv("windir") + "\\system32\\" + "tasklist.exe"; 
Process p = Runtime.getRuntime().exec(cmd); 
InputStreamReader isr = new InputStreamReader(p.getInputStream()); 
BufferedReader input = new BufferedReader(isr); 

다음 입력을 읽습니다.

모양이 좋아 보이지만 작업 목록에서 사용되는 charset이 기본 charset이 아니고이 호출이 실패 할 가능성이 있는지 궁금합니다.

예를 들어 this other question about a different executable은 몇 가지 문제를 일으킬 수 있음을 보여줍니다.

그런 경우 적절한 문자 세트가 무엇인지 판단 할 수있는 방법이 있습니까?

+0

여기에 질문이 있습니까? 해보고 보셨습니까? –

+0

@JimGarrison InputStreamReader의 * "default encoding"의존성에 대한 FindBugs로부터 경고를 받았는데 이것이 문제를 일으킬 수 있는지 없는지 전혀 알지 못합니다. 그래서 나는 그것을 수색했다고 말할 수있는 두 번째 게시물을 찾았습니다. 그게 내가 확인하고 싶은거야. 내 컴퓨터에서 그 코드가 잘 작동합니다. – assylias

+0

불확실성이 상당히 크기 때문에 질문이 아닌 의견으로 추가하겠습니다. 즉, 그 같은 시스템 유틸리티가 사용하는 문자 집합은 OS 설치를위한 기본 로켈의 문자 집합이라고 생각합니다. 해당 로케일을 쿼리하고이를 사용하여 출력 스트림을 해석하는 것이 가장 일반적인 접근 방법 인 것 같습니다. 그러나 현지화가있는 경우이를 해석 할 수 있도록 변경할 수있는 필드를 리버스 엔지니어링해야합니다. 그리고 이것은 문제의 유틸리티가 처음부터 이러한 방식으로 다양하게 작성되었는지 여부에 따라 달라집니다. – eh9

답변

11

2 개 부분으로 이것을 깰 수 :

  1. 창문 부분 자바에서
    당신이 윈도우 명령을 실행하고 - 외부 "윈도우 토지"의 JVM에.Java Runtime 클래스가 windows 명령을 실행하면 콘솔에 DLL을 사용합니다. & 콘솔에 명령이 실행중인 것처럼 Windows에 나타납니다.
    Q : 콘솔에서 C : \ windows \ system32 \ tasklist.exe를 실행할 때 결과의 문자 인코딩 (Windows 용어의 "코드 페이지")은 무엇입니까?

    • 윈도우 "CHCP"는 인수없이 명령 콘솔 (다국어 라틴-1, 1,252 라틴 1 대 예컨대 850)에 대한 활성 코드 페이지 번호를 제공한다. Windows Microsoft Code Pages, Windows OEM Code Pages, Windows ISO Code Pages
      기본 시스템 코드 페이지는 원래 시스템 로캘 (systeminfo 또는 제어판> 지역 및 언어)에 따라 설정됩니다. 나는 창문 코드에서 자바 바이트 스트림을 디코딩 어떻게
      :
    • 는 윈도우 OS는/.NET 기능 getACP() 또한이 정보

  2. 에게 자바 부분을 제공합니다 "x"(예 : 850 또는 1252)의 페이지?

    • 창 코드 페이지 번호와 해당 자바 캐릭터 세트 명 사이의 전체 매핑은 다음 접두사 중 하나가 매핑을 달성하기 위해 추가 할 수 있습니다 실제로,
    • 그러나 here - Code Page Identifiers (Windows)에서 파생 될 수
      ""(none), OEM의 경우 "IBM"또는 "x-IBM", Microsoft/Windows의 경우 "windows-"또는 "x-windows-"입니다.
      예. ISO-8859-1 또는 IBM850 또는 Windows-1252

전체 해결 방법 : Q에 대한

String cmd = System.getenv("windir") + "\\system32\\" + "chcp.com"; 
    Process p = Runtime.getRuntime().exec(cmd); 
    // Use default charset here - only want digits which are "core UTF8/UTF16"; 
    // ignore text preceding ":" 
    String windowsCodePage = new Scanner(
     new InputStreamReader(p.getInputStream())).skip(".*:").next(); 

    Charset charset = null; 
    String[] charsetPrefixes = 
     new String[] {"","windows-","x-windows-","IBM","x-IBM"}; 
    for (String charsetPrefix : charsetPrefixes) { 
     try { 
      charset = Charset.forName(charsetPrefix+windowsCodePage); 
      break; 
     } catch (Throwable t) { 
     } 
    } 
    // If no match found, use default charset 
    if (charset == null) charset = Charset.defaultCharset(); 

    cmd = System.getenv("windir") + "\\system32\\" + "tasklist.exe"; 
    p = Runtime.getRuntime().exec(cmd); 
    InputStreamReader isr = new InputStreamReader(p.getInputStream(), charset); 
    BufferedReader input = new BufferedReader(isr); 

    // Debugging output 
    System.out.println("matched codepage "+windowsCodePage+" to charset name:"+ 
      charset.name()+" displayName:"+charset.displayName()); 
    String line; 
    while ((line = input.readLine()) != null) { 
      System.out.println(line); 
    } 

감사합니다! - 재미 있었어.

+0

이것은 훌륭합니다.'notepad.exe' 응용 프로그램을 복사하고'0aéèçê.exe' 파일에 다시 작성하여 실행했습니다. 내 원래 코드가 실패했습니다 (정사각형 문자 표시). 귀하의 버전은 올바른 문자열 (코드 페이지 850 포함)을 출력했습니다. – assylias

0

실행중인 프로세스를 확인하거나 java : ProcessProcessBuilder을 통해 OS 명령을 실행하는 더 좋은 방법이 있습니다.

는 캐릭터 세트, 당신은 항상 Encoder 또는 Decoder 필요에 따라 지원되는 캐릭터 세트에 대한 OS를 문의하고 얻을 수 있습니다.

[편집] 하자. 주어진 String의 바이트를 인코딩하는 방법을 알 수있는 방법이 없기 때문에, 그 바이트를 얻고, 필요에 따라 순서를 바꾼다. (프로세스가 당신에게 배열을 줄 수있는 그런 환경에 있다면, ByteBuffer를 사용하여 처리), 지원되는 여러 CharsetDecoders를 사용하여 바이트를 적절한 출력으로 디코딩하십시오.

과도 함이며 주어진 출력이 UTF-8, UTF-16 또는 기타 인코딩 일 수 있다고 추정해야합니다. 그러나 에있는 가능한 출력 문자 집합 중 하나를 사용하여 주어진 출력을 디코딩 한 다음 필요에 따라 처리 된 출력을 사용하십시오.

우리는 JVM 자체가 실행중인 동일한 OS에서 실행되는 프로세스에 대해 이야기하고 있으므로, 출력은 availableCharsets() 메소드에서 반환 된 Charset 인코딩 중 하나에 포함될 가능성이 큽니다.

+0

이미 프로세스를 사용하고 있으며 charset을 지정하는 방법을 알고 있습니다. 문제는 사용할 charset입니다. "* 지원되는 문자 세트에 대해 OS에 항상 문의 할 수 있습니다 *": 어떻게 할 수 있습니까? 특정 프로그램에서 지원되는 문자 집합을 사용하는 문자셋을 어떻게 알 수 있습니까? – assylias

+0

Process 클래스를 사용하고 있지만 런타임 클래스를 사용하는 것보다 깔끔한 ProcessBuilder는 사용하지 않습니다. 사용 가능한 문자 세트를 얻기 위해 호출해야하는 실제 메소드는 Charset.availableCharsets()입니다. 그러나 Charset을 테스트 해보는 것이 더 안전 할 것이다. CharsetEncoder.canEncode(), detect() 등 ... – javabeats

+0

미안하지만 어떻게 작동하는지 이해할 수 없습니다. 특정 유스 케이스에 대한 권장 사항을 적용하는 방법에 대한 간단한 예를 들려 주시겠습니까? – assylias

5

실제로 tasklist이 사용하는 charset은 이며 항상 시스템 기본값과 다른입니다.

한편 출력이 ASCII으로 제한되는 한 기본값을 사용하는 것이 안전합니다. 일반적으로 실행 모듈은 이름에 ASCII 문자 만 있습니다.

올바른 문자열을 얻으려면 (ANSI) Windows 코드 페이지를 OEM 코드 페이지로 변환하고 후자를 InputStreamReader으로 charset으로 전달해야합니다.

이러한 인코딩 간에는 포괄적 인 매핑이없는 것으로 보입니다. 다음 매핑을 사용할 수 있습니다 :

Map<String, String> ansi2oem = new HashMap<String, String>(); 
ansi2oem.put("windows-1250", "IBM852"); 
ansi2oem.put("windows-1251", "IBM866"); 
ansi2oem.put("windows-1252", "IBM850"); 
ansi2oem.put("windows-1253", "IBM869"); 

Charset charset = Charset.defaultCharset(); 
String streamCharset = ansi2oem.get(charset.name()); 
if (streamCharset) { 
    streamCharset = charset.name(); 
} 
InputStreamReader isr = new InputStreamReader(p.getInputStream(), 
               streamCharset); 

이 방법 windows-1251IBM866 쌍 날 위해 일했습니다.

Windows에서 사용되는 현재 OEM 인코딩을 얻으려면 GetOEMCP 기능을 사용할 수 있습니다.반환 값은 에 의존합니다. 유니 코드가 아닌 프로그램에 대한 언어지역 및 언어 제어판에 설정합니다. 변경 사항을 적용하려면 재부팅해야합니다. ANSIOEM :


Windows에서 인코딩 두 가지 종류가 있습니다.

전자는 GUI 모드에서 실행되는 비 유니 코드 응용 프로그램에서 사용됩니다.
후자는 콘솔 응용 프로그램에서 사용됩니다. 콘솔 응용 프로그램은 현재 OEM 인코딩으로 표현할 수없는 문자를 표시 할 수 없습니다.

tasklist은 콘솔 모드 응용 프로그램이므로 출력은 항상 현재 OEM 인코딩입니다.

영어 시스템의 경우 일반적으로 Windows-1252CP850입니다.

러시아에 거주하고있는 시스템의 인코딩은 Windows-1251이고 CP866입니다. 내가 파일에 tasklist의 출력을 캡처하는 경우
는 파일이 제대로 키릴 문자를 표시 할 수 없습니다 : (안녕)

내가 메모장에서 볼 대신 ПриветЏаЁўҐв 얻을.
µTorrentзTorrent으로 표시됩니다.

tasklist에서 사용하는 인코딩을 변경할 수 없습니다.


그러나 출력 인코딩을 cmd으로 변경할 수 있습니다. /u 스위치를 넘겨 주면 모든 것이 UTF-16 인코딩으로 출력됩니다. Hi 두 바이트와 새 라인 (\r\n) 두 바이트 :

cmd /c echo Hi>echo.txt 

echo.txt의 크기는 4 바이트이다.

cmd /u /c echo Hi>echo.txt 

지금 echo.txt의 크기는 8 바이트입니다 : 각 문자는 두 바이트로 표현된다.

+0

귀하의 상세하고 유익한 답변을 해주셔서 감사합니다 - Glen Best의 답변은 그것이 전체 작업 예제를 제공하므로 선택 했으므로 귀하의 답변도 매우 훌륭하다는 것을 알았습니다. – assylias

3

스폰 과정 대신 JNA을 통해 Windows API를 사용하지 않는 이유는 무엇입니까? 좋아요 :

import com.sun.jna.platform.win32.Kernel32; 
import com.sun.jna.platform.win32.Tlhelp32; 
import com.sun.jna.platform.win32.WinDef; 
import com.sun.jna.platform.win32.WinNT; 
import com.sun.jna.win32.W32APIOptions; 
import com.sun.jna.Native; 

public class ListProcesses { 
    public static void main(String[] args) { 
     Kernel32 kernel32 = (Kernel32) Native.loadLibrary(Kernel32.class, W32APIOptions.UNICODE_OPTIONS); 
     Tlhelp32.PROCESSENTRY32.ByReference processEntry = new Tlhelp32.PROCESSENTRY32.ByReference();   

     WinNT.HANDLE snapshot = kernel32.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPPROCESS, new WinDef.DWORD(0)); 
     try { 
      while (kernel32.Process32Next(snapshot, processEntry)) {    
       System.out.println(processEntry.th32ProcessID + "\t" + Native.toString(processEntry.szExeFile)); 
      } 
     } 
     finally { 
      kernel32.CloseHandle(snapshot); 
     } 
    } 
} 

나는 비슷한 대답을 게시했습니다. elsewhere.

+0

위 명령은 전체 명령 줄이 아닌 명령 이름 만 출력합니다. 프로세스 전체 명령 줄을 가져올 수 있습니까? –

관련 문제