我的 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);
大概是这样的:
注意事项,不分先后:
图像加载使用LoadImageA
和LR_LOADFROMFILE
选项以允许传入文件系统名称。
文件系统名称作为字符串切片传递,它会自动转换为适当的 API 类型并终止 NUL。
LoadImageA
返回一个 HANDLE
类型的值,需要手动转换为 HBITMAP
。这就是时髦的HBITMAP(LoadImageA(...).0)
所做的。
大多数允许您传递语义上可为 NULL 的值的 API 都实现了从 None
转换的功能。
资源(如屏幕 DC)被正确请求和释放。标记为// Cleanup
的部分中的操作顺序很重要。
需要使用ReleaseDC
或DeleteDC
清理设备上下文,具体取决于获取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 脚本不渲染位图(或不创建位图)但也不会崩溃,谁能帮我查明问题?的主要内容,如果未能解决你的问题,请参考以下文章