/* * 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; }