我想从内存加载一个以.ani格式存储的动画光标,该格式被描述为RIFF存档/容器,而不将内存写入临时文件.到目前为止,我能够解析.ani文件结构并借助于
CreateIconFromResourceEx
LookupIconIdFromDirectoryEx将各个帧加载为普通图标
证明困难的问题之一是这些帧的实际组成和动画数据(jiffy-rate等),因为在Windows API中似乎没有条目这样做.关于主题的文档或书面知识似乎仅限于从内存加载非动画图标/光标.
像‘Load an embedded animated Cursor from the Resource’这样的类似问题表达了从可嵌入资源加载动画光标的愿望.但是,我也无法从中重现可行的解决方案.部分原因是因为Visual Studio 2008中的资源编译器和2010年不支持.ani(仅限ico和cur)文件,因此嵌入它只会产生原始文件中1:1字节的副本,而不是.cur和.ico文件会被分解为多个资源.随后对两个答案中显示的CreateIconFromResource的调用都不起作用,因为它所需的数据是图标存档中单个指令的图标/光标数据,而不是基于RIFF的文件结构.
总结一下,我对任何有关动画游标结构(内存中)或其他相关指针的信息感兴趣,这些信息可以帮助我实现目标.
解决方法
在重新评估从mfc指出的可嵌入资源加载动画光标后,我找到了一个允许我从任意内存地址加载游标的解决方案.
来自可嵌入资源的数据和从文件读入内存的数据完全相同.因此,它表明CreateIconFromResource也应该在正常的常规内存上工作.然而,有一个根本区别.可嵌入资源的内存驻留在可执行文件的特殊部分中,这些部分通常填充到最近的4096字节边界.在运行时分配的内存包含垃圾值.
现在,我发现工作的解决方案通过简单地分配零填充字节的保护带来利用它.在我自己的测试用例中,我发现8是最小值,也恰好是riff容器中块的大小.巧合?我怀疑这是一个错误,并且算法恰好适用于可嵌入资源,因为它在dll /可执行文件中具有对齐限制.
const int guardbandSize = 8; FILE* fs = fopen("action.ani","rb"); fseek(fs,SEEK_END); int dwSize = ftell(fs); fseek(fs,SEEK_SET); char* memory = new char[dwSize + guardbandSize]; fread(memory,1,dwSize,fs); memset(memory + dwSize,guardbandSize); fclose(fs); cursor = (HCURSOR)CreateIconFromResource((PBYTE)memory,FALSE,0x00030000); delete memory;
以下是加载动画光标的各种方法的概述.
#include <Windows.h> #include <stdio.h> #include "resource2.h" void* hWnd; bool visible = true; bool running = true; LRESULT CALLBACK WndProcInternal( HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam) ; HCURSOR cursor = 0; void main() { //Setup configuration const int Width = 640,Height = 480; const int Method = 4; //Setup window class WNDCLASS wcd; wcd.style = CS_PARENTDC | CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wcd.lpfnWndProc = (WNDPROC)WndProcInternal; wcd.cbClsExtra = 0; wcd.cbWndExtra = 0; wcd.hInstance = GetModuleHandle(NULL); wcd.hIcon = LoadIcon(NULL,IDI_WINlogo); wcd.hCursor = LoadCursor(NULL,IDC_ARROW); wcd.hbrBackground = (HBRUSH)COLOR_BACKGROUND; wcd.lpszMenuName = NULL; wcd.lpszClassName = TEXT("AnimatedIcon"); //Register the window class if(!RegisterClass(&wcd)) { MessageBox(NULL,TEXT("Window Registration Failed!"),TEXT("Error!"),MB_ICONEXCLAMATION | MB_OK); FatalExit(-1); } //Create a window if (!(hWnd=CreateWindowEx(WS_EX_CLIENTEDGE,TEXT("AnimatedIcon"),WS_VISIBLE | WS_CAPTION | WS_MINIMIZEBox | WS_THICKFRAME | WS_MAXIMIZEBox | WS_SYSMENU,Width,Height,NULL,NULL))) { MessageBoxA(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION); FatalExit(-1); } if( Method == 1 ) { //Method 1: Load cursor directly from a file cursor = LoadCursorFromFileA("action.ani"); } if( Method == 2 ) { //Method 2: Load cursor from an resource section in the executable. cursor = LoadCursor(0,MAKEINTRESOURCE(IDR_ANICURSORS1)); } if( Method == 3 ) { //Method 3: Manually locate the resource section in the executable & create the cursor from the memory. HINSTANCE hInst=GetModuleHandle(0); HRSRC hRes=FindResourceA(hInst,MAKEINTRESOURCEA(IDR_ANICURSORS1),"ANICURSORS"); DWORD dwSize=SizeofResource(hInst,hRes); HGLOBAL hGlob=LoadResource(hInst,hRes); LPBYTE pBytes=(LPBYTE)LockResource(hGlob); cursor = (HCURSOR)CreateIconFromResource(pBytes,0x00030000); } if( Method == 4 ) { //Method 4: Load the cursor from a file into memory & create the cursor from the memory. const int guardbandSize = 8; FILE* fs = fopen("action.ani","rb"); fseek(fs,SEEK_SET); char* memory = new char[dwSize + guardbandSize]; fread(memory,guardbandSize); fclose(fs); cursor = (HCURSOR)CreateIconFromResource((PBYTE)memory,0x00030000); delete memory; } //Set the cursor for the window and display it. SetClassLong((HWND)hWnd,GCL_HCURSOR,(LONG)cursor); while (running) { MSG wmsg; if (PeekMessage(&wmsg,(HWND)hWnd,PM_REMOVE)) { TranslateMessage(&wmsg); DispatchMessage(&wmsg); } } } LRESULT CALLBACK WndProcInternal( HWND hWnd,LPARAM lParam) { if( uMsg == WM_DESTROY ) { PostQuitMessage(1); running = false; } return (long)DefWindowProc((HWND)hWnd,uMsg,(WPARAM)wParam,(LPARAM)lParam); }