[CLIPBRD] Improvements for the Clipboard Viewer.
[reactos.git] / base / applications / clipbrd / fileutils.c
1 /*
2 * PROJECT: ReactOS Clipboard Viewer
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Clipboard file format helper functions.
5 * COPYRIGHT: Copyright 2015-2018 Ricardo Hanke
6 * Copyright 2015-2018 Hermes Belusca-Maito
7 */
8
9 #include "precomp.h"
10
11 static HGLOBAL ClipboardReadMemoryBlock(HANDLE hFile, DWORD dwOffset, DWORD dwLength)
12 {
13 HGLOBAL hData;
14 LPVOID lpData;
15 DWORD dwBytesRead;
16
17 hData = GlobalAlloc(GHND, dwLength);
18 if (!hData)
19 return NULL;
20
21 lpData = GlobalLock(hData);
22 if (!lpData)
23 {
24 GlobalFree(hData);
25 return NULL;
26 }
27
28 if (SetFilePointer(hFile, dwOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
29 {
30 GlobalUnlock(hData);
31 GlobalFree(hData);
32 return NULL;
33 }
34
35 if (!ReadFile(hFile, lpData, dwLength, &dwBytesRead, NULL))
36 {
37 GlobalUnlock(hData);
38 GlobalFree(hData);
39 return NULL;
40 }
41
42 GlobalUnlock(hData);
43
44 return hData;
45 }
46
47 static BOOL ClipboardReadMemory(HANDLE hFile, DWORD dwFormat, DWORD dwOffset, DWORD dwLength, WORD FileIdentifier, PVOID lpFormatName)
48 {
49 HGLOBAL hData;
50 DWORD dwTemp = 0;
51
52 hData = ClipboardReadMemoryBlock(hFile, dwOffset, dwLength);
53 if (!hData)
54 return FALSE;
55
56 if ((dwFormat >= 0xC000) && (dwFormat <= 0xFFFF))
57 {
58 if (FileIdentifier == CLIP_FMT_31)
59 dwTemp = RegisterClipboardFormatA((LPCSTR)lpFormatName);
60 else if ((FileIdentifier == CLIP_FMT_NT) || (FileIdentifier == CLIP_FMT_BK))
61 dwTemp = RegisterClipboardFormatW((LPCWSTR)lpFormatName);
62
63 if (!dwTemp)
64 {
65 GlobalFree(hData);
66 return FALSE;
67 }
68 }
69 else
70 {
71 dwTemp = dwFormat;
72 }
73
74 if (!SetClipboardData(dwTemp, hData))
75 {
76 GlobalFree(hData);
77 return FALSE;
78 }
79
80 return TRUE;
81 }
82
83 static BOOL ClipboardWriteMemory(HANDLE hFile, DWORD dwFormat, DWORD dwOffset, PDWORD pdwLength)
84 {
85 HGLOBAL hData;
86 LPVOID lpData;
87 DWORD dwBytesWritten;
88
89 hData = GetClipboardData(dwFormat);
90 if (!hData)
91 return FALSE;
92
93 lpData = GlobalLock(hData);
94 if (!lpData)
95 return FALSE;
96
97 *pdwLength = GlobalSize(hData);
98
99 if (SetFilePointer(hFile, dwOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
100 {
101 GlobalUnlock(hData);
102 return FALSE;
103 }
104
105 if (!WriteFile(hFile, lpData, *pdwLength, &dwBytesWritten, NULL))
106 {
107 GlobalUnlock(hData);
108 return FALSE;
109 }
110
111 GlobalUnlock(hData);
112
113 return TRUE;
114 }
115
116 static BOOL ClipboardReadPalette(HANDLE hFile, DWORD dwOffset, DWORD dwLength)
117 {
118 LPLOGPALETTE lpPalette;
119 HPALETTE hPalette;
120 HGLOBAL hData;
121
122 hData = ClipboardReadMemoryBlock(hFile, dwOffset, dwLength);
123 if (!hData)
124 {
125 return FALSE;
126 }
127
128 lpPalette = GlobalLock(hData);
129 if (!lpPalette)
130 {
131 GlobalFree(hData);
132 return FALSE;
133 }
134
135 hPalette = CreatePalette(lpPalette);
136 if (!hPalette)
137 {
138 GlobalUnlock(hData);
139 GlobalFree(hData);
140 SetLastError(ERROR_OUTOFMEMORY);
141 return FALSE;
142 }
143
144 GlobalUnlock(hData);
145 GlobalFree(hData);
146
147 if (!SetClipboardData(CF_PALETTE, hPalette))
148 {
149 DeleteObject(hPalette);
150 return FALSE;
151 }
152
153 return TRUE;
154 }
155
156 static BOOL ClipboardReadMetafile(HANDLE hFile, DWORD dwOffset, DWORD dwLength)
157 {
158 HMETAFILE hMf;
159 HGLOBAL hData;
160 LPVOID lpData;
161
162 hData = ClipboardReadMemoryBlock(hFile, dwOffset, dwLength);
163 if (!hData)
164 {
165 return FALSE;
166 }
167
168 lpData = GlobalLock(hData);
169 if (!lpData)
170 {
171 GlobalFree(hData);
172 return FALSE;
173 }
174
175 hMf = SetMetaFileBitsEx(dwLength, lpData);
176
177 GlobalUnlock(hData);
178 GlobalFree(hData);
179
180 if (!hMf)
181 {
182 SetLastError(ERROR_OUTOFMEMORY);
183 return FALSE;
184 }
185
186 if (!SetClipboardData(CF_METAFILEPICT, hMf))
187 {
188 DeleteMetaFile(hMf);
189 return FALSE;
190 }
191
192 return TRUE;
193 }
194
195 static BOOL ClipboardReadEnhMetafile(HANDLE hFile, DWORD dwOffset, DWORD dwLength)
196 {
197 HENHMETAFILE hEmf;
198 HGLOBAL hData;
199 LPVOID lpData;
200
201 hData = ClipboardReadMemoryBlock(hFile, dwOffset, dwLength);
202 if (!hData)
203 {
204 return FALSE;
205 }
206
207 lpData = GlobalLock(hData);
208 if (!lpData)
209 {
210 GlobalFree(hData);
211 return FALSE;
212 }
213
214 hEmf = SetEnhMetaFileBits(dwLength, lpData);
215
216 GlobalUnlock(hData);
217 GlobalFree(hData);
218
219 if (!hEmf)
220 {
221 SetLastError(ERROR_OUTOFMEMORY);
222 return FALSE;
223 }
224
225 if (!SetClipboardData(CF_ENHMETAFILE, hEmf))
226 {
227 DeleteEnhMetaFile(hEmf);
228 return FALSE;
229 }
230
231 return TRUE;
232 }
233
234 static BOOL ClipboardReadBitmap(HANDLE hFile, DWORD dwOffset, DWORD dwLength)
235 {
236 HGLOBAL hData;
237 HBITMAP hBitmap;
238 LPBITMAP lpBitmap;
239
240 hData = ClipboardReadMemoryBlock(hFile, dwOffset, dwLength);
241 if (!hData)
242 {
243 return FALSE;
244 }
245
246 lpBitmap = GlobalLock(hData);
247 if (!lpBitmap)
248 {
249 GlobalFree(hData);
250 return FALSE;
251 }
252
253 lpBitmap->bmBits = lpBitmap + sizeof(BITMAP) + 1;
254
255 hBitmap = CreateBitmapIndirect(lpBitmap);
256
257 GlobalUnlock(hData);
258 GlobalFree(hData);
259
260 if (!hBitmap)
261 {
262 SetLastError(ERROR_OUTOFMEMORY);
263 return FALSE;
264 }
265
266 if (!SetClipboardData(CF_BITMAP, hBitmap))
267 {
268 DeleteObject(hBitmap);
269 return FALSE;
270 }
271
272 return TRUE;
273 }
274
275 void ReadClipboardFile(LPCWSTR lpFileName)
276 {
277 CLIPFILEHEADER ClipFileHeader;
278 CLIPFORMATHEADER ClipFormatArray;
279 NTCLIPFILEHEADER NtClipFileHeader;
280 NTCLIPFORMATHEADER NtClipFormatArray;
281 PVOID pClipFileHeader;
282 PVOID pClipFormatArray;
283 DWORD SizeOfFileHeader, SizeOfFormatHeader;
284
285 WORD wFileIdentifier;
286 WORD wFormatCount;
287 DWORD dwFormatID;
288 DWORD dwLenData;
289 DWORD dwOffData;
290 PVOID szName;
291
292 HANDLE hFile;
293 DWORD dwBytesRead;
294 BOOL bResult;
295 int i;
296
297 /* Open the file for read access */
298 hFile = CreateFileW(lpFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
299 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
300 if (hFile == INVALID_HANDLE_VALUE)
301 {
302 ShowLastWin32Error(Globals.hMainWnd);
303 goto done;
304 }
305
306 /* Just read enough bytes to get the clipboard file format ID */
307 if (!ReadFile(hFile, &wFileIdentifier, sizeof(wFileIdentifier), &dwBytesRead, NULL))
308 {
309 ShowLastWin32Error(Globals.hMainWnd);
310 goto done;
311 }
312
313 /* Set data according to the clipboard file format ID */
314 switch (wFileIdentifier)
315 {
316 case CLIP_FMT_31:
317 SizeOfFileHeader = sizeof(CLIPFILEHEADER);
318 SizeOfFormatHeader = sizeof(CLIPFORMATHEADER);
319 pClipFileHeader = &ClipFileHeader;
320 pClipFormatArray = &ClipFormatArray;
321 break;
322
323 case CLIP_FMT_NT:
324 case CLIP_FMT_BK:
325 SizeOfFileHeader = sizeof(NTCLIPFILEHEADER);
326 SizeOfFormatHeader = sizeof(NTCLIPFORMATHEADER);
327 pClipFileHeader = &NtClipFileHeader;
328 pClipFormatArray = &NtClipFormatArray;
329 break;
330
331 default:
332 MessageBoxRes(Globals.hMainWnd, Globals.hInstance, ERROR_INVALID_FILE_FORMAT, 0, MB_ICONSTOP | MB_OK);
333 goto done;
334 }
335
336 /* Completely read the header */
337 SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
338 if (!ReadFile(hFile, pClipFileHeader, SizeOfFileHeader, &dwBytesRead, NULL) ||
339 dwBytesRead != SizeOfFileHeader)
340 {
341 ShowLastWin32Error(Globals.hMainWnd);
342 goto done;
343 }
344
345 /* Get header data */
346 switch (wFileIdentifier)
347 {
348 case CLIP_FMT_31:
349 assert(wFileIdentifier == ((CLIPFILEHEADER*)pClipFileHeader)->wFileIdentifier);
350 wFormatCount = ((CLIPFILEHEADER*)pClipFileHeader)->wFormatCount;
351 break;
352
353 case CLIP_FMT_NT:
354 case CLIP_FMT_BK:
355 assert(wFileIdentifier == ((NTCLIPFILEHEADER*)pClipFileHeader)->wFileIdentifier);
356 wFormatCount = ((NTCLIPFILEHEADER*)pClipFileHeader)->wFormatCount;
357 break;
358 }
359
360 /* Loop through the format data array */
361 for (i = 0; i < wFormatCount; i++)
362 {
363 if (SetFilePointer(hFile, SizeOfFileHeader + i * SizeOfFormatHeader, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
364 {
365 ShowLastWin32Error(Globals.hMainWnd);
366 goto done;
367 }
368
369 if (!ReadFile(hFile, pClipFormatArray, SizeOfFormatHeader, &dwBytesRead, NULL))
370 {
371 ShowLastWin32Error(Globals.hMainWnd);
372 goto done;
373 }
374
375 /* Get format data */
376 switch (wFileIdentifier)
377 {
378 case CLIP_FMT_31:
379 dwFormatID = ((CLIPFORMATHEADER*)pClipFormatArray)->dwFormatID;
380 dwLenData = ((CLIPFORMATHEADER*)pClipFormatArray)->dwLenData;
381 dwOffData = ((CLIPFORMATHEADER*)pClipFormatArray)->dwOffData;
382 szName = ((CLIPFORMATHEADER*)pClipFormatArray)->szName;
383 break;
384
385 case CLIP_FMT_NT:
386 case CLIP_FMT_BK:
387 dwFormatID = ((NTCLIPFORMATHEADER*)pClipFormatArray)->dwFormatID;
388 dwLenData = ((NTCLIPFORMATHEADER*)pClipFormatArray)->dwLenData;
389 dwOffData = ((NTCLIPFORMATHEADER*)pClipFormatArray)->dwOffData;
390 szName = ((NTCLIPFORMATHEADER*)pClipFormatArray)->szName;
391 break;
392 }
393
394 switch (dwFormatID)
395 {
396 case CF_OWNERDISPLAY:
397 {
398 break;
399 }
400
401 case CF_DSPBITMAP:
402 case CF_BITMAP:
403 {
404 bResult = ClipboardReadBitmap(hFile, dwOffData, dwLenData);
405 break;
406 }
407
408 case CF_DSPMETAFILEPICT:
409 case CF_METAFILEPICT:
410 {
411 bResult = ClipboardReadMetafile(hFile, dwOffData, dwLenData);
412 break;
413 }
414
415 case CF_DSPENHMETAFILE:
416 case CF_ENHMETAFILE:
417 {
418 bResult = ClipboardReadEnhMetafile(hFile, dwOffData, dwLenData);
419 break;
420 }
421
422 case CF_PALETTE:
423 {
424 bResult = ClipboardReadPalette(hFile, dwOffData, dwLenData);
425 break;
426 }
427
428 default:
429 {
430 if ((dwFormatID < CF_PRIVATEFIRST) || (dwFormatID > CF_PRIVATELAST))
431 {
432 bResult = ClipboardReadMemory(hFile, dwFormatID, dwOffData, dwLenData, wFileIdentifier, szName);
433 }
434 break;
435 }
436 }
437
438 if (!bResult)
439 ShowLastWin32Error(Globals.hMainWnd);
440 }
441
442 done:
443 if (hFile != INVALID_HANDLE_VALUE)
444 CloseHandle(hFile);
445
446 return;
447 }
448
449 void WriteClipboardFile(LPCWSTR lpFileName, WORD wFileIdentifier)
450 {
451 CLIPFILEHEADER ClipFileHeader;
452 CLIPFORMATHEADER ClipFormatArray;
453 NTCLIPFILEHEADER NtClipFileHeader;
454 NTCLIPFORMATHEADER NtClipFormatArray;
455 PVOID pClipFileHeader;
456 PVOID pClipFormatArray;
457 DWORD SizeOfFileHeader, SizeOfFormatHeader;
458
459 WORD wFormatCount;
460 DWORD dwFormatID;
461 DWORD dwLenData;
462 DWORD dwOffData;
463 // PVOID szName;
464
465 HANDLE hFile;
466 DWORD dwBytesWritten;
467 int i;
468
469 /* Create the file for write access */
470 hFile = CreateFileW(lpFileName, GENERIC_WRITE, 0, NULL,
471 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
472 if (hFile == INVALID_HANDLE_VALUE)
473 {
474 ShowLastWin32Error(Globals.hMainWnd);
475 goto done;
476 }
477
478 wFormatCount = CountClipboardFormats();
479
480 /* Select the file format and setup the header according to the clipboard file format ID */
481 switch (wFileIdentifier)
482 {
483 case CLIP_FMT_31:
484 SizeOfFileHeader = sizeof(CLIPFILEHEADER);
485 SizeOfFormatHeader = sizeof(CLIPFORMATHEADER);
486 pClipFileHeader = &ClipFileHeader;
487 pClipFormatArray = &ClipFormatArray;
488
489 ClipFileHeader.wFileIdentifier = CLIP_FMT_31; // wFileIdentifier
490 ClipFileHeader.wFormatCount = wFormatCount;
491 break;
492
493 case CLIP_FMT_NT:
494 case CLIP_FMT_BK:
495 SizeOfFileHeader = sizeof(NTCLIPFILEHEADER);
496 SizeOfFormatHeader = sizeof(NTCLIPFORMATHEADER);
497 pClipFileHeader = &NtClipFileHeader;
498 pClipFormatArray = &NtClipFormatArray;
499
500 NtClipFileHeader.wFileIdentifier = CLIP_FMT_NT; // wFileIdentifier
501 NtClipFileHeader.wFormatCount = wFormatCount;
502 break;
503
504 default:
505 MessageBoxRes(Globals.hMainWnd, Globals.hInstance, ERROR_INVALID_FILE_FORMAT, 0, MB_ICONSTOP | MB_OK);
506 goto done;
507 }
508
509 /* Write the header */
510 SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
511 if (!WriteFile(hFile, pClipFileHeader, SizeOfFileHeader, &dwBytesWritten, NULL) ||
512 dwBytesWritten != SizeOfFileHeader)
513 {
514 ShowLastWin32Error(Globals.hMainWnd);
515 goto done;
516 }
517
518 /* Compute where the data should start (after the file header and the format array) */
519 dwOffData = SizeOfFileHeader + wFormatCount * SizeOfFormatHeader;
520
521 /* Loop through each format and save the data */
522 i = 0;
523 dwFormatID = EnumClipboardFormats(0);
524 while (dwFormatID)
525 {
526 if (i >= wFormatCount)
527 {
528 /* Must never happen! */
529 assert(FALSE);
530 break;
531 }
532
533 /* Write the clipboard data at the specified offset, and retrieve its length */
534 if (!ClipboardWriteMemory(hFile, dwFormatID, dwOffData, &dwLenData))
535 goto Cont;
536
537 /* Write the format data header */
538 switch (wFileIdentifier)
539 {
540 case CLIP_FMT_31:
541 ZeroMemory(pClipFormatArray, sizeof(CLIPFORMATHEADER));
542 ((CLIPFORMATHEADER*)pClipFormatArray)->dwFormatID = dwFormatID;
543 ((CLIPFORMATHEADER*)pClipFormatArray)->dwLenData = dwLenData;
544 ((CLIPFORMATHEADER*)pClipFormatArray)->dwOffData = dwOffData;
545 RetrieveClipboardFormatName(Globals.hInstance,
546 dwFormatID,
547 FALSE,
548 ((CLIPFORMATHEADER*)pClipFormatArray)->szName,
549 ARRAYSIZE(((CLIPFORMATHEADER*)pClipFormatArray)->szName));
550 break;
551
552 case CLIP_FMT_NT:
553 case CLIP_FMT_BK:
554 ZeroMemory(pClipFormatArray, sizeof(NTCLIPFORMATHEADER));
555 ((NTCLIPFORMATHEADER*)pClipFormatArray)->dwFormatID = dwFormatID;
556 ((NTCLIPFORMATHEADER*)pClipFormatArray)->dwLenData = dwLenData;
557 ((NTCLIPFORMATHEADER*)pClipFormatArray)->dwOffData = dwOffData;
558 RetrieveClipboardFormatName(Globals.hInstance,
559 dwFormatID,
560 TRUE,
561 ((NTCLIPFORMATHEADER*)pClipFormatArray)->szName,
562 ARRAYSIZE(((NTCLIPFORMATHEADER*)pClipFormatArray)->szName));
563 break;
564 }
565
566 if (SetFilePointer(hFile, SizeOfFileHeader + i * SizeOfFormatHeader, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
567 {
568 ShowLastWin32Error(Globals.hMainWnd);
569 goto done;
570 }
571
572 if (!WriteFile(hFile, pClipFormatArray, SizeOfFormatHeader, &dwBytesWritten, NULL))
573 {
574 ShowLastWin32Error(Globals.hMainWnd);
575 goto done;
576 }
577
578 /* Adjust the offset for the next data stream */
579 dwOffData += dwLenData;
580
581 Cont:
582 i++;
583 dwFormatID = EnumClipboardFormats(dwFormatID);
584 }
585
586 done:
587 if (hFile != INVALID_HANDLE_VALUE)
588 CloseHandle(hFile);
589
590 return;
591 }