将 HICON / HCURSOR 复制到字节数组中
Posted
技术标签:
【中文标题】将 HICON / HCURSOR 复制到字节数组中【英文标题】:Copy HICON / HCURSOR in to Byte Array 【发布时间】:2019-08-28 09:07:39 【问题描述】:有什么方法可以将HICON
或HCURSOR
转换为字节数组,我一直在谷歌上搜索我没有找到一个通用的解决方案,下面我尝试将HICON
颜色和掩码BITMAP 转换为字节数组并通过套接字发送它并使用CreateIconIndirect
API 创建我的图标,但如果我能够直接发送HICON
,而不是做所有这些事情会很好。
int ProcessMouse()
BYTE m_hbmMaskBits[70000];
BYTE m_hbmColorBits[70000];
CURSORINFO CursorInfo;
CursorInfo.cbSize = sizeof(CursorInfo);
GetCursorInfo(&CursorInfo);
ICONINFO iconInfo;
if (!GetIconInfo(CursorInfo.hCursor, &iconInfo))
MessageBox(NULL, _T("CreateCursor Failed"),_T("message"),MB_OK|MB_SYSTEMMODAL);
bool isColorShape = (iconInfo.hbmColor != NULL);
bool isMaskShape = (iconInfo.hbmMask != NULL);
LONG cbSize = 0; int nWidth = 0; int nHeight = 0; int actualHeight = 0; int bmPlanes = 0;
int bmBitsPixel = 0; int xHotspot = 0; int yHotspot = 0; int widthBytes = 0;
// Return width,height,actualheight,bmplanes,bmbitspixel,hotsopt of cursor.
if(!CopyIconInfo( CursorInfo.hCursor,
nWidth,
nHeight,
actualHeight,
bmPlanes,
bmBitsPixel,
xHotspot,
yHotspot,
widthBytes ))
return 0;
std::vector<BYTE> bColor;
std::vector<BYTE> bMask;
int sz_hbmColor = 0;
int sz_hbmMask = 0;
_tempWidth = nWidth;
_tempHeight = nHeight;
//If HCURSOR have both color and mask go with regular approach.
if(isColorShape)
//Convert iconInfo.hbmColor HBITMAP to Byte array.
bColor = HBIMAPtoBYTE(iconInfo.hbmColor,sz_hbmColor);
//Convert iconInfo.hbmMask HBITMAP to Byte array.
bMask = HBIMAPtoBYTE(iconInfo.hbmMask,sz_hbmMask);
// If HCURSOR have only mask data go with new approach(split mask bitmap to color and mask).
else if(isMaskShape)
std::vector<BYTE> bSrcBitmap;
int sz_hbmBitmap = 0;
//Convert iconInfo.hbmMask HBITMAP to Byte array.
bSrcBitmap = HBIMAPtoBYTE(iconInfo.hbmMask,sz_hbmBitmap);
sz_hbmColor = sz_hbmBitmap/2;
sz_hbmMask = sz_hbmBitmap/2;
bMask.resize(bMask.size() + sz_hbmBitmap/2);
memcpy(&bMask[bSrcBitmap.size() - sz_hbmBitmap], &bSrcBitmap[0], sz_hbmBitmap/2 * sizeof(BYTE));
bColor.resize(bColor.size() + sz_hbmBitmap/2);
memcpy(&bColor[bSrcBitmap.size() - sz_hbmBitmap], &bSrcBitmap[sz_hbmBitmap/2], sz_hbmBitmap/2 * sizeof(BYTE));
//Clear at end.
bSrcBitmap.clear();
try
err = memcpy_s((m_hbmMaskBits), sz_hbmMask, &(bMask[0]), sz_hbmMask );
err = memcpy_s((m_hbmColorBits),sz_hbmColor,&(bColor[0]),sz_hbmColor);
//Clear at end.
bMask.clear();
bColor.clear();
return 1;
catch(...)
if(err)
MessageBox(NULL, _T("memcopy failed at mask or color copy"),_T("message"),MB_OK|MB_SYSTEMMODAL);
我尝试了以下方式,但它不支持少数单色光标。
PICTDESC pd = sizeof(pd), PICTYPE_ICON;
pd.icon.hicon = CursorInfo.hCursor;
CComPtr<IPicture> pPict = NULL;
CComPtr<IStream> pStrm = NULL;
BOOL res = FALSE;
res = SUCCEEDED( ::CreateStreamOnHGlobal(NULL, TRUE, &pStrm) );
res = SUCCEEDED( ::OleCreatePictureIndirect(&pd, IID_IPicture, TRUE, (void**)&pPict) );
res = SUCCEEDED( pPict->SaveAsFile( pStrm, TRUE, &cbSize ) );
if( res )
// rewind stream to the beginning
LARGE_INTEGER li = 0;
pStrm->Seek(li, STREAM_SEEK_SET, NULL);
// write to file
DWORD dwWritten = 0, dwRead = 0, dwDone = 0;
while( dwDone < cbSize )
if( SUCCEEDED(pStrm->Read(bCursorBuff, sizeof(bCursorBuff), &dwRead)) )
dwDone += dwRead;
_ASSERTE(dwDone == cbSize);
//End of Cursor image
pStrm.Release();
pPict.Release();
【问题讨论】:
【参考方案1】:HICON 和 HCURSOR 是系统句柄,因此它们只能在当前机器上工作。
通过网络只能发送实际数据(位图字节)。然后该机器可以为其创建自己的句柄。
使用 HBITMAP 字节是正确的方法。你可以在这里找到一些细节: How to convert HICON to HBITMAP in VC++?
您可以使用GetDIBits() 获取原始 HBITMAP 位。更多信息:C++/Win32: How to get the alpha channel from an HBITMAP?
【讨论】:
我尝试过这种方式,如果我将 HICON 复制到 HBITMAP,它会复制图标图像,其余部分将是黑色,我们无法在 HBITMAP 中制作蒙版。 这是因为您需要对图像进行操作以提取其数据。没有可用的掩码,您需要原始 RGB 字节(实际图像)。 是的,你是对的,但是在从位图中读取实际图像的 RGB 值时,它也会读取 RGB(0,0,0) -> 黑色,想象一下,如果我从位图中读取实际图像的 RGB 值并排除黑色这不是一般的权利。 使用我回答中的 GetDIBits() 函数。需要包含透明度信息,否则默认为黑色。 请阅读我回答中的所有链接。您当前的方法提供了无法使用的单独颜色和遮罩。【参考方案2】:以下代码仅适用于彩色光标,单色光标用于 将 16bpp 位图转换为 32bpp 位图并使用相同的代码。
bool saveToMemory(HICON hIcon, BYTE* buffer, DWORD& nSize)
if (hIcon == 0)
return FALSE;
int * pImageOffset;
int nNumIcons = 1;
nSize = 0;
// copy iconheader first of all
ICONHEADER iconheader;
// Setup the icon header
iconheader.idReserved = 0; // Must be 0
iconheader.idType = 1; // Type 1 = ICON (type 2 = CURSOR)
iconheader.idCount = nNumIcons; // number of ICONDIRs
// save to memory
memcpy(buffer, &iconheader, sizeof(iconheader));
nSize += sizeof(iconheader); // update
//
// Leave space for the IconDir entries
//
nSize += sizeof(ICONDIR);
pImageOffset = (int *)malloc(nNumIcons * sizeof(int));
ICONINFO iconInfo;
BITMAP bmpColor, bmpMask;
GetIconBitmapInfo(hIcon, &iconInfo, &bmpColor, &bmpMask);
// record the file-offset of the icon image for when we write the icon directories
pImageOffset[0] = nSize;
// bitmapinfoheader + colortable
//WriteIconImageHeader(hFile, &bmpColor, &bmpMask);
BITMAPINFOHEADER biHeader;
UINT nImageBytes;
// calculate how much space the COLOR and MASK bitmaps take
nImageBytes = NumBitmapBytes(&bmpColor) + NumBitmapBytes(&bmpMask);
// write the ICONIMAGE to disk (first the BITMAPINFOHEADER)
ZeroMemory(&biHeader, sizeof(biHeader));
// Fill in only those fields that are necessary
biHeader.biSize = sizeof(biHeader);
biHeader.biWidth = bmpColor.bmWidth;
biHeader.biHeight = bmpColor.bmHeight * 2; // height of color+mono
biHeader.biPlanes = bmpColor.bmPlanes;
biHeader.biBitCount = bmpColor.bmBitsPixel;
biHeader.biSizeImage = nImageBytes;
// write the BITMAPINFOHEADER
//WriteFile(hFile, &biHeader, sizeof(biHeader), &nWritten, 0);
memcpy(&buffer[nSize], &biHeader, sizeof(biHeader));
nSize += sizeof(biHeader);
// save color and mask bitmaps
saveIconData(buffer, nSize, iconInfo.hbmColor);
saveIconData(buffer, nSize, iconInfo.hbmMask);
DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);
//
// Lastly, save the icon directories.
//
DWORD size = saveIconDirectoryEntry(buffer, sizeof(ICONHEADER), pImageOffset[0], hIcon);
free(pImageOffset);
return TRUE;
//
// Return the number of BYTES the bitmap will take ON DISK
//
static UINT NumBitmapBytes(BITMAP *pBitmap)
int nWidthBytes = pBitmap->bmWidthBytes;
// bitmap scanlines MUST be a multiple of 4 bytes when stored
// inside a bitmap resource, so round up if necessary
if (nWidthBytes & 3)
nWidthBytes = (nWidthBytes + 4) & ~3;
return nWidthBytes * pBitmap->bmHeight;
// same as WriteIconData but save to memory
static UINT saveIconData(BYTE* buffer, DWORD& nSize, HBITMAP hBitmap)
BITMAP bmp;
int i;
BYTE * pIconData;
UINT nBitmapBytes;
DWORD nWritten = 0;
GetObject(hBitmap, sizeof(BITMAP), &bmp);
nBitmapBytes = NumBitmapBytes(&bmp);
pIconData = (BYTE *)malloc(nBitmapBytes);
GetBitmapBits(hBitmap, nBitmapBytes, pIconData);
// bitmaps are stored inverted (vertically) when on disk..
// so write out each line in turn, starting at the bottom + working
// towards the top of the bitmap. Also, the bitmaps are stored in packed
// in memory - scanlines are NOT 32bit aligned, just 1-after-the-other
for (i = bmp.bmHeight - 1; i >= 0; i--)
// Write the bitmap scanline
// save to memory
memcpy(&buffer[nSize], pIconData + (i * bmp.bmWidthBytes), bmp.bmWidthBytes);
nSize += bmp.bmWidthBytes;
nWritten += bmp.bmWidthBytes;
free(pIconData);
return nWritten;
//
// same as WriteIconDirectoryEntry but save to memory
//
static UINT saveIconDirectoryEntry(BYTE* buffer, DWORD pos, int imageOffset, HICON hIcon)
ICONINFO iconInfo;
ICONDIR iconDir;
BITMAP bmpColor;
BITMAP bmpMask;
DWORD nWritten = 0;
UINT nColorCount;
UINT nImageBytes;
GetIconBitmapInfo(hIcon, &iconInfo, &bmpColor, &bmpMask);
nImageBytes = NumBitmapBytes(&bmpColor) + NumBitmapBytes(&bmpMask);
if (bmpColor.bmBitsPixel >= 8)
nColorCount = 0;
else
nColorCount = 1 << (bmpColor.bmBitsPixel * bmpColor.bmPlanes);
// Create the ICONDIR structure
iconDir.bWidth = (BYTE)bmpColor.bmWidth;
iconDir.bHeight = (BYTE)bmpColor.bmHeight;
iconDir.bColorCount = nColorCount;
iconDir.bReserved = 0;
iconDir.wPlanes = bmpColor.bmPlanes;
iconDir.wBitCount = bmpColor.bmBitsPixel;
iconDir.dwBytesInRes = sizeof(BITMAPINFOHEADER) + nImageBytes;
iconDir.dwImageOffset = imageOffset;
// save to memory
memcpy(&buffer[pos], &iconDir, sizeof(iconDir));
nWritten += sizeof(iconDir);
// Free resources
DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);
return nWritten;
【讨论】:
【参考方案3】:我可以通过两次调用GetDIBits()
来做到这一点,一次是获取光标图像的实际细节,另一次是获取像素。
您可以将此代码应用于颜色和蒙版,但请注意,它仅返回 32x32px 光标,也仅返回第一帧,即使为其他内容配置了大小。
var windowDeviceContext = User32.GetWindowDC(IntPtr.Zero);
//Initialize the bitmap header and calculate its size.
var maskHeader = new BitmapInfoHeader();
maskHeader.Size = (uint) Marshal.SizeOf(maskHeader);
//Gets the image details.
Gdi32.GetDIBits(windowDeviceContext, iconInfo.Mask, 0, 0, null, ref maskHeader, DibColorModes.RgbColors);
//If there's any data, get it.
if (maskHeader.Height != 0)
//To prevent the cursor image from being inverted.
maskHeader.Height *= -1;
var maskBuffer = new byte[maskHeader.SizeImage];
Gdi32.GetDIBits(windowDeviceContext, iconInfo.Mask, 0, (uint) maskHeader.Height, maskBuffer, ref maskHeader, DibColorModes.RgbColors);
它是 C#,但很容易转换为您选择的语言。
【讨论】:
以上是关于将 HICON / HCURSOR 复制到字节数组中的主要内容,如果未能解决你的问题,请参考以下文章