9

この投稿は、Windowsで実行中のプロセスのリストを取得するためのソリューションを提供します。本質的に、それはします:

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);

次に、入力を読み取ります。

見た目も動作も素晴らしいですが、tasklistで使用されている文字セットがデフォルトの文字セットではなく、この呼び出しが失敗する可能性があるのではないかと考えていました。

たとえば、別の実行可能ファイルに関するこの他の質問は、それがいくつかの問題を引き起こす可能性があることを示しています。

その場合、適切な文字セットが何であるかを判断する方法はありますか?

4

4 に答える 4

12

これを2つの部分に分けることができます:

  1. Windowsの部分
    Javaから、Windowsコマンドを実行しています-「Windowsland」のjvmの外部。java RuntimeクラスがWindowsコマンドを実行すると、コンソールにDLLが使用されるため、コマンドがコンソールで実行されているようにWindowsに表示されます
    。Q:コンソールでC:\ windows \ system32 \ tasklist.exeを実行すると、何が表示されますか。結果の文字エンコーディング(Windows用語では「コードページ」)?

    • 引数のないwindows"chcp"コマンドは、コンソールのアクティブなコードページ番号を示します(たとえば、Multilingual-Latin-1の場合は850、Latin-1の場合は1252)。Windows MicrosoftコードページWindows OEMコードページWindows ISOコードページを参照
      してください。デフォルトのシステムコードページは、元々、システムロケールに従って設定されています(これを表示するにはsysteminfoと入力するか、コントロールパネル->地域と言語)。
    • Windows OS / .NET関数getACP()もこの情報を提供します

  2. Javaの部分:
    「x」のWindowsコードページ(850や1252など)からJavaバイトストリームをデコードするにはどうすればよいですか?

    • Windowsコードページ番号と同等のJava文字セット名の間の完全なマッピングはここから導き出すことができます-コードページ識別子(Windows)
    • ただし、実際には、マッピングを実現するために次のプレフィックスのいずれかを追加できます
      。ISOの場合は「」(なし)、OEMの場合は「IBM」または「x-IBM」、Microsoftの場合は「windows-」または「x-windows-」 /ウィンドウズ。
      例:ISO-8859-1またはIBM850またはwindows-1252

完全なソリューション:

    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);
    }

Qありがとうございます!- 楽しかった。

于 2012-11-20T23:01:35.080 に答える
5

実際、によって使用される文字セットは、システムのデフォルトとtasklist常に異なります。

一方、出力がASCIIに制限されている限り、デフォルトを使用するのは非常に安全です。通常、実行可能モジュールの名前にはASCII文字しか含まれていません。

したがって、正しい文字列を取得するには、(ANSI)WindowsコードページをOEMコードページに変換し、後者を文字セットとしてに渡す必要がありInputStreamReaderます。

これらのエンコーディング間に包括的なマッピングはないようです。次のマッピングを使用できます。

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関数を使用できます。戻り値は、 [地域と言語]コントロールパネルの[管理]タブの[ Unicode以外のプログラムの言語]設定によって異なります。変更を適用するには、再起動が必要です。


Windowsには、 ANSIOEMの2種類のエンコーディングがあります。

前者は、GUIモードで実行されている非Unicodeアプリケーションによって使用されます。
後者はコンソールアプリケーションで使用されます。コンソールアプリケーションは、現在のOEMエンコーディングで表現できない文字を表示できません。

はコンソールモードアプリケーションであるためtasklist、その出力は常に現在のOEMエンコーディングになります。

英語のシステムの場合、ペアは通常Windows-1252CP850です。

私はロシアにいるので、私のシステムには次のエンコーディングがあります:Windows-1251CP866。の出力をファイル
にキャプチャすると、ファイルにキリル文字を正しく表示できません。tasklist

メモ帳で表示すると、 (Hi!)ЏаЁўҐвの代わりに表示されます。 そして、として表示されます。Привет
µTorrentзTorrent

で使用されるエンコーディングを変更することはできませんtasklist


ただし、の出力エンコーディングを変更することは可能ですcmd。スイッチを渡す/uと、すべてがUTF-16エンコーディングで出力されます。

cmd /c echo Hi>echo.txt

のサイズecho.txtは4バイトです。Hi改行(\rおよび\n)の場合は2バイト、改行の場合は2バイトです。

cmd /u /c echo Hi>echo.txt

現在、のサイズecho.txtは8バイトです。各文字は2バイトで表されます。

于 2012-11-19T21:45:34.513 に答える
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);
        }
    } 
}

他の場所にも同様の回答を投稿しました。

于 2012-11-20T17:28:31.723 に答える
0

実行中のプロセスをチェックしたり、Javaを介してOSコマンドを実行したりするためのはるかに優れた方法があります:ProcessProcessBuilder

文字セットについては、いつでもOSにサポートされている文字セットを問い合わせて、必要に応じてエンコーダまたはデコーダを入手できます。

[編集]それを分解しましょう。特定の文字列のバイトがどのエンコーディングであるかを知る方法はないため、それらのバイトを取得し、必要に応じて順序をシフトするしかありません(プロセスが次の配列を提供できるような環境にいる場合)異なる順序のバイト、それを処理するためにByteBufferを使用)、およびサポートされている複数のCharsetDecodersを使用して、バイトを適切な出力にデコードします。

これはやり過ぎであり、特定の出力がUTF-8、UTF-16、またはその他のエンコーディングである可能性があると見積もる必要があります。ただし、少なくとも、可能な文字セットの1つを使用して特定の出力をデコードし、処理された出力を必要に応じて使用することができます。

JVM自体が実行されているのと同じOSによって実行されるプロセスについて話しているので、出力がavailableCharsets()メソッドによって返されるCharsetエンコーディングの1つになる可能性があります。

于 2012-11-12T19:00:46.237 に答える