reactos/base/applications/clipbrd/fileutils.c
Hermès Bélusca-Maïto ebe3d5273e
[CLIPBRD] Improvements for the Clipboard Viewer.
- 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.
2018-05-13 22:25:55 +02:00

591 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;
}