2017-05-03 9 views
0

まず、私はWindowsプログラマーではなく(Windowsユーザーでもない)、Linux上でクロスコンパイラを使用してWin32およびWin64。 Netを掘り下げた後(そしてここで質問しても)、私はWindowsのコンソールを開き、stdin/stdout/stderrのために使うことができるコード断片をまとめて管理しました。 Win32ではうまく動作しますが、Win64ではプログラムがクラッシュします。私は問題は、異なる長整数型のデータ型のサイズだと思う、gccはこれについて警告します。しかし、私はいくつかのWindows APIタイプの正確な目的とサイズがわからないので、私は何を変更すべきか分かりません。確かに、最高のwin32/win64の独立したソリューションがあります。私もlStdHandleに "HANDLE"タイプを使用しようとしましたが、コンパイルさえしません。誰もこれについて助けることができますか?Cのwin32とwin64のstdin/stdout/stderrのためのWindowsコンソールを開きます

int hConHandle; 
    long lStdHandle; 
    //HANDLE lStdHandle; 
    CONSOLE_SCREEN_BUFFER_INFO coninfo; 
    FILE *fp; 
    FreeConsole(); // be sure to release possible already allocated console 
    if (!AllocConsole()) { 
      ERROR_WINDOW("Cannot allocate windows console!"); 
      return; 
    } 
    SetConsoleTitle("My Nice Console"); 
    // set the screen buffer to be big enough to let us scroll text 
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); 
    coninfo.dwSize.Y = 1024; 
    //coninfo.dwSize.X = 100; 
    SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); 
    // redirect unbuffered STDOUT to the console 
    lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE); 
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); 
    fp = _fdopen(hConHandle, "w"); 
    *stdout = *fp; 
    setvbuf(stdout, NULL, _IONBF, 0); 
    // redirect unbuffered STDIN to the console 
    lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE); 
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); 
    fp = _fdopen(hConHandle, "r"); 
    *stdin = *fp; 
    setvbuf(stdin, NULL, _IONBF, 0); 
    // redirect unbuffered STDERR to the console 
    lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE); 
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); 
    fp = _fdopen(hConHandle, "w"); 
    *stderr = *fp; 
    setvbuf(stderr, NULL, _IONBF, 0); 
    // Set Con Attributes 
    //SetConsoleTextAttribute(GetStdHandle(STD_ERROR_HANDLE), FOREGROUND_RED | FOREGROUND_INTENSITY); 
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_INTENSITY); 
    SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT); 
    SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT); 
+0

デバッグしましたか?それはどこでクラッシュですか? – Peanut

+0

Visual Studio 2017でコンパイルされています。関数からの戻り値もチェックしてください。 – Peanut

+0

私は試してもできないので、実際には私はデバッグできません。ウィンドウがなく、誰かに試してみるだけです。これはMingwのクロスコンパイラをターゲットとしたLinuxでコンパイルされました。 32ビットのexeは問題ありません、64ビットのためだけにOKです。確かにそれは私が問題を抱えていることはあまり良くありません。私はあまりデバッグできませんが、私のプロジェクトのほとんどは、プラットフォームに依存しません(win32/win64/OSX/Linux/etc)。 –

答えて

1

これはハンドルなので、HANDLEタイプを使用する必要があります。 _open_osfhandleに電話するときにINT_PTRにキャストするか(SDKが実際には古くなったらSIZE_T)、longを使用すると値を切り捨てることができます。

+0

それは事実です。 'HANDLE'はポインタ(例えば' HMODULE'はモジュールのベースアドレスです)または 'GetCurrentProcess'によって返された' INVALID_HANDLE_VALUE'や '(HANDLE)-1'のような特別な値です。これらの場合、切り捨ては悪くなります。しかし、通常のカーネルハンドルとウィンドウハンドルは、32ビットの「long」に収まることが保証されています。また、 '_open_osfhandle'に渡されたハンドルをキャストするためには、Windowsのtypedefの代わりに' intptr_t'を使用する方が慣れています。 – eryksun

+0

@eryksunコンソールハンドルは、通常のカーネルハンドルではない場合もありますが、場合によっては擬似ハンドルであり、ビットについて何も仮定することはできません。 32ビットアプリケーションは64ビットアプリケーションでHWNDで動作する必要があるため、HWNDは32ビットに収まりますが、HANDLは同じではありません。 – Anders

+0

Windows 8より前のバージョンのコンソールハンドルは実際にはコンソール自体で管理される疑似ハンドルですが、実際には、コンソールについては正当な理由があるかどうかにかかわらず問題ありません。 Windows 7のconhost.exe、または本当に古いバージョンのcsrss.exe)は、ベースセット3,7,11から始まり、ハンドルを複製して新しいスクリーンバッファのハンドルを作成するときに増分します。 – eryksun

1

問題の原因を正確にはわかりません。私はLinuxでMinGWを使用してビルドするように設定されていません。コンソールアプリケーションをビルドしていない場合、無効なファイル記述子に関連する可能性があります。この場合、CRTは無効なハンドル値としてマッピングを処理するファイル記述子を初期化し、標準のFILEストリームも-1 filenoに初期化されます。しかし、私はあなたのコードでそれに問題は見られません。

しかし、あなたの*stdin = *fpハックは移植性がありません。これはMSVCの古いバージョンで動作します。msvcrt.dllでも動作します(MinGWではより良い選択肢がないために多少曖昧に使用されます)。しかし、新しいユニバーサルCRTでは動作しません。次のように新しいCRTでFILEが定義されています。だから、*stdinに割り当てる

typedef struct _iobuf 
{ 
    void* _Placeholder; 
} FILE; 

ちょうどこの_Placeholderポインタを上書きします。次のように内部構造は、実際には次のとおりです。

struct __crt_stdio_stream_data 
{ 
    union 
    { 
     FILE _public_file; 
     char* _ptr; 
    }; 

    char*   _base; 
    int    _cnt; 
    long    _flags; 
    long    _file; 
    int    _charbuf; 
    int    _bufsiz; 
    char*   _tmpfname; 
    CRITICAL_SECTION _lock; 
}; 

あなたが本当に上書きしているすべては、そのバッファ_ptrです。

標準ストリームをポータブルに再オープンする方法は、freopen経由です。だから、他の誰かがもっと良い解決策を持っているのは、デバイスです。コンソール以外のアプリケーションの場合は、ストリームを有効なファイル記述子にリセットします。次に、_dup2を使用して、基礎となるファイル記述子をリダイレクトします。例:

#include <io.h> 
#include <stdio.h> 
#include <fcntl.h> 
#include <Windows.h> 

int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
      LPWSTR lpCmdLine, int nCmdShow) 
//int wmain(int argc, wchar_t **argv) 
{ 
    int fdStd; 
    HANDLE hStd; 
    CONSOLE_SCREEN_BUFFER_INFO coninfo; 

    printf("Goodbye, World!\n"); 

    /* ensure references to current console are flushed and closed 
    * before freeing the console. To get things set up in case we're 
    * not a console application, first re-open the std streams to 
    * NUL with no buffering, and close invalid file descriptors 
    * 0, 1, and 2. The std streams will be redirected to the console 
    * once it's created. */ 

    if (_get_osfhandle(0) < 0) 
     _close(0); 
    freopen("//./NUL", "r", stdin); 
    setvbuf(stdin, NULL, _IONBF, 0); 
    if (_get_osfhandle(1) < 0) 
     _close(1); 
    freopen("//./NUL", "w", stdout); 
    setvbuf(stdout, NULL, _IONBF, 0); 
    if (_get_osfhandle(2) < 0) 
     _close(2); 
    freopen("//./NUL", "w", stderr); 
    setvbuf(stderr, NULL, _IONBF, 0); 

    FreeConsole(); 

    if (!AllocConsole()) { 
     //ERROR_WINDOW("Cannot allocate windows console!"); 
     return 1; 
    } 
    SetConsoleTitle("My Nice Console"); 

    // set the screen buffer to be big enough to let us scroll text 
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); 
    coninfo.dwSize.Y = 1024; 
    //coninfo.dwSize.X = 100; 
    SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); 

    // redirect unbuffered STDIN to the console 
    hStd = GetStdHandle(STD_INPUT_HANDLE); 
    fdStd = _open_osfhandle((intptr_t)hStd, _O_TEXT); 
    _dup2(fdStd, fileno(stdin)); 
    SetStdHandle(STD_INPUT_HANDLE, (HANDLE)_get_osfhandle(fileno(stdin))); 
    _close(fdStd); 

    // redirect unbuffered STDOUT to the console 
    hStd = GetStdHandle(STD_OUTPUT_HANDLE); 
    fdStd = _open_osfhandle((intptr_t)hStd, _O_TEXT); 
    _dup2(fdStd, fileno(stdout)); 
    SetStdHandle(STD_OUTPUT_HANDLE, (HANDLE)_get_osfhandle(fileno(stdout))); 
    _close(fdStd); 

    // redirect unbuffered STDERR to the console 
    hStd = GetStdHandle(STD_ERROR_HANDLE); 
    fdStd = _open_osfhandle((intptr_t)hStd, _O_TEXT); 
    _dup2(fdStd, fileno(stderr)); 
    SetStdHandle(STD_ERROR_HANDLE, (HANDLE)_get_osfhandle(fileno(stderr))); 
    _close(fdStd); 

    // Set Con Attributes 
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 
     FOREGROUND_GREEN | FOREGROUND_INTENSITY); 
    SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), 
     ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT); 
    SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), 
     ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT); 

    printf("Hello, World!\n"); 

    Sleep(10000); 
    return 0; 
} 
+0

ありがとう! HANDLE型を使用してINT_PTRにキャストすることで問題が解決されたようです。それは私の既存のソリューションの問題に役立ちました。しかし、私のことを誤解しないでください。私の提案したソリューション(実際に私の仕事ではありませんが、私はネットから推測してまとめることができます...)よりも優れています。しかし、いくつかの行だけがうまくいきます:) –

+0

@LGBGáborLénárt、あなたのコードは、新しいCRT用のVS 2015+でビルドするとうまくいかず、 'FILE'の内部実装の詳細に頼るのではなく移植性があるように修正するべきです。あなたが '* stdin = * fp'を行うことができると仮定して、フィールドに直接アクセスしていなくても、すべてのビットが間違っています。 – eryksun

+0

'HANDLE'問題に関しては、解決策は私には意味をなさない。この場合のハンドルはすべて「ロング」に収まります。私はあなたが最初にやったようにそれを書いていないだろうが、それはうまくいくと思う。私はMSVCでそれを試し、それが間違っている可能性がある場所を推測しようとします。これが自分自身の問題だった場合、デバッガでどこが間違っているのかを正確に診断するまでは満足できません。 – eryksun

関連する問題