0

hey folks can anyone help me a bit? I have the problem w/ this snippet:


fn main() -> windows::Result<()> {

    unsafe {
        
        let h_bmp = LoadBitmapA(None,"C:\\Users\\grass\\Desktop\\codes\\Rust\\catso\\src\\OIP.jpg"); // does that load bitmap? 
        let hdc = CreateCompatibleDC(HDC::NULL); // It suppose to have default 1px monochrome Bitmap, does it change on select object?

        SelectObject(hdc,h_bmp); // Do I need to create bitmap in the hdc and fill it from file smh?

        let time_back: SystemTime = SystemTime::now();

        while SystemTime::now().duration_since(time_back).unwrap().as_secs() < 10 as u64 {
            BitBlt(HDC::NULL, 0, 0, 474, 266, hdc, 0, 0, ROP_CODE(13)); // is ROP_CODE(13) refers to SRCCOPY?
        }


        println!("DELETED: {:?}",DeleteDC(hdc));


    }

    Ok(())
}

I think that there are several places where it can go wrong so I commented them. I don't get compiler errors so I'm bit in the dark

Update: I`ve realized that I can use return type of BitBlt to do further debugging so I adjusted the code like so


println!("succeeded? : {:?}",BitBlt(HDC::NULL, 0, 0, 474, 266, hdc, 0, 0, ROP_CODE(13)));

and it yielded BOOL(0), which I assume is equivalent to false, and hence I assume the issue is with BitBlt? Can it be that I need some extra arguments?

Sequentially I tried using GetLastError and found out that I have invalid handle error (6).

Update: So I tried printing the results of LoadBitmapA and it returns null, I've looked over generated docs and realized I had to change arguments to

 let h_bmp = LoadBitmapA(HINSTANCE::NULL,PSTR(bmp_name));

Sadly I'm not sure what bmp_name should be here, as windows API webpage seem to indicate its a string but rust docs claim its a mut u8. I do know that u8 is a numeric used in utf_8 but it seems that it needs just one mutable u8 rather then an array as the only thing it does error out on is

let h_bmp = LoadBitmapA(HINSTANCE::NULL,PSTR(&mut (2 as u8)));

.

Update: Ok, so as suggested by Aiden4 in comments I looked at Cstring documentation but I didn't find a way to convert it into *mut u8. Closes I found was

let h_bmp = LoadBitmapA(HINSTANCE::NULL,
                                PSTR(CString::new("C:\\Users\\grass\\Desktop\\codes\\Rust\\catso\\src\\OIP.jpg").unwrap().into_raw()));

which resulted in the *mut i8, casting it with

as *mut u8 

does work but doesn't resolve the issue while not casting gives a compiler error.

Update: So after being pointed out that jpeg file will not work, and that its enough to pass the string slice as well as advice to append \0 to the end of the slice I've adjusted my snippet accordingly:

let h_bmp = LoadBitmapA(HINSTANCE::NULL,"C:\\Users\\grass\\Desktop\\codes\\Rust\\catso\\src\\OIP.bmp\0");

Which sadly still doesnt show the bitmap. Here is a bitmap file: my bitmap

Nikolai Savulkin
  • 697
  • 5
  • 12
  • What's the expected behavior, and what does the code actually do? – Aiden4 May 30 '21 at 19:46
  • @Aiden4 i want it to draw a bitmap on top of my screen (on top of other apps as well) - it does nothing, runs 10 seconds and quits. – Nikolai Savulkin May 30 '21 at 19:47
  • Running it with elevated access didn't help either. – Nikolai Savulkin May 30 '21 at 19:49
  • Try printing out the result of the `LoadBitmapA` function. – Aiden4 May 30 '21 at 20:31
  • @Aiden4 I already did - it is null, so I found some flaws from misreading docs. Ill update the question now. – Nikolai Savulkin May 30 '21 at 20:34
  • @Aiden4 check the question now, I've mentioned the outcome of what you suggested. – Nikolai Savulkin May 30 '21 at 20:36
  • The `*mut u8` is a c-style string. Reading the docs for `std::ffi::CString` should be helpful there. – Aiden4 May 30 '21 at 20:53
  • [LoadBitmapA](https://microsoft.github.io/windows-docs-rs/doc/bindings/Windows/Win32/Graphics/Gdi/fn.LoadBitmapA.html) takes a conversion trait ([IntoParam](https://microsoft.github.io/windows-docs-rs/doc/windows/trait.IntoParam.html)) that will auto-convert from a string slice. There's an [FAQ](https://github.com/microsoft/windows-rs/blob/master/docs/FAQ.md) entry with more information. – IInspectable May 30 '21 at 21:25
  • You will want to zero-terminate that string slice, though, i.e. `"C:\\Users\\...\\OIP.jpg\0"`. Though [LoadBitmap](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadbitmapa) will not load JPGs. – IInspectable May 30 '21 at 21:36
  • @IInspectable so since its a trait it functions like interface in java more or less just notifying the underlying code that there is a method which can be used for string slice, which means I just have to pass a string slice? – Nikolai Savulkin May 30 '21 at 21:58
  • because afaik that what traits are, some common functionality which can be implied for structs w. certain fields – Nikolai Savulkin May 30 '21 at 21:58

2 Answers2

1

There are lots of issues with the code, that cause it to fail. First and foremost, LoadBitmapA is an ancient API that can load BMP images from module resources only. Its lpBitmapName refers to a resource name or ID. It will not work with filesystem paths.

If you can convert your source image to the BMP file format, you can use LoadImage instead. This API is capable of loading images from disk. Excluding error reporting, the following code

use bindings::Windows::Win32::{
    Graphics::Gdi::*, UI::Controls::*, UI::WindowsAndMessaging::*
};

fn main() {
    unsafe {
        let bmp = HBITMAP(
            LoadImageA(
                None,
                "C:\\Users\\...\\img.bmp",
                IMAGE_BITMAP,
                0,
                0,
                LR_LOADFROMFILE | LR_CREATEDIBSECTION,
            )
            .0,
        );

        let dc_src = CreateCompatibleDC(None);
        let bmp_prev = SelectObject(dc_src, bmp);
        let dc_dst = GetDC(None);

        BitBlt(dc_dst, 0, 0, 1000, 1000, dc_src, 0, 0, SRCCOPY);

        // Cleanup
        ReleaseDC(None, dc_dst);
        SelectObject(dc_src, bmp_prev);
        DeleteDC(dc_src);
        DeleteObject(bmp);
    }
}

produces roughly this:

Screencapture

Things to note, in no particular order:

  • Image loading uses LoadImageA with the LR_LOADFROMFILE option to allow passing in a filesystem name.
  • The filesystem name is passed as a string slice, that gets automatically converted to the appropriate API type and NUL terminated.
  • LoadImageA returns a value of type HANDLE that needs to be manually converted into an HBITMAP. That's what the funky HBITMAP(LoadImageA(...).0) does.
  • Most APIs that allow you to pass values that can be semantically NULL implement the ability to convert from None.
  • Resources (like the screen DC) are properly requested and released. The order of operations in the section labeled // Cleanup matters.
  • Device contexts need to be cleaned up using ReleaseDC or DeleteDC depending on how the DC was acquired. The Windows crate prevents you from accidentally calling DeleteDC on a DC acquired from GetDC. For example, DeleteDC(dc_dst); will fail to compile.
  • There is absolutely no error handling. The Windows crate offers some convenience helpers there, if you are looking for a more robust solution.

The above works for BMP images. If you do need to load other formats, things get significantly more involved. While not terribly complex, implementing an image loader using the Windows Imaging Component in Rust is a fair bit out of scope of this question. If that is something you need you should ask a new question.

IInspectable
  • 46,945
  • 8
  • 85
  • 181
-1

According to LoadBitmap, the function loads .bmp resources from the instance of the module whose executable file contains the bitmap to be loaded but this doesn't work with PNG files. And this thread shows a solution that load bitmap from files.
Code:

HBITMAP GetHBITMAPFromImageFile(LPCWSTR pFilePath)
{
    Gdiplus::GdiplusStartupInput gpStartupInput;
    ULONG_PTR gpToken;
    GdiplusStartup(&gpToken, &gpStartupInput, NULL);
    HBITMAP result = NULL;
    Gdiplus::Bitmap* bitmap = Gdiplus::Bitmap::FromFile(pFilePath, false);
    if (bitmap)
    {
        bitmap->GetHBITMAP(Gdiplus::Color(255, 255, 255), &result);
        delete bitmap;
    }
    Gdiplus::GdiplusShutdown(gpToken);
    return result;
}
    
int DrawAnImage(HWND hWnd)
{
    HDC hdcWindow = GetDC(hWnd);
    
    HBITMAP hbmp = GetHBITMAPFromImageFile(L"C:\\wUceJ.png");
    //HBITMAP hbmp = LoadBitmap(hInst, MAKEINTRESOURCEW(IDB_BITMAP1));//In Resource OK
    HDC hdc = CreateCompatibleDC(hdcWindow); // It suppose to have default 1px monochrome Bitmap, does it change on select object?
    SelectObject(hdc, hbmp);
    
    BITMAP bmpScreen;
    GetObject(hbmp, sizeof(BITMAP), &bmpScreen);
        
    // Bit block transfer into our compatible memory DC.
    if (!BitBlt(hdcWindow,
        0, 0,
        bmpScreen.bmWidth, bmpScreen.bmHeight,
        hdc,
        0, 0,
        SRCCOPY))
    {
        OutputDebugString(L"FAILED");
    }
    DeleteObject(hbmp);
    DeleteDC(hdc);
    ReleaseDC(hWnd, hdcWindow);
    return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: Add any drawing code that uses hdc here...
            DrawAnImage(hWnd);
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

enter image description here

Jmb
  • 18,893
  • 2
  • 28
  • 55
YangXiaoPo-MSFT
  • 1,589
  • 1
  • 4
  • 22
  • There is no GDI+ in Rust. – IInspectable May 31 '21 at 07:57
  • [Load a PNG image using Win32/GDI](https://stackoverflow.com/questions/4567875/how-would-i-load-a-png-image-using-win32-gdi-no-gdi-if-possible) Or add the bitmap to the .rc resource file then load from the resource `HBITMAP hbmp = LoadBitmap(hInst, MAKEINTRESOURCEW(IDB_BITMAP1));` – YangXiaoPo-MSFT May 31 '21 at 08:43
  • Either way you're going to have to decode the JPG/PNG. The Windows Imaging Component allows you to do that. Using it from Rust is certainly non-trivial. – IInspectable May 31 '21 at 08:47