2016-12-29 105 views
0

我只是想知道,如果在Windows中從字節數組(緩衝區)加載HICON的API?假設我下載了一個*.ico文件,並且我在某個緩衝區中包含了這個文件的內容。我希望能夠從該緩衝區創建HICON從緩衝區(* .ico文件)加載HICON

可以從*.ico加載HICON放置在硬盤上,所以我想應該有一個同樣簡單的方法來從內存緩衝區中做到這一點?

到目前爲止,我只找到2個解決方案,但沒有一個適合我。

第一個involved ATL usage and GDI+(我正在使用Rust,我沒有任何與GDI +的綁定)。

第二個是基於使用LookupIconIdFromDirectoryEx()CreateIconFromResourceEx()。首先,我撥打LookupIconIdFromDirectoryEx()來獲取正確圖標的偏移量,然後嘗試呼叫CreateIconFromResourceEx()(和CreateIconFromResource())獲取HICON,但在所有情況下,我都收到NULL值,因此GetLastError()返回0。我對這些函數的使用是基於this article(我試圖通過不僅0作爲第二個參數,但也是數組緩衝區的大小,不包括偏移量,但它仍然失敗)。

我唯一想到的解決方案是手動解析*.ico文件,然後從中提取PNG圖像,然後使用描述的方法here從PNG圖像創建一個圖標。但它似乎更像是一種解決方法(儘管Qt使用類似的方法,也許他們無法找到不同的解決方案)。有沒有更簡單的方法(可能是一些WinAPI調用)來完成任務?

UPD。下面是我嘗試過的一些測試代碼(爲了運行沒有崩潰的例子,你應該有一個圖標)。

#include <cstdio> 
#include <cstdlib> 
#include <Windows.h> 

#pragma comment(lib, "User32.lib") 

int main() 
{ 
    // Read the icon into the memory 
    FILE* f = fopen("icon.ico", "rb"); 
    fseek(f, 0, SEEK_END); 
    long fsize = ftell(f); 
    fseek(f, 0, SEEK_SET); 
    char* data = (char*)malloc(fsize + 1); 
    fread(data, fsize, 1, f); 
    fclose(f); 

    static const int icon_size = 32; 
    int offset = LookupIconIdFromDirectoryEx((PBYTE)data, TRUE, icon_size, icon_size, LR_DEFAULTCOLOR); 
    if (offset != 0) { 
     HICON hicon = CreateIconFromResourceEx((PBYTE)data + offset, 0, TRUE, 0x30000, icon_size, icon_size, LR_DEFAULTCOLOR); 
     if (hicon != NULL) { 
      printf("SUCCESS"); 
      return 0; 
     } 
    } 

    printf("FAIL %d", GetLastError()); 
    return 1; 
} 
+0

看起來您的代碼中存在一個錯誤。除非你提供[mcve],否則我們無法幫助你。 – IInspectable

+0

修復你的代碼而不是放棄 –

+0

@IInspectable,我沒有在最初添加它,因爲它基本上與我鏈接的文章中的相同。但是現在我更新了描述並附上源代碼。在我的情況下,它總是打印「失敗0」。 – ScienceSE

回答

-2

我找到了解決方案。事實上,經過一番研究後發現,我在樣本中放置的代碼確實是正確的。

WinAPI函數LookupIconIdFromDirectoryEx()中存在一個錯誤。我注意到,對於某些圖標,我可以得到正確的圖標並進行設置,但對於其他圖標,它可能會在後面的階段CreateIconFromResourceEx()或更早的LookupIconIdFromDirectoryEx()上失敗。我注意到,即使圖標在文件中,有時該函數也無法找到該圖標。有時函數爲圖標文件中的不同圖標返回相同的值。

我做了幾輪測試,並根據format definition分析了每個圖標文件的格式。然後我將實際偏移與LookupIconIdFromDirectoryEx()返回的值進行比較。

比方說,我們有2個圖標:AB

在我的情況下的A圖標含有5個圖像,ICO文件內的條目置於順序如下:

  1. 256x256的PNG
  2. 128x128的BMP
  3. 64×64 BMP
  4. 32×32 BMP
  5. 16x16 BMP

B圖標含有7倍的圖像,它們被安置在下面的命令:

  1. 16×16 BMP
  2. 32×32 BMP
  3. 48x48的BMP
  4. 64×64 BMP
  5. 128x128的BMP
  6. 256x256的BMP

結果LookupIconIdFromDirectoryEx() fo每個圖標都可以在下面找到。

圖標A

圖標B

  1. 未找到(函數失敗並返回0
  2. 未找到(函數失敗並返回0
  3. 未找到(函數失敗和返回0

我根據definition in wikipedia(下面的表格包含圖標條目,每行是一個單獨的條目,每一列是這個條目的一個字段)解析實際格式,這兩個圖標文件都是。

A的實際佈局是:

W  H  * * * **  SIZE  OFFSET 
------------------------------------------------ 
0  0  0 0 1 32  43253 86 
128 128 0 0 1 32  67624 43339 
48 48 0 0 1 32  9640  110963 
32 32 0 0 1 32  4264  120603 
16 16 0 0 1 32  1128  124867 

B的實際佈局是:

W  H  * * * **  SIZE  OFFSET 
------------------------------------------------ 
16 16 0 0 0 32  1128  102 
32 32 0 0 0 32  4264  1230 
48 48 0 0 0 32  9640  5494 
64 64 0 0 0 32  16936 15134 
128 128 0 0 0 32  67624 32070 
0  0  0 0 0 32  270376 99694 

所以,你可以清楚地看到,在A情況下,只有第一個圖像偏移是正確的,其他圖像的偏移量不正確,等於第三張圖像的大小(??),也許只是巧合。

在第二張圖像的情況下,所有的偏移都是正確的,直到我們達到128x128的圖像,甚至沒有找到。

這兩種情況之間的共同之處在於,函數在解析128x128圖標後開始顯得很奇怪,這裏有一件有趣的事情 - 查看128x128圖標的大小並將其與其他圖像的大小進行比較。在這兩種情況下,128x128圖像的大小都不適合2個字節。在解析其中大小大於2個字節的圖標條目之後,函數行爲未定義。從這些數據來看,我可以假設代碼中的某處他們期望圖標的大小不能大於2個字節。如果尺寸較大,則行爲未定義。

我用ImageMagick重新組裝了一個新的圖標,裏面沒有這樣的圖像,現在該功能在所有情況下都能正確工作。

所以我可以肯定地說LookupIconIdFromDirectoryEx()執行中有一個bug。

UPD。圖標可以在這裏找到:A icon,B icon

+0

非常不可能,Windows API在這裏有一個bug。更可能是你誤解了結果。帶有PNG圖像的圖標與帶有BMP圖像的圖標非常不同。 Raymond Chen有關於ICO文件格式的多部分系列。 [ICO文件格式的演變,第4部分:PNG圖像](https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473)可能有助於理解您觀察的結果。 – IInspectable

+0

@IInspectable,但結果表明,即使我使用僅包含BMP圖像的圖標,它也不能正常工作。在我的答案中查看圖標文件'B',當我嘗試搜索大於128x128像素的圖標時,該功能失敗。 – ScienceSE

+0

我最初問的是從ICO文件中加載'HICON'的方式,但似乎沒有其他可靠的解決方案(至少我沒有聽到別人的答案,我也無法找到更好的解決方案我)。爲我工作的唯一方法就是我描述的那個。然後我做了一個小研究,找出失敗的原因。奇怪的是,我的答案收到了太多的反對票而沒有解釋他們的理由。 – ScienceSE