Displaying an Image

I am trying to create a console controlled program, that receives an input command, creates a window and displays a .bmp picture in the window, and then is closed/destroyed with another command to the console input.
Using "theForger's Win32 API Programming Tutorial" ( http://www.winprog.org/tutorial/ ) as a primary resource, I can create and destroy the window successfully, and call for it to update with WM_PAINT.
Since the window function is blocking until WM_CLOSE is received, I am running my cin input function in a separate thread.
The issue is that LoadBitmap(hInstance, L"picture.bmp"); (Line 27) won't load the .bmp file. I have made sure that the bmp file is saved in the same folder as the .vcxproj file and that the names match.
I have looked through all the docs.microsoft.com documentation I can find, and scoured the forums to the best of my ability, but I seem to still be making a fundamental mistake somewhere.

If you can point out some glaring or obvious error it would be much appreciated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// bmpDisplay2.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <iostream>
#include<windows.h>
#include <thread>

using namespace std;
bool running = true;

const wchar_t g_szClassName[] = L"myClass";
HINSTANCE hInstance; HINSTANCE hPrevInstance; LPSTR lpCmdLine; int nCmdShow = 1;
HBITMAP picture = NULL;
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
    case WM_CREATE:
        break;
    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_PAINT:
        cout << "UPDATED\n";
    {
        picture = LoadBitmap(hInstance, L"screenShot.bmp");
        if (picture == NULL){ 
            cout << "picture didn't load\n"; 
        }
        BITMAP bm;
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);
        HDC hdcMem = CreateCompatibleDC(hdc);
        HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, picture);
        GetObject(picture, sizeof(bm), &bm);
        BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
        SelectObject(hdcMem, hbmOld);
        DeleteDC(hdcMem);
        EndPaint(hwnd, &ps);
    }
    break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int screenShare(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = LPCWSTR(g_szClassName);
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if (!RegisterClassEx(&wc)) {
        cout << "window registration failed";
        return 0;
    }
    hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, g_szClassName, L"Window Name", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 600, 600, NULL, NULL, hInstance, NULL);
    if (hwnd == NULL) {
        cout << "window registration failed";
        return 0;
    }
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    while (GetMessage(&Msg, NULL, 0, 0) > 0) {
        //cout << "message " << Msg.message << endl;
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

void commander() {
    while (running) {
        string input;
        cin >> input;
        if (input == "update") {
            PostMessage(hwnd, WM_PAINT, 0, 0);
        }
        else if (input == "exit") {
            running = false;
            PostMessage(hwnd, WM_CLOSE, 0, 0);
        }
        else {
            cout << "Command not recognized\n";
        }
    }
}
int main() {
    thread command(commander);
    screenShare(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
    while (running) {}
    command.join();
}
Last edited on
are you getting line 29 picture didn't load message, or some other problem?
@jonnin that's right, I put that little check in to confirm if anything had been loaded, but the console always prints "picture didn't load". Also when the window comes up it stays blank.
https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getcurrentdirectory
Are you looking in the right place for the file?

Windows has some cockamamie "rules" for what the current directory is depending on how you choose to run the program.

Use the above to print out where it tried to find the file.

https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
Also, get into the habit of printing out why something failed.
Knowing that it was "permission denied" as opposed to "file not found" would be a valuable clue to what your next step should be.
^^^ Take the easy way out and put the path on the picture file for a test run. If that works, its just a pathing problem that will clear up if run normally (not from the dev environment).
If I remember correctly the LoadBitmap function loads the specified bitmap resource from a module's executable file. If you are loading from disk I think you need LoadImage.
Grey Wolf has it exactly right. Remember kids, always read the documentation.

https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadimagew

For reference, you can fix your code to use it thus:
 
        picture = (HBITMAP)LoadImageW(NULL, L"screenShot.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

Something to note: the created window does not match the dimensions of the image you create.
Also, what happens when you close the window? (Because it leaves things in an odd state as the code currently is.)

Hope this helps.
LoadIcon, LoadCursor and LoadBitmap have been deprecated, MS recommends loading an image with LoadImage whether from a resource or a file. That includes icons and cursors.
salam c & jonnin Thanks for the advice! Your suggestions seem like good best practices that I will start using from now on.

Grey Wolf & Duthomhas & Furry Guy. This was very insightful, switching to LoadImage did the trick. The program still isn't doing exactly what I want it to, but I've got some good paths to explore now that I can at least get the image to display. Thanks for all the help


PS: I've posted to other coding forums in the past, and usually find the help or advice I need, but also get a good deal of sh*t-posting and just general unpleasantness. This was the first time I posted to these forums and I have to say this is an amazing community. If there's anything I can do to encourage, support and, contribute to this great community please let me know.
You know... I almost hate to suggest it...

but you could blow peoples' minds and just display the image on the console itself...


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <iostream>
#include <windows.h>

#pragma comment(lib, "user32")
#pragma comment(lib, "gdi32")


void GotoXY( int x, int y )
{
  HANDLE hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
  if (hStdOut == INVALID_HANDLE_VALUE) return;
  
  COORD xy = { (SHORT)x, (SHORT)y };
  SetConsoleCursorPosition( hStdOut, xy );
}


void ClearScreen()
{
  HANDLE                     hStdOut;
  CONSOLE_SCREEN_BUFFER_INFO csbi;
  DWORD                      count;
  DWORD                      cellCount;
  COORD                      homeCoords = { 0, 0 };

  hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
  if (hStdOut == INVALID_HANDLE_VALUE) return;

  /* Get the number of cells in the current buffer */
  if (!GetConsoleScreenBufferInfo( hStdOut, &csbi )) return;
  cellCount = csbi.dwSize.X *csbi.dwSize.Y;

  /* Fill the entire buffer with spaces */
  if (!FillConsoleOutputCharacter( hStdOut, (TCHAR) ' ', cellCount, homeCoords, &count )) return;

  /* Fill the entire buffer with the current colors and attributes */
  if (!FillConsoleOutputAttribute( hStdOut, csbi.wAttributes, cellCount, homeCoords, &count )) return;

  /* Move the cursor home */
  SetConsoleCursorPosition( hStdOut, homeCoords );
}


COORD GetConsoleWindowSize()
{
  HANDLE hStdOut = GetStdHandle( STD_OUTPUT_HANDLE );
  if (hStdOut == INVALID_HANDLE_VALUE) return { 0, 0 };
  
  CONSOLE_SCREEN_BUFFER_INFO csbi;
  if (!GetConsoleScreenBufferInfo( hStdOut, &csbi )) return { 0, 0 };
  
  return
  { 
    csbi.srWindow.Right - csbi.srWindow.Left + 1, 
    csbi.srWindow.Bottom - csbi.srWindow.Top + 1
  };
}


int main( int argc, char** argv )
{
  if (argc != 2) return 1;
  
  // First we'll clear the console and position our cursor for future update
  // (We do this NOW since anything we do will likely clobber some of the image we draw)
  
  ClearScreen();
  COORD size = GetConsoleWindowSize();
  GotoXY( 0, size.Y - 3 );  
  Sleep(150); // wait a moment for the above commands to update to the console
  
  
  // Here we draw on the console.
  // Remember, the console does not care about what we draw on it.
  // Anything we ask the console to do later may clobber all or part of it.
  
  HWND hconsole = GetConsoleWindow();
  if (!hconsole) return 2;
  
  HBITMAP hbitmap = (HBITMAP)LoadImageA( NULL, argv[1], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
  if (!hbitmap) return 3;
  
  BITMAP bm;
  GetObject( hbitmap, sizeof(bm), &bm );
  
  HDC hdc = GetDC(hconsole);
  {
    HDC hdc_bitmap = CreateCompatibleDC( hdc );
    SelectObject(hdc_bitmap, hbitmap);
    {
      BitBlt( hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdc_bitmap, 0, 0, SRCCOPY );
    } 
    DeleteDC( hdc_bitmap );
  }
  
  
  // Write information. Wait for user to press ENTER.
  
  std::cout
    <<   "image width  = " << bm.bmWidth 
    << "\nimage height = " << bm.bmHeight
    << "\nPress ENTER...";

  std::cin.ignore(~0UL, '\n');
}

Compile with

    cl /EHsc /W4 /Ox /std:c++17 a.cpp

Run it by specifying the path to the image to display as argument.

Oh, also, notice I am using LoadImageA() here because of main()’s argument types. There is no reason you couldn’t use the wide version to match your program’s needs.

Enjoy!
Topic archived. No new replies allowed.