質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.53%

  • C

    4386questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • C++

    4304questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Win32 API

    283questions

    Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

WinAPI BITMAPを読み込んでBITMAPの描画・UI

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 261

Weapon

score 65

前提・実現したいこと

BMPをウィンドウにドロップするとファイルヘッダ―等を読み取って情報を画像とともに表示するコードを書いています.圧縮の有無判別はまだしていませんからかなり荒いですが画像の表示ができません.また文字の表示が一回上書きされてしまいドロップ時に文字列が表示されません. 画像のデータの取得ミスのように思いますがお願いします.

また文字列変数の定義場所や変数の代入方法において改善点がありましたらお願いします.(wsprintfが長くなりすぎている点等)

修正および追記

WM_PAINT周辺は変更したつもりですが223行目のポインターに代入すると例外が投げられます.引数のbitmapのSizeiImageを再代入してみたりしましたが私のデバッグの知識ではそれ以上把握できませんでした.画像の描画領域の確保まではいけていると思いますが画像が表示できません.
GetDCをWM_CREATEで入手していいものかその辺りの変なところもご指摘お願いします.

該当のソースコード

#define UNICODE

#include <windows.h>

LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);

const wchar_t CLASS_NAME[] = L"CLASS";

HINSTANCE hGlobalInstance;

int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR nCmdLine, _In_ int nCmdShow) {
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(nCmdLine);

    hGlobalInstance = hInstance;

    WNDCLASSEX wc = {
        sizeof(WNDCLASSEX), CS_VREDRAW | CS_HREDRAW, WindowProc,
        0, 0, hInstance,
        NULL, (HCURSOR)LoadCursor(NULL, IDC_ARROW), (HBRUSH)GetStockObject(WHITE_BRUSH),
        NULL, CLASS_NAME, NULL
    };

    RegisterClassEx(&wc);

    HWND hwnd = CreateWindowEx(
        WS_EX_ACCEPTFILES, CLASS_NAME, L"Template", WS_OVERLAPPEDWINDOW,
        50, 50, 960, 525,
        NULL, NULL, hInstance, NULL
    );

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    MSG msg = {};

    while (GetMessage(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    static BOOL status_b_open = FALSE;
    static DWORD status_d_head = 0;

    static wchar_t filename[_MAX_PATH] = { 0 };

    static RECT rc = { 0, 0, 0, 0 };
    static RECT src = { 5, 5, 0, 0 };
    static BITMAPFILEHEADER bmfh;
    static BITMAPV5HEADER* bmih;


    static HDC hdc;
    static HDC hBuffer;
    static HBITMAP hbmp;
    static HANDLE hFile;
    static HBITMAP bm;


    static BYTE* filedata;
    static void* pv;
    static wchar_t pstring[4096];

    switch (uMsg) {
    case WM_CREATE:
        hdc = GetDC(hwnd);
        break;

    case WM_PAINT:
    {
        if (status_b_open == TRUE) {
            if (StretchBlt(hdc, 250, 50, bmih->bV5Width / 2, bmih->bV5Height / 2, hBuffer, 0, 0, bmih->bV5Width, bmih->bV5Height, SRCCOPY) == FALSE) {
                MessageBox(hwnd, L"Failed to drawtext", L"CAUTION", MB_OK);
            }
        }

        DrawText(hdc, pstring, -1, &src, DT_LEFT | DT_TOP);

        ValidateRect(hwnd, NULL);
    }
    break;
    case WM_SIZE:
    {
        GetClientRect(hwnd, &rc);
        src.right = rc.right;
        src.bottom = rc.bottom;
    }
    break;


    case WM_DROPFILES:
    {
        /* temporary param */
        LPDWORD rfsize=0;
        wchar_t tbmp[64] = { 0 };
        wchar_t infoheadstatus[32] = { 0 };
        wchar_t getStatus[1025] = { 0 };
        /* End temporary param */

        /* Get Droped File */
        wchar_t openedfilename[_MAX_PATH] = { 0 };
        wcscpy_s(openedfilename, _MAX_PATH, filename);
        HDROP hDrop = (HDROP)wParam;
        unsigned cof = DragQueryFile(hDrop, -1, NULL, 0);
        if (cof == -1)MessageBox(hwnd, L"This application arrowed only 1 file at the same time", L"CAUTION", MB_OK);//alert when i get plural files -> get the first file
        DragQueryFile(hDrop, 0, filename, _MAX_PATH);
        /* End Get Droped File */


        if (wcscmp(filename, openedfilename) != 0) {//process when the droped file is different with before one

            /* Copy File */
            hFile = CreateFile(filename, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

            size_t fsize = GetFileSize(hFile, NULL);
            rfsize = 0;
            filedata = (BYTE*)malloc(fsize);
            if (ReadFile(hFile, filedata, fsize, rfsize, NULL) == FALSE) {
                MessageBox(hwnd, L"FAILED TO READ", L"CAUTION", MB_OK);
                return -1;
            }
            /* End Copy File */

            /* Get Magic Number */
            DWORD dwHead = filedata[0] << 8 | filedata[1];

            size_t hotspotx = filedata[6] << 8 | filedata[7];
            size_t hotspoty = filedata[8] << 8 | filedata[9];

            DWORD wsize = 0;
            switch (dwHead) {
            case 'BM':
            {
                int* s = (int*)(filedata + 0xe);
                if (hotspotx == 0 && hotspoty == 0 && *s != 12) {
                    wsprintf(tbmp, L"BMP file\nBITMAPFILEHEADER : version.1");
                    status_d_head = 0x100;
                }
                else {
                    wsprintf(tbmp, L"BMP file\nBITMAPFILEHEADER : version.2");
                    status_d_head = 0x200;
                }

                wsize = filedata[2] | filedata[3] << 8 | filedata[4] << 16 | filedata[5] << 24;
            }
            break;

            case 'IC':
            case 'CI':
            case 'PT':
            case 'CP':
                if (hotspotx != 0 && hotspoty != 0) {
                    wsprintf(tbmp, L"BMP file\nBITMAPFILEHEADER : version.2");
                    status_d_head = 0x200;
                    wsize = filedata[2] | filedata[3] << 8 | filedata[4] << 16 | filedata[5] << 24;
                    break;
                }
                else {
                    wsprintf(tbmp, L"BMP file\nBITMAPFILEHEADER : version.2 but not use hotspot");
                    status_d_head = 0x200;
                    wsize = 0;
                    break;
                }
            default:
                wsprintf(tbmp, L"not BMP file");
                wsize = 0;
                status_d_head = 0x0;
            }
            /* End Get Magic Number */


            /* Check File version and Fill defined structure */
                /* Remark : If the file is classified as Bitmap for Windows, i read the file data */


            size_t tableoff = filedata[0xa] | filedata[0xb] << 8 | filedata[0xc] << 16 | filedata[0xd] << 24;//the offset of the table lead from the file lead
            size_t infosize = filedata[0xe] | filedata[0xf] << 8 | filedata[0x10] << 16 | filedata[0x11] << 24;//size of header

            switch (status_d_head) {
            case 0x100:
            {
                switch (infosize) {
                case 40:
                    wsprintf(infoheadstatus, L"version.1 for Win");
                    bmih = (BITMAPV5HEADER*)(filedata + 0xe);
                    wsprintf(getStatus, L"biSize : %d\nbiWidth : %d\nbiHeight : %d\nbiPlanes : %d\nbiBitCount : %d\nbiCompression : %d\nbiSizeImage : %d\nbiXPelsPerMeter : %d\nbiYPelsPerMeter : %d\nbiClrUsed : %d\nbiClrImportant : %d\n\0", bmih->bV5Size, bmih->bV5Width, bmih->bV5Height, bmih->bV5Planes, bmih->bV5BitCount, bmih->bV5Compression, bmih->bV5SizeImage, bmih->bV5XPelsPerMeter, bmih->bV5YPelsPerMeter, bmih->bV5ClrUsed, bmih->bV5ClrImportant);
                    status_d_head |= 0x1;

                    break;
                case 108:
                    wsprintf(infoheadstatus, L"version.4");
                    bmih = (BITMAPV5HEADER*)(filedata + 0xe);
                    wsprintf(getStatus, L"bV4Size : %d\nbV4Width : %d\nbV4Height : %d\nbV4Planes : %d\nbV4bV4tCount : %d\nbV4Compression : %d\nbV4SizeImage : %d\nbV4XPelsPerMeter : %d\nbV4YPelsPerMeter : %d\nbV4ClrUsed : %d\nbV4ClrImportant : %d\nbV4RedMask : %d\nbV4GreenMask : %d\nbV4BlueMask : %d\nbV4AlphaMask : %d\nbV4CSType : %d\nbV4Endpoints : %p\nbV4GammaRed : %d\nbV4GammaGreen : %d\nbV4GammaBlue : %d\n\0", bmih->bV5Size, bmih->bV5Width, bmih->bV5Height, bmih->bV5Planes, bmih->bV5BitCount, bmih->bV5Compression, bmih->bV5SizeImage, bmih->bV5XPelsPerMeter, bmih->bV5YPelsPerMeter, bmih->bV5ClrUsed, bmih->bV5ClrImportant, bmih->bV5RedMask, bmih->bV5GreenMask, bmih->bV5BlueMask, bmih->bV5AlphaMask, bmih->bV5CSType, bmih->bV5Endpoints, bmih->bV5GammaRed, bmih->bV5GammaGreen, bmih->bV5GammaBlue);
                    status_d_head |= 0x2;

                    break;

                case 124:
                    wsprintf(infoheadstatus, L"version.5");
                    bmih = (BITMAPV5HEADER*)(filedata + 0xe);
                    wsprintf(getStatus, L"bV5Size : %d\nbV5Width : %d\nbV5Height : %d\nbV5Planes : %d\nbV5BitCount : %d\nbV5Compression : %d\nbV5SizeImage : %d\nbV5XPelsPerMeter : %d\nbV5YPelsPerMeter : %d\nbV5ClrUsed : %d\nbV5ClrImportant : %d\nbV5RedMask : %d\nbV5GreenMask : %d\nbV5BlueMask : %d\nbV5AlphaMask : %d\nbV5CSType : %d\nbV5Endpoints : %p\nbV5GammaRed : %d\nbV5GammaGreen : %d\nbV5GammaBlue : %d\nbV5Intent : %d\nbV5ProfileData : %d\nbV5ProfileSize : %d\nbV5Reserved%d\n\0", bmih->bV5Size, bmih->bV5Width, bmih->bV5Height, bmih->bV5Planes, bmih->bV5BitCount, bmih->bV5Compression, bmih->bV5SizeImage, bmih->bV5XPelsPerMeter, bmih->bV5YPelsPerMeter, bmih->bV5ClrUsed, bmih->bV5ClrImportant, bmih->bV5RedMask, bmih->bV5GreenMask, bmih->bV5BlueMask, bmih->bV5AlphaMask, bmih->bV5CSType, (tagICEXYZTRIPLE)bmih->bV5Endpoints, bmih->bV5GammaRed, bmih->bV5GammaGreen, bmih->bV5GammaBlue, bmih->bV5Intent, bmih->bV5ProfileData, bmih->bV5ProfileSize, bmih->bV5Reserved);
                    status_d_head |= 0x3;

                    break;

                default:
                    wsprintf(infoheadstatus, L"BROKEN BMP");

                    goto FileCheckEnd;
                }

                /* Image Rendering */

                hBuffer = CreateCompatibleDC(hdc);

                hbmp = CreateDIBSection(hBuffer, (BITMAPINFO*)bmih, DIB_RGB_COLORS, &pv, NULL, NULL);
                if (hbmp != NULL) {
                    int bmpoffset = bmfh.bfOffBits;
                    for (int i = 0; i < bmih->bV5Height * bmih->bV5Width; i++)((unsigned *)pv)[i] = filedata[bmpoffset+i];

                    SelectObject(hBuffer, hbmp);
                    GetObject(hBuffer, sizeof(BITMAP), &bm);

                    status_b_open = TRUE;

                }
                else {
                    MessageBox(hwnd, L"FAILED TO CDIBS", L"CAUTION", MB_OK);
                    status_b_open = FALSE;
                }

                /* End Image Rendering */


            }
            break;

            case 0x200:
                if (infosize == 12) {
                    wsprintf(infoheadstatus, L"core for OS/2");
                }
                else if (infosize >= 16 && infosize <= 64) {
                    wsprintf(infoheadstatus, L"version.2 for OS/2");
                }
                else {
                    wsprintf(infoheadstatus, L"BROKEN BMP for OS/2");
                }
                status_b_open = FALSE;
                break;

            default:
                wsprintf(infoheadstatus, L"NO INFOHEADER");
                status_b_open = FALSE;
            }
        }
        /* End Check File version and Fill defined structure */

        /* Pass the string data to the parameter of parent scope */
        wsprintf(pstring, L"PATH : %s\n\nFileType : \n%s\n\nInfoHeader : \n%s\n", filename, tbmp, getStatus);
        /* Pass the string data to the parameter of parent scope */

    }
FileCheckEnd:

    InvalidateRect(hwnd, NULL, TRUE);
    break;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

補足情報

Windows10 Pro
VisualStudio2019 Community

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Weapon

    2019/04/10 23:25

    流石にファイルフォーマット頭から読んでるのに関数にもせずそのまま書くのは見にく過ぎました.気を付けます.

    キャンセル

  • yominet

    2019/04/11 01:17

    WM_PAINTの中身も見直したほうがいい。

    キャンセル

  • atata0319

    2019/04/15 02:33

    デバッグは知識でやる部分もありますが、最も重要なことは特定タイミングでの変数の値がすべて想定通り(これを一般的には設計通りと言う)であることを確認することです。現在発生している問題はそれだけで解決できます。やり方については y_waiwai さんが既に述べられている通りです。

    仕様が決まっているファイルの解析処理は誰が書いても似たようなものになるので、あえてコメントをつけなくても、その知識を持った人には問題はありません。広く意見を募るのであれば、それを知らない人にも読んでもらえるようにコメントを付ける努力は必要です。とは言っても、分かりやすいコメントを付けるには相応の経験は必要ですが。

    キャンセル

回答 1

check解決した方法

+1

bfOffBitsに正確なオフセットが入ってない場合があるようで0になっていました.
またCreateDIBSectionでシステム作成されたpvBitesの配列が3byte(RGB)で構成されていたため

for (int i = 0; i < bmih->bV5Height * bmih->bV5Width; i++)
((unsigned*)pv)[i]=filedata[bmpoffset + i]


ではなく

for (int i = 0; i < bmih->bV5Height * bmih->bV5Width * 3; i++)
((BYTE*)pv)[i]=filedata[bmpoffset + i]


とする必要がありました.

またStretchBltの1/2圧縮表示のサンプリングがうまくいかなかったため直前で

SetStretchBltMode(hdc, STRETCH_HALFTONE);


を呼ぶことで正確に表示できました.

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/04/15 17:34

    本件はもう閉じている上に直接関係も無いのですが、コード中で気づいたので一応指摘させていただきます。
    WM_DROPFILESメッセージのハンドリングで、LPDWORD rfsize=0; ... ReadFile(hFile, filedata, fsize, rfsize, NULL) と使っていますが、rfsize は読み出したデータのバイト長を格納するDWORDの変数へのポインタを指定しなければなりません。このコードですとNULLを渡していて、API仕様的には不正です。

    ReadFile DWORD lpNumberOfBytesRead
    https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-readfile
    This parameter can be NULL only when the lpOverlapped parameter is not NULL.
    とあるように、NULLがOKなのは、非同期モードでReadFileを呼び出すときだけです。このコードをWindows 7で動作させるとNULLポインターアクセスでエラーが起き、落ちました。質問者さんのWindows10 で問題ないのは、API側のガードが強くなっているのだと思います。

    キャンセル

  • 2019/04/15 20:28

    rfsizeのパラメータがMessageBoxで回収できなかったので不思議に思っていました.ありがとうございます.

    キャンセル

同じタグがついた質問を見る

  • C

    4386questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • C++

    4304questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • Win32 API

    283questions

    Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。