|
|
1.1 root 1:
2: /******************************************************************************\
3: * This is a part of the Microsoft Source Code Samples.
4: * Copyright (C) 1993 Microsoft Corporation.
5: * All rights reserved.
6: * This source code is only intended as a supplement to
7: * Microsoft Development Tools and/or WinHelp documentation.
8: * See these sources for detailed information regarding the
9: * Microsoft samples programs.
10: \******************************************************************************/
11:
12: /****************************** Module Header *******************************
13: * Module Name: rwres.c
14: *
15: * Does all the reading and writing of the .RES (resource) file.
16: *
17: * Functions:
18: *
19: * OpenResFile()
20: * WriteRes()
21: * LoadResFile()
22: * IsValidResFile()
23: * SafeParseResHeader()
24: * SafeNameOrdLen()
25: * SafeDWordAlign()
26: * WriteDlgIncludeRes()
27: *
28: * Comments:
29: *
30: ****************************************************************************/
31:
32: #include "dlgedit.h"
33: #include "dlgfuncs.h"
34: #include "dlgextrn.h"
35:
36:
37: /*
38: * The bytes in the special RT_RESOURCE32 type resource that is the
39: * first resource in every Win32 format res file. The first 8 bytes
40: * in this resource's header were specially designed to be invalid
41: * for a 16 bit format resource file, so that tools can determine
42: * immediately if they are reading a 16 bit or a Win32 format res
43: * file.
44: */
45: static BYTE abResource32[] = {
46: 0x00, 0x00, 0x00, 0x00, // DataSize (0 bytes).
47: 0x20, 0x00, 0x00, 0x00, // HeaderSize (32 bytes).
48: 0xff, 0xff, 0x00, 0x00, // Type (RT_RESOURCE32).
49: 0xff, 0xff, 0x00, 0x00, // Name (ordinal 0).
50: 0x00, 0x00, 0x00, 0x00, // DataVersion
51: 0x00, 0x00, // MemoryFlags
52: 0x00, 0x00, // LanguageId
53: 0x00, 0x00, 0x00, 0x00, // Version
54: 0x00, 0x00, 0x00, 0x00 // Characteristics
55: };
56:
57:
58: STATICFN BOOL LoadResFile(HANDLE hfRes, LPTSTR pszFullResFile,
59: LPTSTR pszIncludeBuf);
60: STATICFN BOOL IsValidResFile(PRES pRes, INT cbFileSize);
61: STATICFN PRES SafeParseResHeader(PRES pRes, INT cbMaxSize);
62: STATICFN INT SafeNameOrdLen(LPTSTR psz, INT cbMaxLen);
63: STATICFN VOID SafeDWordAlign(PBYTE *ppb, PINT pcbMax);
64: STATICFN BOOL WriteDlgIncludeRes(HANDLE hfWrite, LPTSTR pszFullResFile);
65:
66:
67:
68: /************************************************************************
69: * OpenResFile
70: *
71: * High level function to load the data in a resource file. The
72: * function LoadResFile is called to do the actual work, after
73: * this code does some housekeeping.
74: *
75: * Arguments:
76: * LPTSTR pszFullPath - The full path to the resource file.
77: *
78: * Returns:
79: * TRUE if resource file was opened; otherwise, FALSE.
80: *
81: ************************************************************************/
82:
83: BOOL OpenResFile(
84: LPTSTR pszFullPath)
85: {
86: HCURSOR hcurSave;
87: PRESLINK prl;
88: PRESLINK prlSave;
89: BOOL fSuccess = FALSE;
90: INT cDlg;
91: HANDLE hfRes;
92: TCHAR szInclude[CCHMAXPATH];
93: TCHAR szFullInclude[CCHMAXPATH];
94: BOOL fIncOpened = FALSE;
95:
96: hcurSave = SetCursor(hcurWait);
97:
98: /*
99: * Close any existing resource and include file and free memory.
100: * It is assumed that if either had been changed, the user was asked
101: * if they wanted to save them, because it is too late now.
102: */
103: FreeRes();
104: FreeInclude();
105:
106: if ((hfRes = CreateFile(pszFullPath, GENERIC_READ,
107: FILE_SHARE_READ, NULL, OPEN_EXISTING,
108: FILE_FLAG_SEQUENTIAL_SCAN, NULL)) != (HANDLE)-1 &&
109: LoadResFile(hfRes, pszFullPath, szInclude)) {
110: lstrcpy(szFullResFile, pszFullPath);
111: pszResFile = FileInPath(szFullResFile);
112:
113: ShowFileStatus(TRUE);
114:
115: /*
116: * If there was a DLGINCLUDE resource found, try and open the
117: * specified include file, after making sure that it is a
118: * fully formed path.
119: */
120: if (*szInclude) {
121: /*
122: * Does the include filespec from the res file appear to
123: * be a simple filename without a path? If so, look for
124: * it in the same directory that the res file is in.
125: * Otherwise, assume it has a fully qualified path.
126: */
127: if (!HasPath(szInclude)) {
128: lstrcpy(szFullInclude, szFullResFile);
129: lstrcpy(FileInPath(szFullInclude), szInclude);
130: }
131: else {
132: lstrcpy(szFullInclude, szInclude);
133: }
134:
135: fIncOpened = OpenIncludeFile(szFullInclude);
136: }
137:
138: /*
139: * If there wasn't an include resource found, or there was
140: * but it couldn't be opened, we want to ask the user for the
141: * include file to use for this resource file.
142: */
143: if (!fIncOpened)
144: Open(FILE_INCLUDE);
145:
146: /*
147: * Start counting the dialogs in the resource, but stop at two.
148: */
149: cDlg = 0;
150: for (cDlg = 0, prl = gprlHead; prl; prl = prl->prlNext) {
151: if (prl->fDlgResource) {
152: if (++cDlg > 1)
153: break;
154:
155: prlSave = prl;
156: }
157: }
158:
159: /*
160: * If there are multiple dialogs, display the "Select Dialog"
161: * dialog to ask the user which one they want to edit. If
162: * there is exactly one dialog, just go ahead and show it
163: * initially.
164: */
165: if (cDlg == 1)
166: ResLinkToDialog(prlSave);
167: else if (cDlg > 1)
168: SelectDialogDialog();
169:
170: fSuccess = TRUE;
171: }
172:
173: if (hfRes != (HANDLE)-1)
174: CloseHandle(hfRes);
175:
176: ShowFileStatus(TRUE);
177: SetCursor(hcurSave);
178:
179: return fSuccess;
180: }
181:
182:
183:
184: /************************************************************************
185: * LoadResFile
186: *
187: * Loads the resource file specified by the passed in file handle.
188: * This function first verifies that it is a valid resource file.
189: *
190: * Arguments:
191: * HANDLE hfRes - File handle to read from.
192: * LPTSTR pszFullResFile - Full name of resource file being loaded.
193: * LPTSTR pszIncludeBuf - Where to return the include file name, if
194: * a DLGINCLUDE resource is found in the res
195: * file. If not, this buffer gets a null byte
196: * as its first character.
197: *
198: * Returns:
199: * TRUE if load was successful; otherwise, FALSE is returned.
200: *
201: ************************************************************************/
202:
203: STATICFN BOOL LoadResFile(
204: HANDLE hfRes,
205: LPTSTR pszFullResFile,
206: LPTSTR pszIncludeBuf)
207: {
208: HANDLE hAllRes;
209: PRES pRes;
210: PRES pResAll;
211: PRESLINK prl;
212: PRESLINK prlT;
213: INT cbRead;
214: LPTSTR pszResType;
215: DWORD cbFileSize;
216:
217: cbFileSize = GetFileSize((HANDLE)hfRes, NULL);
218:
219: if (!(hAllRes = GlobalAlloc(GMEM_MOVEABLE, cbFileSize))) {
220: Message(MSG_OUTOFMEMORY);
221: return FALSE;
222: }
223:
224: *pszIncludeBuf = CHAR_NULL;
225: pRes = pResAll = (PRES)GlobalLock(hAllRes);
226: if ((cbRead = _lread((HFILE)hfRes, (LPSTR)pResAll, cbFileSize)) != -1 &&
227: cbRead == (INT)cbFileSize) {
228: if (!IsValidResFile(pResAll, cbFileSize)) {
229: Message(MSG_BADRESFILE, pszFullResFile);
230: }
231: else do {
232: pszResType = ResourceType(pRes);
233:
234: if (IsOrd(pszResType) && OrdID(pszResType) == ORDID_RT_DLGINCLUDE) {
235: /*
236: * Pass back the include file name. This resource
237: * will not be saved in the res list because it is
238: * going to be explicitly written out later if
239: * necessary.
240: */
241: NameOrdCpy(pszIncludeBuf, (LPTSTR)SkipResHeader(pRes));
242: }
243: else if (IsOrd(pszResType) &&
244: OrdID(pszResType) == ORDID_RT_RESOURCE32) {
245: /*
246: * This is the dummy resource that identifies a
247: * 32 bit resource file. This resource should be
248: * skipped also.
249: */
250: }
251: else {
252: /*
253: * This is some other kind of a resource.
254: * Save it in the resource list.
255: */
256: if (!(prlT = AllocResLink(pRes))) {
257: FreeResList();
258: break;
259: }
260:
261: if (!gprlHead) {
262: gprlHead = prl = prlT;
263: }
264: else {
265: prl->prlNext = prlT;
266: prl = prlT;
267: }
268: }
269:
270: /*
271: * Move to the next resource.
272: */
273: pRes = (PRES)((PBYTE)pRes + pRes->HeaderSize + pRes->DataSize);
274: DWordAlign((PBYTE *)&pRes);
275: } while (pRes < (PRES)((PBYTE)pResAll + cbFileSize));
276: }
277:
278: GlobalUnlock(hAllRes);
279: GlobalFree(hAllRes);
280:
281: return (gprlHead ? TRUE : FALSE);
282: }
283:
284:
285:
286: /************************************************************************
287: * IsValidResFile
288: *
289: * This function does some basic checks on the resource file in memory
290: * pointed to by pbRes. It does this by walking through the resource
291: * checking for the resource header info and lengths.
292: *
293: * Arguments:
294: * PRES pRes - Pointer to the first resource in the file.
295: * INT cbFileSize - Size of the file in memory.
296: *
297: * Returns:
298: * TRUE if it is a valid resource file, FALSE if not.
299: *
300: ************************************************************************/
301:
302: STATICFN BOOL IsValidResFile(
303: PRES pRes,
304: INT cbFileSize)
305: {
306: INT cbCurLoc = 0;
307: PRES pResT;
308:
309: /*
310: * The file is zero size.
311: */
312: if (!cbFileSize)
313: return FALSE;
314:
315: pResT = pRes;
316: while (cbCurLoc < cbFileSize) {
317: /*
318: * Check this resource for validity.
319: */
320: if (!(pResT = SafeParseResHeader(pResT, cbFileSize - cbCurLoc)))
321: return FALSE;
322:
323: /*
324: * Point just past the resource that was just checked.
325: */
326: cbCurLoc = (PBYTE)pResT - (PBYTE)pRes;
327: }
328:
329: return (cbCurLoc == cbFileSize) ? TRUE : FALSE;
330: }
331:
332:
333:
334: /************************************************************************
335: * SafeParseResHeader
336: *
337: * This function parses the specified resource header and returns a
338: * pointer to the next resource header in the resource file. It does
339: * it in a safe manner, not touching memory beyond the maximum size
340: * specified. If the resource header is somehow messed up and
341: * specifies a size that is larger than will fit in the given maximum
342: * size, NULL is returned.
343: *
344: * Arguments:
345: * PRES pRes - Pointer to the resource.
346: * INT cbMaxSize - Maximum size the resource can be.
347: *
348: * Returns:
349: * A pointer to just past this resource, or NULL if the resource
350: * is larger than cbMaxSize.
351: *
352: ************************************************************************/
353:
354: STATICFN PRES SafeParseResHeader(
355: PRES pRes,
356: INT cbMaxSize)
357: {
358: INT cbLen;
359: DWORD cbDataSize;
360: PBYTE pb;
361:
362: pb = (PBYTE)pRes;
363:
364: /*
365: * There must be room for the first part of the resource header.
366: */
367: if (sizeof(RES) > cbMaxSize)
368: return FALSE;
369:
370: pb += sizeof(RES);
371: cbMaxSize -= sizeof(RES);
372:
373: /*
374: * Parse the type field then skip over it.
375: */
376: cbLen = SafeNameOrdLen((LPTSTR)pb, cbMaxSize);
377: if (cbLen > cbMaxSize)
378: return NULL;
379:
380: pb += cbLen;
381: cbMaxSize -= cbLen;
382:
383: /*
384: * Parse the name field then skip over it.
385: */
386: cbLen = SafeNameOrdLen((LPTSTR)pb, cbMaxSize);
387: if (cbLen > cbMaxSize)
388: return NULL;
389:
390: pb += cbLen;
391: cbMaxSize -= cbLen;
392:
393: SafeDWordAlign(&pb, &cbMaxSize);
394:
395: /*
396: * There must be room for the second part of the resource header.
397: */
398: if (sizeof(RES2) > cbMaxSize)
399: return FALSE;
400:
401: pb += sizeof(RES2);
402: cbMaxSize -= sizeof(RES2);
403:
404: /*
405: * The header size field must be valid.
406: */
407: if (pRes->HeaderSize != (DWORD)(pb - (PBYTE)pRes))
408: return FALSE;
409:
410: /*
411: * Calculate the size of the data, taking into account any
412: * padding that may be at the end to make it DWORD aligned.
413: */
414: cbDataSize = pRes->DataSize;
415: DWordAlign((PBYTE *)&cbDataSize);
416:
417: /*
418: * There must be room enough left for the data.
419: */
420: if (cbDataSize > (DWORD)cbMaxSize)
421: return FALSE;
422:
423: return (PRES)(pb + cbDataSize);
424: }
425:
426:
427:
428: /************************************************************************
429: * SafeNameOrdLen
430: *
431: * This function returns the size of the specified name/ordinal. It
432: * does it in a safe manner, not touching memory beyond the specified
433: * maximum size. If it is a string and the terminating null is not
434: * found within cbMaxLen bytes, then cbMaxLen plus one is returned.
435: *
436: * Arguments:
437: * LPTSTR psz - Pointer to the name/ordinal.
438: * INT cbMaxLen - Maximum length to probe.
439: *
440: * Returns:
441: * The length of the ordinal if it is an ordinal, or the length
442: * of the string (plus the null terminator) if it is a string.
443: *
444: ************************************************************************/
445:
446: STATICFN INT SafeNameOrdLen(
447: LPTSTR psz,
448: INT cbMaxLen)
449: {
450: INT cbLen = 0;
451:
452: if (cbMaxLen == 0)
453: return 1;
454:
455: if (IsOrd(psz))
456: return sizeof(ORDINAL);
457:
458: for (cbLen = 0; cbLen < cbMaxLen && *psz; psz++, cbLen += sizeof(TCHAR))
459: ;
460:
461: /*
462: * Account for the null terminator.
463: */
464: cbLen += sizeof(TCHAR);
465:
466: return cbLen;
467: }
468:
469:
470:
471: /************************************************************************
472: * SafeDWordAlign
473: *
474: * This function aligns the passed pointer to a DWORD boundary. At the
475: * same time, it subtracts from the specified counter the amount that
476: * it had to add to the pointer, if any.
477: *
478: * Arguments:
479: * PBYTE *ppb - Points to the pointer to align.
480: * PINT pcbMax - Points to the current count to decrement.
481: *
482: ************************************************************************/
483:
484: STATICFN VOID SafeDWordAlign(
485: PBYTE *ppb,
486: PINT pcbMax)
487: {
488: INT cbAlign;
489:
490: cbAlign = (4 - (((WORD)(DWORD)*ppb) & 3)) % 4;
491: *ppb += cbAlign;
492: *pcbMax -= cbAlign;
493: }
494:
495:
496:
497: /************************************************************************
498: * WriteRes
499: *
500: * Worker routine that does the actual writing out of the resource data.
501: *
502: * Arguments:
503: * HANDLE hfWrite - Resource file to write to.
504: * LPTSTR pszFullResFile - Full pathname to the resource file that
505: * is being written.
506: *
507: * Returns:
508: * TRUE if successful; otherwise, FALSE.
509: *
510: ************************************************************************/
511:
512: BOOL WriteRes(
513: HANDLE hfWrite,
514: LPTSTR pszFullResFile)
515: {
516: PRESLINK prl;
517: PRES pRes;
518:
519: /*
520: * Write the special RT_RESOURCE32 dummy resource to the beginning
521: * of the resource file. This resource is aligned, so no padding
522: * needs to be done before writing the resource that follows it.
523: */
524: if (_lwrite((HFILE)hfWrite, abResource32, sizeof(abResource32)) == -1)
525: return FALSE;
526:
527: /*
528: * Write out any DLGINCLUDE resource there may be.
529: */
530: if (!WriteDlgIncludeRes(hfWrite, pszFullResFile))
531: return FALSE;
532:
533: /*
534: * Loop through all the resources.
535: */
536: for (prl = gprlHead; prl; prl = prl->prlNext) {
537: if (!(pRes = (PRES)GlobalLock(prl->hRes)))
538: return FALSE;
539:
540: /*
541: * Write the actual data.
542: */
543: if (_lwrite((HFILE)hfWrite, (LPSTR)pRes, prl->cbRes) == -1)
544: return FALSE;
545:
546: /*
547: * Write pads out to the next DWORD boundary.
548: */
549: if (!WriteDWordPad(hfWrite, prl->cbRes))
550: return FALSE;
551:
552: GlobalUnlock(prl->hRes);
553: }
554:
555: return TRUE;
556: }
557:
558:
559:
560: /************************************************************************
561: * WriteDlgIncludeRes
562: *
563: * Writes out a DLGINCLUDE resource to the specified resource file for
564: * the currently open include file.
565: *
566: * Arguments:
567: * HANDLE hfWrite - Resource file handle to write to.
568: * LPTSTR pszFullResFile - Full pathname to the resource file that
569: * is being written.
570: *
571: * Returns:
572: * Number of characters written if the include resource was
573: * written successfully (or there wasn't one to write) or -1
574: * if an error occurred.
575: *
576: ************************************************************************/
577:
578: STATICFN BOOL WriteDlgIncludeRes(
579: HANDLE hfWrite,
580: LPTSTR pszFullResFile)
581: {
582: INT cbResSize;
583: INT cbDataSize;
584: PRES pResBegin;
585: PBYTE pb;
586: INT cbWritten;
587: LPTSTR pszInc;
588: ORDINAL ordDlgIncName;
589: BOOL fSuccess = FALSE;
590:
591: /*
592: * No include file. Do nothing (return success).
593: */
594: if (!pszIncludeFile)
595: return TRUE;
596:
597: /*
598: * If the include file is in a different directory than the resource
599: * file, write the full path to it. Otherwise, we just write the
600: * include file name.
601: */
602: if (DifferentDirs(pszFullResFile, szFullIncludeFile))
603: pszInc = szFullIncludeFile;
604: else
605: pszInc = pszIncludeFile;
606:
607: /*
608: * The DLGINCLUDE resource name always is the same (a value of 1).
609: */
610: WriteOrd(&ordDlgIncName, ORDID_DLGINCLUDE_NAME);
611:
612: /*
613: * Determine the size of the resource data.
614: */
615: cbDataSize = NameOrdLen(pszInc);
616:
617: /*
618: * Determine the resource size. Note that there is no need for
619: * DWORD padding after the res header, because the header will
620: * be aligned (there are no strings in it).
621: */
622: cbResSize = sizeof(RES) + // First part of res header.
623: sizeof(ORDINAL) + // Type ordinal.
624: sizeof(ORDINAL) + // Name ordinal.
625: sizeof(RES2) + // Second half of header.
626: cbDataSize; // Size of data.
627:
628: if (!(pResBegin = (PRES)MyAlloc(cbResSize)))
629: return FALSE;
630:
631: /*
632: * Write the resource header.
633: */
634: pb = WriteResHeader(pResBegin, cbDataSize, ORDID_RT_DLGINCLUDE,
635: (LPTSTR)&ordDlgIncName, MMF_MOVEABLE | MMF_PURE | MMF_DISCARDABLE,
636: 0, 0, 0, 0);
637:
638: /*
639: * Write the resource data. This is simply the name
640: * of the include file.
641: */
642: NameOrdCpy((LPTSTR)pb, pszInc);
643:
644: /*
645: * Write the resource to the file.
646: */
647: cbWritten = _lwrite((HFILE)hfWrite, (LPSTR)pResBegin, cbResSize);
648:
649: if (cbWritten == cbResSize) {
650: /*
651: * Write pads out to the next DWORD boundary.
652: */
653: if (WriteDWordPad(hfWrite, cbWritten))
654: fSuccess = TRUE;
655: }
656:
657: MyFree(pResBegin);
658:
659: return fSuccess;
660: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.