mirror of
https://github.com/reactos/reactos.git
synced 2024-08-26 13:18:51 +00:00
![Hermès Bélusca-Maïto](/assets/img/avatar_default.png)
- Improve the scrolling support for bitmaps, DIBs and text formats. This completes the work started in CORE-10679 by Ricardo Hanke. Includes scrolling with the keyboard and the mouse wheel. - Add support for the CF_DSP* clipboard formats, as well as CF_TEXT and CF_OEMTEXT. - Add support for owner-display clipboard format CF_OWNERDISPLAY. - Realize any palette found in the clipboard (CF_PALETTE) before displaying the clipboard data format we want. - Remove dead code. - Update the file headers.
592 lines
16 KiB
C
592 lines
16 KiB
C
/*
|
|
* PROJECT: ReactOS Clipboard Viewer
|
|
* LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
|
|
* PURPOSE: Clipboard file format helper functions.
|
|
* COPYRIGHT: Copyright 2015-2018 Ricardo Hanke
|
|
* Copyright 2015-2018 Hermes Belusca-Maito
|
|
*/
|
|
|
|
#include "precomp.h"
|
|
|
|
static HGLOBAL ClipboardReadMemoryBlock(HANDLE hFile, DWORD dwOffset, DWORD dwLength)
|
|
{
|
|
HGLOBAL hData;
|
|
LPVOID lpData;
|
|
DWORD dwBytesRead;
|
|
|
|
hData = GlobalAlloc(GHND, dwLength);
|
|
if (!hData)
|
|
return NULL;
|
|
|
|
lpData = GlobalLock(hData);
|
|
if (!lpData)
|
|
{
|
|
GlobalFree(hData);
|
|
return NULL;
|
|
}
|
|
|
|
if (SetFilePointer(hFile, dwOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
|
|
{
|
|
GlobalUnlock(hData);
|
|
GlobalFree(hData);
|
|
return NULL;
|
|
}
|
|
|
|
if (!ReadFile(hFile, lpData, dwLength, &dwBytesRead, NULL))
|
|
{
|
|
GlobalUnlock(hData);
|
|
GlobalFree(hData);
|
|
return NULL;
|
|
}
|
|
|
|
GlobalUnlock(hData);
|
|
|
|
return hData;
|
|
}
|
|
|
|
static BOOL ClipboardReadMemory(HANDLE hFile, DWORD dwFormat, DWORD dwOffset, DWORD dwLength, WORD FileIdentifier, PVOID lpFormatName)
|
|
{
|
|
HGLOBAL hData;
|
|
DWORD dwTemp = 0;
|
|
|
|
hData = ClipboardReadMemoryBlock(hFile, dwOffset, dwLength);
|
|
if (!hData)
|
|
return FALSE;
|
|
|
|
if ((dwFormat >= 0xC000) && (dwFormat <= 0xFFFF))
|
|
{
|
|
if (FileIdentifier == CLIP_FMT_31)
|
|
dwTemp = RegisterClipboardFormatA((LPCSTR)lpFormatName);
|
|
else if ((FileIdentifier == CLIP_FMT_NT) || (FileIdentifier == CLIP_FMT_BK))
|
|
dwTemp = RegisterClipboardFormatW((LPCWSTR)lpFormatName);
|
|
|
|
if (!dwTemp)
|
|
{
|
|
GlobalFree(hData);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwTemp = dwFormat;
|
|
}
|
|
|
|
if (!SetClipboardData(dwTemp, hData))
|
|
{
|
|
GlobalFree(hData);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL ClipboardWriteMemory(HANDLE hFile, DWORD dwFormat, DWORD dwOffset, PDWORD pdwLength)
|
|
{
|
|
HGLOBAL hData;
|
|
LPVOID lpData;
|
|
DWORD dwBytesWritten;
|
|
|
|
hData = GetClipboardData(dwFormat);
|
|
if (!hData)
|
|
return FALSE;
|
|
|
|
lpData = GlobalLock(hData);
|
|
if (!lpData)
|
|
return FALSE;
|
|
|
|
*pdwLength = GlobalSize(hData);
|
|
|
|
if (SetFilePointer(hFile, dwOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
|
|
{
|
|
GlobalUnlock(hData);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!WriteFile(hFile, lpData, *pdwLength, &dwBytesWritten, NULL))
|
|
{
|
|
GlobalUnlock(hData);
|
|
return FALSE;
|
|
}
|
|
|
|
GlobalUnlock(hData);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL ClipboardReadPalette(HANDLE hFile, DWORD dwOffset, DWORD dwLength)
|
|
{
|
|
LPLOGPALETTE lpPalette;
|
|
HPALETTE hPalette;
|
|
HGLOBAL hData;
|
|
|
|
hData = ClipboardReadMemoryBlock(hFile, dwOffset, dwLength);
|
|
if (!hData)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
lpPalette = GlobalLock(hData);
|
|
if (!lpPalette)
|
|
{
|
|
GlobalFree(hData);
|
|
return FALSE;
|
|
}
|
|
|
|
hPalette = CreatePalette(lpPalette);
|
|
if (!hPalette)
|
|
{
|
|
GlobalUnlock(hData);
|
|
GlobalFree(hData);
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
GlobalUnlock(hData);
|
|
GlobalFree(hData);
|
|
|
|
if (!SetClipboardData(CF_PALETTE, hPalette))
|
|
{
|
|
DeleteObject(hPalette);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL ClipboardReadMetafile(HANDLE hFile, DWORD dwOffset, DWORD dwLength)
|
|
{
|
|
HMETAFILE hMf;
|
|
HGLOBAL hData;
|
|
LPVOID lpData;
|
|
|
|
hData = ClipboardReadMemoryBlock(hFile, dwOffset, dwLength);
|
|
if (!hData)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
lpData = GlobalLock(hData);
|
|
if (!lpData)
|
|
{
|
|
GlobalFree(hData);
|
|
return FALSE;
|
|
}
|
|
|
|
hMf = SetMetaFileBitsEx(dwLength, lpData);
|
|
|
|
GlobalUnlock(hData);
|
|
GlobalFree(hData);
|
|
|
|
if (!hMf)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!SetClipboardData(CF_METAFILEPICT, hMf))
|
|
{
|
|
DeleteMetaFile(hMf);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL ClipboardReadEnhMetafile(HANDLE hFile, DWORD dwOffset, DWORD dwLength)
|
|
{
|
|
HENHMETAFILE hEmf;
|
|
HGLOBAL hData;
|
|
LPVOID lpData;
|
|
|
|
hData = ClipboardReadMemoryBlock(hFile, dwOffset, dwLength);
|
|
if (!hData)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
lpData = GlobalLock(hData);
|
|
if (!lpData)
|
|
{
|
|
GlobalFree(hData);
|
|
return FALSE;
|
|
}
|
|
|
|
hEmf = SetEnhMetaFileBits(dwLength, lpData);
|
|
|
|
GlobalUnlock(hData);
|
|
GlobalFree(hData);
|
|
|
|
if (!hEmf)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!SetClipboardData(CF_ENHMETAFILE, hEmf))
|
|
{
|
|
DeleteEnhMetaFile(hEmf);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL ClipboardReadBitmap(HANDLE hFile, DWORD dwOffset, DWORD dwLength)
|
|
{
|
|
HGLOBAL hData;
|
|
HBITMAP hBitmap;
|
|
LPBITMAP lpBitmap;
|
|
|
|
hData = ClipboardReadMemoryBlock(hFile, dwOffset, dwLength);
|
|
if (!hData)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
lpBitmap = GlobalLock(hData);
|
|
if (!lpBitmap)
|
|
{
|
|
GlobalFree(hData);
|
|
return FALSE;
|
|
}
|
|
|
|
lpBitmap->bmBits = lpBitmap + sizeof(BITMAP) + 1;
|
|
|
|
hBitmap = CreateBitmapIndirect(lpBitmap);
|
|
|
|
GlobalUnlock(hData);
|
|
GlobalFree(hData);
|
|
|
|
if (!hBitmap)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!SetClipboardData(CF_BITMAP, hBitmap))
|
|
{
|
|
DeleteObject(hBitmap);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void ReadClipboardFile(LPCWSTR lpFileName)
|
|
{
|
|
CLIPFILEHEADER ClipFileHeader;
|
|
CLIPFORMATHEADER ClipFormatArray;
|
|
NTCLIPFILEHEADER NtClipFileHeader;
|
|
NTCLIPFORMATHEADER NtClipFormatArray;
|
|
PVOID pClipFileHeader;
|
|
PVOID pClipFormatArray;
|
|
DWORD SizeOfFileHeader, SizeOfFormatHeader;
|
|
|
|
WORD wFileIdentifier;
|
|
WORD wFormatCount;
|
|
DWORD dwFormatID;
|
|
DWORD dwLenData;
|
|
DWORD dwOffData;
|
|
PVOID szName;
|
|
|
|
HANDLE hFile;
|
|
DWORD dwBytesRead;
|
|
BOOL bResult;
|
|
int i;
|
|
|
|
/* Open the file for read access */
|
|
hFile = CreateFileW(lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
ShowLastWin32Error(Globals.hMainWnd);
|
|
goto done;
|
|
}
|
|
|
|
/* Just read enough bytes to get the clipboard file format ID */
|
|
if (!ReadFile(hFile, &wFileIdentifier, sizeof(wFileIdentifier), &dwBytesRead, NULL))
|
|
{
|
|
ShowLastWin32Error(Globals.hMainWnd);
|
|
goto done;
|
|
}
|
|
|
|
/* Set data according to the clipboard file format ID */
|
|
switch (wFileIdentifier)
|
|
{
|
|
case CLIP_FMT_31:
|
|
SizeOfFileHeader = sizeof(CLIPFILEHEADER);
|
|
SizeOfFormatHeader = sizeof(CLIPFORMATHEADER);
|
|
pClipFileHeader = &ClipFileHeader;
|
|
pClipFormatArray = &ClipFormatArray;
|
|
break;
|
|
|
|
case CLIP_FMT_NT:
|
|
case CLIP_FMT_BK:
|
|
SizeOfFileHeader = sizeof(NTCLIPFILEHEADER);
|
|
SizeOfFormatHeader = sizeof(NTCLIPFORMATHEADER);
|
|
pClipFileHeader = &NtClipFileHeader;
|
|
pClipFormatArray = &NtClipFormatArray;
|
|
break;
|
|
|
|
default:
|
|
MessageBoxRes(Globals.hMainWnd, Globals.hInstance, ERROR_INVALID_FILE_FORMAT, 0, MB_ICONSTOP | MB_OK);
|
|
goto done;
|
|
}
|
|
|
|
/* Completely read the header */
|
|
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
|
|
if (!ReadFile(hFile, pClipFileHeader, SizeOfFileHeader, &dwBytesRead, NULL) ||
|
|
dwBytesRead != SizeOfFileHeader)
|
|
{
|
|
ShowLastWin32Error(Globals.hMainWnd);
|
|
goto done;
|
|
}
|
|
|
|
/* Get header data */
|
|
switch (wFileIdentifier)
|
|
{
|
|
case CLIP_FMT_31:
|
|
assert(wFileIdentifier == ((CLIPFILEHEADER*)pClipFileHeader)->wFileIdentifier);
|
|
wFormatCount = ((CLIPFILEHEADER*)pClipFileHeader)->wFormatCount;
|
|
break;
|
|
|
|
case CLIP_FMT_NT:
|
|
case CLIP_FMT_BK:
|
|
assert(wFileIdentifier == ((NTCLIPFILEHEADER*)pClipFileHeader)->wFileIdentifier);
|
|
wFormatCount = ((NTCLIPFILEHEADER*)pClipFileHeader)->wFormatCount;
|
|
break;
|
|
}
|
|
|
|
/* Loop through the format data array */
|
|
for (i = 0; i < wFormatCount; i++)
|
|
{
|
|
if (SetFilePointer(hFile, SizeOfFileHeader + i * SizeOfFormatHeader, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
|
|
{
|
|
ShowLastWin32Error(Globals.hMainWnd);
|
|
goto done;
|
|
}
|
|
|
|
if (!ReadFile(hFile, pClipFormatArray, SizeOfFormatHeader, &dwBytesRead, NULL))
|
|
{
|
|
ShowLastWin32Error(Globals.hMainWnd);
|
|
goto done;
|
|
}
|
|
|
|
/* Get format data */
|
|
switch (wFileIdentifier)
|
|
{
|
|
case CLIP_FMT_31:
|
|
dwFormatID = ((CLIPFORMATHEADER*)pClipFormatArray)->dwFormatID;
|
|
dwLenData = ((CLIPFORMATHEADER*)pClipFormatArray)->dwLenData;
|
|
dwOffData = ((CLIPFORMATHEADER*)pClipFormatArray)->dwOffData;
|
|
szName = ((CLIPFORMATHEADER*)pClipFormatArray)->szName;
|
|
break;
|
|
|
|
case CLIP_FMT_NT:
|
|
case CLIP_FMT_BK:
|
|
dwFormatID = ((NTCLIPFORMATHEADER*)pClipFormatArray)->dwFormatID;
|
|
dwLenData = ((NTCLIPFORMATHEADER*)pClipFormatArray)->dwLenData;
|
|
dwOffData = ((NTCLIPFORMATHEADER*)pClipFormatArray)->dwOffData;
|
|
szName = ((NTCLIPFORMATHEADER*)pClipFormatArray)->szName;
|
|
break;
|
|
}
|
|
|
|
switch (dwFormatID)
|
|
{
|
|
case CF_OWNERDISPLAY:
|
|
{
|
|
break;
|
|
}
|
|
|
|
case CF_DSPBITMAP:
|
|
case CF_BITMAP:
|
|
{
|
|
bResult = ClipboardReadBitmap(hFile, dwOffData, dwLenData);
|
|
break;
|
|
}
|
|
|
|
case CF_DSPMETAFILEPICT:
|
|
case CF_METAFILEPICT:
|
|
{
|
|
bResult = ClipboardReadMetafile(hFile, dwOffData, dwLenData);
|
|
break;
|
|
}
|
|
|
|
case CF_DSPENHMETAFILE:
|
|
case CF_ENHMETAFILE:
|
|
{
|
|
bResult = ClipboardReadEnhMetafile(hFile, dwOffData, dwLenData);
|
|
break;
|
|
}
|
|
|
|
case CF_PALETTE:
|
|
{
|
|
bResult = ClipboardReadPalette(hFile, dwOffData, dwLenData);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
if ((dwFormatID < CF_PRIVATEFIRST) || (dwFormatID > CF_PRIVATELAST))
|
|
{
|
|
bResult = ClipboardReadMemory(hFile, dwFormatID, dwOffData, dwLenData, wFileIdentifier, szName);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bResult)
|
|
ShowLastWin32Error(Globals.hMainWnd);
|
|
}
|
|
|
|
done:
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hFile);
|
|
|
|
return;
|
|
}
|
|
|
|
void WriteClipboardFile(LPCWSTR lpFileName, WORD wFileIdentifier)
|
|
{
|
|
CLIPFILEHEADER ClipFileHeader;
|
|
CLIPFORMATHEADER ClipFormatArray;
|
|
NTCLIPFILEHEADER NtClipFileHeader;
|
|
NTCLIPFORMATHEADER NtClipFormatArray;
|
|
PVOID pClipFileHeader;
|
|
PVOID pClipFormatArray;
|
|
DWORD SizeOfFileHeader, SizeOfFormatHeader;
|
|
|
|
WORD wFormatCount;
|
|
DWORD dwFormatID;
|
|
DWORD dwLenData;
|
|
DWORD dwOffData;
|
|
// PVOID szName;
|
|
|
|
HANDLE hFile;
|
|
DWORD dwBytesWritten;
|
|
int i;
|
|
|
|
/* Create the file for write access */
|
|
hFile = CreateFileW(lpFileName, GENERIC_WRITE, 0, NULL,
|
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
ShowLastWin32Error(Globals.hMainWnd);
|
|
goto done;
|
|
}
|
|
|
|
wFormatCount = CountClipboardFormats();
|
|
|
|
/* Select the file format and setup the header according to the clipboard file format ID */
|
|
switch (wFileIdentifier)
|
|
{
|
|
case CLIP_FMT_31:
|
|
SizeOfFileHeader = sizeof(CLIPFILEHEADER);
|
|
SizeOfFormatHeader = sizeof(CLIPFORMATHEADER);
|
|
pClipFileHeader = &ClipFileHeader;
|
|
pClipFormatArray = &ClipFormatArray;
|
|
|
|
ClipFileHeader.wFileIdentifier = CLIP_FMT_31; // wFileIdentifier
|
|
ClipFileHeader.wFormatCount = wFormatCount;
|
|
break;
|
|
|
|
case CLIP_FMT_NT:
|
|
case CLIP_FMT_BK:
|
|
SizeOfFileHeader = sizeof(NTCLIPFILEHEADER);
|
|
SizeOfFormatHeader = sizeof(NTCLIPFORMATHEADER);
|
|
pClipFileHeader = &NtClipFileHeader;
|
|
pClipFormatArray = &NtClipFormatArray;
|
|
|
|
NtClipFileHeader.wFileIdentifier = CLIP_FMT_NT; // wFileIdentifier
|
|
NtClipFileHeader.wFormatCount = wFormatCount;
|
|
break;
|
|
|
|
default:
|
|
MessageBoxRes(Globals.hMainWnd, Globals.hInstance, ERROR_INVALID_FILE_FORMAT, 0, MB_ICONSTOP | MB_OK);
|
|
goto done;
|
|
}
|
|
|
|
/* Write the header */
|
|
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
|
|
if (!WriteFile(hFile, pClipFileHeader, SizeOfFileHeader, &dwBytesWritten, NULL) ||
|
|
dwBytesWritten != SizeOfFileHeader)
|
|
{
|
|
ShowLastWin32Error(Globals.hMainWnd);
|
|
goto done;
|
|
}
|
|
|
|
/* Compute where the data should start (after the file header and the format array) */
|
|
dwOffData = SizeOfFileHeader + wFormatCount * SizeOfFormatHeader;
|
|
|
|
/* Loop through each format and save the data */
|
|
i = 0;
|
|
dwFormatID = EnumClipboardFormats(0);
|
|
while (dwFormatID)
|
|
{
|
|
if (i >= wFormatCount)
|
|
{
|
|
/* Must never happen! */
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
|
|
/* Write the clipboard data at the specified offset, and retrieve its length */
|
|
if (!ClipboardWriteMemory(hFile, dwFormatID, dwOffData, &dwLenData))
|
|
goto Cont;
|
|
|
|
/* Write the format data header */
|
|
switch (wFileIdentifier)
|
|
{
|
|
case CLIP_FMT_31:
|
|
ZeroMemory(pClipFormatArray, sizeof(CLIPFORMATHEADER));
|
|
((CLIPFORMATHEADER*)pClipFormatArray)->dwFormatID = dwFormatID;
|
|
((CLIPFORMATHEADER*)pClipFormatArray)->dwLenData = dwLenData;
|
|
((CLIPFORMATHEADER*)pClipFormatArray)->dwOffData = dwOffData;
|
|
RetrieveClipboardFormatName(Globals.hInstance,
|
|
dwFormatID,
|
|
FALSE,
|
|
((CLIPFORMATHEADER*)pClipFormatArray)->szName,
|
|
ARRAYSIZE(((CLIPFORMATHEADER*)pClipFormatArray)->szName));
|
|
break;
|
|
|
|
case CLIP_FMT_NT:
|
|
case CLIP_FMT_BK:
|
|
ZeroMemory(pClipFormatArray, sizeof(NTCLIPFORMATHEADER));
|
|
((NTCLIPFORMATHEADER*)pClipFormatArray)->dwFormatID = dwFormatID;
|
|
((NTCLIPFORMATHEADER*)pClipFormatArray)->dwLenData = dwLenData;
|
|
((NTCLIPFORMATHEADER*)pClipFormatArray)->dwOffData = dwOffData;
|
|
RetrieveClipboardFormatName(Globals.hInstance,
|
|
dwFormatID,
|
|
TRUE,
|
|
((NTCLIPFORMATHEADER*)pClipFormatArray)->szName,
|
|
ARRAYSIZE(((NTCLIPFORMATHEADER*)pClipFormatArray)->szName));
|
|
break;
|
|
}
|
|
|
|
if (SetFilePointer(hFile, SizeOfFileHeader + i * SizeOfFormatHeader, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
|
|
{
|
|
ShowLastWin32Error(Globals.hMainWnd);
|
|
goto done;
|
|
}
|
|
|
|
if (!WriteFile(hFile, pClipFormatArray, SizeOfFormatHeader, &dwBytesWritten, NULL))
|
|
{
|
|
ShowLastWin32Error(Globals.hMainWnd);
|
|
goto done;
|
|
}
|
|
|
|
/* Adjust the offset for the next data stream */
|
|
dwOffData += dwLenData;
|
|
|
|
Cont:
|
|
i++;
|
|
dwFormatID = EnumClipboardFormats(dwFormatID);
|
|
}
|
|
|
|
done:
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hFile);
|
|
|
|
return;
|
|
}
|