我的 windows-rs 脚本不渲染位图(或不创建位图)但也不会崩溃,谁能帮我查明问题?

Posted

技术标签:

【中文标题】我的 windows-rs 脚本不渲染位图(或不创建位图)但也不会崩溃,谁能帮我查明问题?【英文标题】:My windows-rs script doesn't render bitmap (or doesn't create one) but doesn't crash either, can anyone help me pinpointing the issue? 【发布时间】:2021-08-18 06:04:38 【问题描述】:

大家好,谁能帮帮我?我有这个 sn-p 的问题:


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(())


我认为有几个地方可能会出错,所以我评论了它们。我没有得到编译器错误,所以我有点不知所措

更新: 我已经意识到我可以使用 BitBlt 的返回类型来做进一步的调试,所以我像这样调整了代码


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

它产生了 BOOL(0),我认为它等同于 false,因此我认为问题出在 BitBlt?是不是我需要一些额外的参数?

随后我尝试使用 GetLastError 并发现我有无效句柄错误 (6)。

更新: 所以我尝试打印 LoadBitmapA 的结果,它返回 null,我查看了生成的文档并意识到我必须将参数更改为

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

遗憾的是,我不确定 bmp_name 应该在这里,因为 Windows API 网页似乎表明它是一个字符串,但 rust docs 声称它是一个 mut u8。我确实知道 u8 是 utf_8 中使用的数字,但它似乎只需要一个可变的 u8 而不是一个数组,因为它唯一会出错的是

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

.

更新: 好的,正如 Aiden4 在 cmets 中所建议的那样,我查看了 Cstring 文档,但我没有找到将其转换为 *mut u8 的方法。我发现的关闭是

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

导致*mut i8,使用

as *mut u8 

确实有效,但不能解决问题,而不是强制转换会导致编译器错误。

更新: 因此,在被指出 jpeg 文件不起作用,并且它足以传递字符串切片以及将 \0 附加到切片末尾的建议之后,我相应地调整了我的 sn-p:

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

遗憾的是仍然没有显示位图。 这是一个位图文件:

【问题讨论】:

预期的行为是什么,代码实际做了什么? @Aiden4 我希望它在我的屏幕顶部(也在其他应用程序顶部)绘制位图 - 它什么也不做,运行 10 秒然后退出。 以提升的访问权限运行它也没有帮助。 尝试打印出LoadBitmapA函数的结果。 @Aiden4 我已经这样做了——它是空的,所以我发现了一些误读文档的缺陷。我现在更新问题。 【参考方案1】:

代码有很多问题,导致它失败。首先,LoadBitmapA 是一个古老的 API,它只能从模块资源中加载 BMP 图像。它的lpBitmapName 指的是一个资源 名称或ID。它不适用于文件系统路径。

如果您可以将源图像转换为 BMP 文件格式,则可以改用 LoadImage。此 API 能够从磁盘加载图像。排除报错,如下代码

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);
    

大概是这样的:

注意事项,不分先后:

图像加载使用LoadImageALR_LOADFROMFILE 选项以允许传入文件系统名称。 文件系统名称作为字符串切片传递,它会自动转换为适当的 API 类型并终止 NUL。 LoadImageA 返回一个 HANDLE 类型的值,需要手动转换为 HBITMAP。这就是时髦的HBITMAP(LoadImageA(...).0) 所做的。 大多数允许您传递语义上可为 NULL 的值的 API 都实现了从 None 转换的功能。 资源(如屏幕 DC)被正确请求和释放。标记为// Cleanup 的部分中的操作顺序很重要。 需要使用ReleaseDCDeleteDC 清理设备上下文,具体取决于获取DC 的方式。 Windows crate 可防止您在从 GetDC 获得的 DC 上意外调用 DeleteDC。例如,DeleteDC(dc_dst); 将无法编译。 绝对没有错误处理。如果您正在寻找更强大的解决方案,Windows crate 提供了一些便利的帮助。

以上适用于 BMP 图像。如果您确实需要加载其他格式,事情就会变得更加复杂。虽然不是很复杂,但在 Rust 中使用 Windows 映像组件实现图像加载器有点超出了这个问题的范围。如果这是您需要的,您应该提出一个新问题。

【讨论】:

【参考方案2】:

根据LoadBitmap,该函数从可执行文件包含要加载的位图的模块实例中加载.bmp resources,但这不适用于PNG 文件。 this thread 展示了从文件加载位图的解决方案。 代码:

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;

【讨论】:

Rust 中没有 GDI+。 Load a PNG image using Win32/GDI 或者将位图添加到.rc资源文件然后从资源HBITMAP hbmp = LoadBitmap(hInst, MAKEINTRESOURCEW(IDB_BITMAP1));加载 无论哪种方式,您都必须解码 JPG/PNG。 Windows 映像组件允许您执行此操作。在 Rust 中使用它当然不是一件容易的事。

以上是关于我的 windows-rs 脚本不渲染位图(或不创建位图)但也不会崩溃,谁能帮我查明问题?的主要内容,如果未能解决你的问题,请参考以下文章

您如何将 PostMessage 与 windows-rs 板条箱一起使用?

XML 位图渲染不好?

OpenGL高度图地形渲染不绘制

OpenGL FreeType2 位图不渲染

bash shell和进程

如何使用 SwiftUI 连续渲染软件位图?