3

Win32GUIアプリを作成しています。そのアプリ内では、コマンドラインアプリで使用することを目的としたDLLを使用しています。

Foo.exeが私のGUIアプリであり、bar()が「hello」をstdoutに出力するDLLの関数であるとします。Foo.exeはbar()を呼び出します。

コマンドラインからリダイレクト(>)(つまり)を使用してFoo.exeを実行するFoo.exe > out.txtout.txtに「hello」が書き込まれ、正常に終了します(予想どおり)。

ただし、リダイレクトせずに(cmd.exeから、またはWindowsエクスプローラーでダブルクリックして)Foo.exeを実行すると、bar()が呼び出されたときにクラッシュします。

コマンドラインでリダイレクト(プロジェクトのVSのプロパティを介して設定)を使用してデバッガー内でFoo.exeを実行し、「GetStdHandle(STD_OUTPUT_HANDLE)」を呼び出すと、ハンドルの適切なアドレスを取得します。コマンドラインでリダイレクトせずに呼び出すと、0になります。

標準を「初期化」するために何かが必要ですか?アプリケーションの起動時にこのリダイレクトを設定する方法はありますか?(ファイルにリダイレクトするのが理想的ですが、DLLによって出力されたデータを破棄するだけでも問題ありません。)

最後に、クロスプラットフォームDLLであるため、DLLがCRTPOSIXのようなAPIを介してstdoutに書き込んでいるのではないかと思います。これが重要かどうかはわかりません。

CreateFileを使用してファイルを作成し、SetStdHandleを呼び出してみましたが、うまくいかないようです。ただし、ファイルを間違って作成している可能性があります。以下のコードを参照してください。

HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
// hStdOut is zero  

HANDLE hFile;
hFile = CreateFile(TEXT("something.txt"),        // name of the write
                   GENERIC_WRITE,               // open for writing
                   0,                           // do not share
                   NULL,                        // default security
                   CREATE_NEW,             // create new file only
                   FILE_ATTRIBUTE_NORMAL,  // normal file
                   NULL);                  // no attr. template
BOOL r = SetStdHandle(STD_OUTPUT_HANDLE, hFile) ;   
hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
// hStdOut is now equal to hFile, and r is 1

bar();
// crashes if there isn't a redirect in the program arguments

更新:この記事を見つけました:http ://support.microsoft.com/kb/105305 。「このコードはハンドル0、1、2の問題を修正しないことに注意してください。実際、他の問題のため、これを修正することはできません。したがって、lowではなくstream I/Oを使用する必要があります。 -レベルI/O。」

私のDLLは間違いなくファイルハンドル0、1、2を使用しています。したがって、この問題に対する適切な解決策がない可能性があります。

このケースをチェックし、CreateProcessを使用してexeを適切に再起動するソリューションに取り組んでいます。終わったらここに投稿します。

4

3 に答える 3

1

私が見つけた解決策は次のとおりです。

  • 標準出力を指示するために、何らかの方法で有効なファイルハンドルを取得します。ファイルハンドルを「fh」と呼びましょう。(Windowsでは、ファイルハンドルはファイル記述子と同じものではないことに注意してください)
  • ファイル記述子を_open_osfhandleでファイルハンドルに関連付けます(詳細については、http://msdn.microsoft.com/en-us/library/kdfaxaay.aspxを参照してください)。新しいファイル記述子であるint値「fd」を呼び出します。
  • dup2を呼び出して、STDOUT_FILENOを指定されたファイル記述子に関連付けます。dup2(fd、STDOUT_FILENO)
  • stdoutファイル記述子に関連付けられたファイルstremを作成しますFILE*f = _fdopen(STDOUT_FILENO、 "w");
  • memset stdoutをfのコンテンツに:* stdout = * f
  • 指定されたファイルハンドルでSetStdHandleを呼び出します。SetStdHandle(STD_OUTPUT_HANDLE、ofh);

私はこのシーケンスを正確にテストしていませんが、少し異なることに注意してください。

いくつかの手順が冗長かどうかはわかりません。

いずれにせよ、次の記事では、ファイルハンドル、記述子、およびフィールドストリームの概念を非常によく説明しています。

http://dslweb.nwnexus.com/~ast/dload/guicon.htm

于 2012-09-07T17:12:38.580 に答える
0

どのライブラリを使用しているか教えていただけますか? この問題には良い解決策があります。アイコンを持ち、すべてのショートカットが起動する小さなスタブ ランチャー EXE (GUI モードで、ただしウィンドウはありません!) を作成します。このスタブ EXE "CreateProcess" を実際の EXE にして、出力を "NUL" または "CON" にリダイレクトするか、CreateProcess() を一時停止し、その STDOUT を取得して何もしません。このように、元の EXE は目に見えるコンソールなしで動作するはずですが、実際には、親の目に見えないスタブ EXE によって取得されるハンドル 0、1、および 2 に書き込む場所があります。親の EXE を強制終了すると、子のハンドルが失われ、クラッシュする可能性があることに注意してください。

タスク マネージャーに 2 つのプロセスが表示される場合があります。したがって、これら 2 つのプロセスを Google Chrome のようにジョブにしてみることができます。

あなたの質問について標準出力を「初期化」する必要がありますか? - 親/ランチャーのみが、ハンドル 0、1、および 2 の STDOUT を「適切に」事前に初期化できます。

于 2013-02-14T06:21:43.853 に答える
0

スイッチを使用して、コンソール アプリケーションとして foo.exe をビルドする必要があります/SUBSYSTEM。Windows はアプリケーションにコンソール (stdout) を自動的に割り当てます。

  • 現在のコンソール
  • ファイルへのリダイレクト
  • 別のプログラムの STDIN へのパイプ

foo.exe を GUI アプリケーションとしてビルドすると、デフォルトではコンソールが割り当てられないため、クラッシュが発生します。

GUI サブシステムを使用する必要がある場合でも、AllocConsole を使用して実行できます。この古いWDJ 記事には、役立つサンプル コードがあります。

于 2012-09-04T04:21:08.177 に答える