comparison foosdk/wtl/Include/atlmisc.h @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
comparison
equal deleted inserted replaced
0:e9bb126753e7 1:20d02a178406
1 // Windows Template Library - WTL version 10.0
2 // Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.
3 //
4 // This file is a part of the Windows Template Library.
5 // The use and distribution terms for this software are covered by the
6 // Microsoft Public License (http://opensource.org/licenses/MS-PL)
7 // which can be found in the file MS-PL.txt at the root folder.
8
9 #ifndef __ATLMISC_H__
10 #define __ATLMISC_H__
11
12 #pragma once
13
14 #ifndef __ATLAPP_H__
15 #error atlmisc.h requires atlapp.h to be included first
16 #endif
17
18 #ifndef _WTL_NO_COMPATIBILITY_INCLUDES
19 #include <atlstr.h>
20 #include <atltypes.h>
21 #endif // _WTL_NO_COMPATIBILITY_INCLUDES
22
23
24 ///////////////////////////////////////////////////////////////////////////////
25 // Classes in this file:
26 //
27 // CRecentDocumentListBase<T, t_cchItemLen, t_nFirstID, t_nLastID>
28 // CRecentDocumentList
29 // CFindFile
30 // CRegProperty
31 // CRegPropertyImpl<T>
32 //
33 // Global functions:
34 // AtlGetStockPen()
35 // AtlGetStockBrush()
36 // AtlGetStockFont()
37 // AtlGetStockPalette()
38 //
39 // AtlCompactPath()
40
41
42 namespace WTL
43 {
44
45 ///////////////////////////////////////////////////////////////////////////////
46 // CSize scalar operators
47
48 #if !defined(_WTL_NO_SIZE_SCALAR) && defined(__ATLTYPES_H__)
49
50 template <class Num>
51 inline CSize operator *(SIZE s, Num n)
52 {
53 return CSize((int)(s.cx * n), (int)(s.cy * n));
54 };
55
56 template <class Num>
57 inline void operator *=(SIZE & s, Num n)
58 {
59 s = s * n;
60 };
61
62 template <class Num>
63 inline CSize operator /(SIZE s, Num n)
64 {
65 return CSize((int)(s.cx / n), (int)(s.cy / n));
66 };
67
68 template <class Num>
69 inline void operator /=(SIZE & s, Num n)
70 {
71 s = s / n;
72 };
73
74 #endif // !defined(_WTL_NO_SIZE_SCALAR) && defined(__ATLTYPES_H__)
75
76
77 ///////////////////////////////////////////////////////////////////////////////
78 // CRecentDocumentList - MRU List Support
79
80 #ifndef _WTL_MRUEMPTY_TEXT
81 #define _WTL_MRUEMPTY_TEXT _T("(empty)")
82 #endif
83
84 // forward declaration
85 inline bool AtlCompactPath(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen);
86
87 template <class T, int t_cchItemLen = MAX_PATH, int t_nFirstID = ID_FILE_MRU_FIRST, int t_nLastID = ID_FILE_MRU_LAST>
88 class CRecentDocumentListBase
89 {
90 public:
91 // Declarations
92 struct _DocEntry
93 {
94 TCHAR szDocName[t_cchItemLen];
95 bool operator ==(const _DocEntry& de) const
96 { return (lstrcmpi(szDocName, de.szDocName) == 0); }
97 };
98
99 enum
100 {
101 m_nMaxEntries_Min = 2,
102 m_nMaxEntries_Max = t_nLastID - t_nFirstID + 1,
103 m_cchMaxItemLen_Min = 6,
104 m_cchMaxItemLen_Max = t_cchItemLen,
105 m_cchItemNameLen = 11
106 };
107
108 // Data members
109 ATL::CSimpleArray<_DocEntry> m_arrDocs;
110 int m_nMaxEntries; // default is 4
111 HMENU m_hMenu;
112
113 TCHAR m_szNoEntries[t_cchItemLen];
114
115 int m_cchMaxItemLen;
116
117 // Constructor
118 CRecentDocumentListBase() : m_nMaxEntries(4), m_hMenu(NULL), m_cchMaxItemLen(-1)
119 {
120 // These ASSERTs verify values of the template arguments
121 ATLASSERT(t_cchItemLen > m_cchMaxItemLen_Min);
122 ATLASSERT(m_nMaxEntries_Max > m_nMaxEntries_Min);
123 }
124
125 // Attributes
126 HMENU GetMenuHandle() const
127 {
128 return m_hMenu;
129 }
130
131 void SetMenuHandle(HMENU hMenu)
132 {
133 ATLASSERT((hMenu == NULL) || ::IsMenu(hMenu));
134 m_hMenu = hMenu;
135 if((m_hMenu == NULL) || (::GetMenuString(m_hMenu, t_nFirstID, m_szNoEntries, t_cchItemLen, MF_BYCOMMAND) == 0))
136 {
137 T* pT = static_cast<T*>(this);
138 (void)pT; // avoid level 4 warning
139 ATL::Checked::tcsncpy_s(m_szNoEntries, _countof(m_szNoEntries), pT->GetMRUEmptyText(), _TRUNCATE);
140 }
141 }
142
143 int GetMaxEntries() const
144 {
145 return m_nMaxEntries;
146 }
147
148 void SetMaxEntries(int nMaxEntries)
149 {
150 ATLASSERT((nMaxEntries >= m_nMaxEntries_Min) && (nMaxEntries <= m_nMaxEntries_Max));
151 if(nMaxEntries < m_nMaxEntries_Min)
152 nMaxEntries = m_nMaxEntries_Min;
153 else if(nMaxEntries > m_nMaxEntries_Max)
154 nMaxEntries = m_nMaxEntries_Max;
155 m_nMaxEntries = nMaxEntries;
156 }
157
158 int GetMaxItemLength() const
159 {
160 return m_cchMaxItemLen;
161 }
162
163 void SetMaxItemLength(int cchMaxLen)
164 {
165 ATLASSERT(((cchMaxLen >= m_cchMaxItemLen_Min) && (cchMaxLen <= m_cchMaxItemLen_Max)) || (cchMaxLen == -1));
166 if(cchMaxLen != -1)
167 {
168 if(cchMaxLen < m_cchMaxItemLen_Min)
169 cchMaxLen = m_cchMaxItemLen_Min;
170 else if(cchMaxLen > m_cchMaxItemLen_Max)
171 cchMaxLen = m_cchMaxItemLen_Max;
172 }
173 m_cchMaxItemLen = cchMaxLen;
174 T* pT = static_cast<T*>(this);
175 pT->UpdateMenu();
176 }
177
178 // Operations
179 BOOL AddToList(LPCTSTR lpstrDocName)
180 {
181 _DocEntry de;
182 errno_t nRet = ATL::Checked::tcsncpy_s(de.szDocName, _countof(de.szDocName), lpstrDocName, _TRUNCATE);
183 if((nRet != 0) && (nRet != STRUNCATE))
184 return FALSE;
185
186 for(int i = 0; i < m_arrDocs.GetSize(); i++)
187 {
188 if(lstrcmpi(m_arrDocs[i].szDocName, lpstrDocName) == 0)
189 {
190 m_arrDocs.RemoveAt(i);
191 break;
192 }
193 }
194
195 if(m_arrDocs.GetSize() == m_nMaxEntries)
196 m_arrDocs.RemoveAt(0);
197
198 BOOL bRet = m_arrDocs.Add(de);
199 if(bRet)
200 {
201 T* pT = static_cast<T*>(this);
202 bRet = pT->UpdateMenu();
203 }
204 return bRet;
205 }
206
207 // This function is deprecated because it is not safe.
208 // Use the version below that accepts the buffer length.
209 __declspec(deprecated)
210 BOOL GetFromList(int /*nItemID*/, LPTSTR /*lpstrDocName*/)
211 {
212 ATLASSERT(FALSE);
213 return FALSE;
214 }
215
216 BOOL GetFromList(int nItemID, LPTSTR lpstrDocName, int cchLength)
217 {
218 int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
219 if((nIndex < 0) || (nIndex >= m_arrDocs.GetSize()))
220 return FALSE;
221 if(lstrlen(m_arrDocs[nIndex].szDocName) >= cchLength)
222 return FALSE;
223 ATL::Checked::tcscpy_s(lpstrDocName, cchLength, m_arrDocs[nIndex].szDocName);
224
225 return TRUE;
226 }
227
228 #ifdef __ATLSTR_H__
229 BOOL GetFromList(int nItemID, ATL::CString& strDocName)
230 {
231 int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
232 if((nIndex < 0) || (nIndex >= m_arrDocs.GetSize()))
233 return FALSE;
234 strDocName = m_arrDocs[nIndex].szDocName;
235 return TRUE;
236 }
237 #endif // __ATLSTR_H__
238
239 BOOL RemoveFromList(int nItemID)
240 {
241 int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
242 BOOL bRet = m_arrDocs.RemoveAt(nIndex);
243 if(bRet)
244 {
245 T* pT = static_cast<T*>(this);
246 bRet = pT->UpdateMenu();
247 }
248 return bRet;
249 }
250
251 BOOL MoveToTop(int nItemID)
252 {
253 int nIndex = m_arrDocs.GetSize() - (nItemID - t_nFirstID) - 1;
254 if((nIndex < 0) || (nIndex >= m_arrDocs.GetSize()))
255 return FALSE;
256 _DocEntry de;
257 de = m_arrDocs[nIndex];
258 m_arrDocs.RemoveAt(nIndex);
259 BOOL bRet = m_arrDocs.Add(de);
260 if(bRet)
261 {
262 T* pT = static_cast<T*>(this);
263 bRet = pT->UpdateMenu();
264 }
265 return bRet;
266 }
267
268 BOOL ReadFromRegistry(LPCTSTR lpstrRegKey)
269 {
270 T* pT = static_cast<T*>(this);
271 ATL::CRegKey rkParent;
272 ATL::CRegKey rk;
273
274 LONG lRet = rkParent.Open(HKEY_CURRENT_USER, lpstrRegKey);
275 if(lRet != ERROR_SUCCESS)
276 return FALSE;
277 lRet = rk.Open(rkParent, pT->GetRegKeyName());
278 if(lRet != ERROR_SUCCESS)
279 return FALSE;
280
281 DWORD dwRet = 0;
282 lRet = rk.QueryDWORDValue(pT->GetRegCountName(), dwRet);
283 if(lRet != ERROR_SUCCESS)
284 return FALSE;
285 SetMaxEntries(dwRet);
286
287 m_arrDocs.RemoveAll();
288
289 TCHAR szRetString[t_cchItemLen] = {};
290 _DocEntry de;
291
292 for(int nItem = m_nMaxEntries; nItem > 0; nItem--)
293 {
294 TCHAR szBuff[m_cchItemNameLen] = {};
295 _stprintf_s(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem);
296 ULONG ulCount = t_cchItemLen;
297 lRet = rk.QueryStringValue(szBuff, szRetString, &ulCount);
298 if(lRet == ERROR_SUCCESS)
299 {
300 ATL::Checked::tcscpy_s(de.szDocName, _countof(de.szDocName), szRetString);
301 m_arrDocs.Add(de);
302 }
303 }
304
305 rk.Close();
306 rkParent.Close();
307
308 return pT->UpdateMenu();
309 }
310
311 BOOL WriteToRegistry(LPCTSTR lpstrRegKey)
312 {
313 T* pT = static_cast<T*>(this);
314 (void)pT; // avoid level 4 warning
315 ATL::CRegKey rkParent;
316 ATL::CRegKey rk;
317
318 LONG lRet = rkParent.Create(HKEY_CURRENT_USER, lpstrRegKey);
319 if(lRet != ERROR_SUCCESS)
320 return FALSE;
321 lRet = rk.Create(rkParent, pT->GetRegKeyName());
322 if(lRet != ERROR_SUCCESS)
323 return FALSE;
324
325 lRet = rk.SetDWORDValue(pT->GetRegCountName(), m_nMaxEntries);
326 ATLASSERT(lRet == ERROR_SUCCESS);
327
328 // set new values
329 int nItem;
330 for(nItem = m_arrDocs.GetSize(); nItem > 0; nItem--)
331 {
332 TCHAR szBuff[m_cchItemNameLen] = {};
333 _stprintf_s(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem);
334 TCHAR szDocName[t_cchItemLen] = {};
335 GetFromList(t_nFirstID + nItem - 1, szDocName, t_cchItemLen);
336 lRet = rk.SetStringValue(szBuff, szDocName);
337 ATLASSERT(lRet == ERROR_SUCCESS);
338 }
339
340 // delete unused keys
341 for(nItem = m_arrDocs.GetSize() + 1; nItem <= m_nMaxEntries_Max; nItem++)
342 {
343 TCHAR szBuff[m_cchItemNameLen] = {};
344 _stprintf_s(szBuff, m_cchItemNameLen, pT->GetRegItemName(), nItem);
345 rk.DeleteValue(szBuff);
346 }
347
348 rk.Close();
349 rkParent.Close();
350
351 return TRUE;
352 }
353
354 // Implementation
355 BOOL UpdateMenu()
356 {
357 if(m_hMenu == NULL)
358 return FALSE;
359 ATLASSERT(::IsMenu(m_hMenu));
360
361 int nItems = ::GetMenuItemCount(m_hMenu);
362 int nInsertPoint = 0;
363 for(int i = 0; i < nItems; i++)
364 {
365 CMenuItemInfo mi;
366 mi.fMask = MIIM_ID;
367 ::GetMenuItemInfo(m_hMenu, i, TRUE, &mi);
368 if (mi.wID == t_nFirstID)
369 {
370 nInsertPoint = i;
371 break;
372 }
373 }
374
375 ATLASSERT((nInsertPoint < nItems) && "You need a menu item with an ID = t_nFirstID");
376
377 for(int j = t_nFirstID; j < (t_nFirstID + m_nMaxEntries); j++)
378 {
379 // keep the first one as an insertion point
380 if (j != t_nFirstID)
381 ::DeleteMenu(m_hMenu, j, MF_BYCOMMAND);
382 }
383
384 TCHAR szItemText[t_cchItemLen + 6] = {}; // add space for &, 2 digits, and a space
385 int nSize = m_arrDocs.GetSize();
386 int nItem = 0;
387 if(nSize > 0)
388 {
389 for(nItem = 0; nItem < nSize; nItem++)
390 {
391 if(m_cchMaxItemLen == -1)
392 {
393 _stprintf_s(szItemText, t_cchItemLen + 6, _T("&%i %s"), nItem + 1, m_arrDocs[nSize - 1 - nItem].szDocName);
394 }
395 else
396 {
397 TCHAR szBuff[t_cchItemLen] = {};
398 T* pT = static_cast<T*>(this);
399 (void)pT; // avoid level 4 warning
400 bool bRet = pT->CompactDocumentName(szBuff, m_arrDocs[nSize - 1 - nItem].szDocName, m_cchMaxItemLen);
401 (void)bRet; // avoid level 4 warning
402 ATLASSERT(bRet);
403 _stprintf_s(szItemText, t_cchItemLen + 6, _T("&%i %s"), nItem + 1, szBuff);
404 }
405
406 ::InsertMenu(m_hMenu, nInsertPoint + nItem, MF_BYPOSITION | MF_STRING, t_nFirstID + nItem, szItemText);
407 }
408 }
409 else // empty
410 {
411 ::InsertMenu(m_hMenu, nInsertPoint, MF_BYPOSITION | MF_STRING, t_nFirstID, m_szNoEntries);
412 ::EnableMenuItem(m_hMenu, t_nFirstID, MF_GRAYED);
413 nItem++;
414 }
415 ::DeleteMenu(m_hMenu, nInsertPoint + nItem, MF_BYPOSITION);
416
417 return TRUE;
418 }
419
420 // Overrideables
421 // override to provide a different method of compacting document names
422 static bool CompactDocumentName(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen)
423 {
424 return AtlCompactPath(lpstrOut, lpstrIn, cchLen);
425 }
426
427 static LPCTSTR GetRegKeyName()
428 {
429 return _T("Recent Document List");
430 }
431
432 static LPCTSTR GetRegCountName()
433 {
434 return _T("DocumentCount");
435 }
436
437 static LPCTSTR GetRegItemName()
438 {
439 // Note: This string is a format string used with wsprintf().
440 // Resulting formatted string must be m_cchItemNameLen or less
441 // characters long, including the terminating null character.
442 return _T("Document%i");
443 }
444
445 static LPCTSTR GetMRUEmptyText()
446 {
447 return _WTL_MRUEMPTY_TEXT;
448 }
449 };
450
451 class CRecentDocumentList : public CRecentDocumentListBase<CRecentDocumentList>
452 {
453 public:
454 // nothing here
455 };
456
457
458 ///////////////////////////////////////////////////////////////////////////////
459 // CFindFile - file search helper class
460
461 class CFindFile
462 {
463 public:
464 // Data members
465 HANDLE m_hFind;
466 WIN32_FIND_DATA m_fd;
467 LPTSTR m_lpszRoot;
468 const TCHAR m_chDirSeparator;
469 BOOL m_bFound;
470
471 // Constructor/destructor
472 CFindFile() : m_hFind(NULL), m_lpszRoot(NULL), m_chDirSeparator(_T('\\')), m_bFound(FALSE)
473 { }
474
475 ~CFindFile()
476 {
477 Close();
478 }
479
480 // Attributes
481 ULONGLONG GetFileSize() const
482 {
483 ATLASSERT(m_hFind != NULL);
484
485 ULARGE_INTEGER nFileSize = {};
486 if(m_bFound)
487 {
488 nFileSize.LowPart = m_fd.nFileSizeLow;
489 nFileSize.HighPart = m_fd.nFileSizeHigh;
490 }
491 else
492 {
493 nFileSize.QuadPart = 0;
494 }
495
496 return nFileSize.QuadPart;
497 }
498
499 BOOL GetFileName(LPTSTR lpstrFileName, int cchLength) const
500 {
501 ATLASSERT(m_hFind != NULL);
502 if(lstrlen(m_fd.cFileName) >= cchLength)
503 return FALSE;
504
505 if(m_bFound)
506 ATL::Checked::tcscpy_s(lpstrFileName, cchLength, m_fd.cFileName);
507
508 return m_bFound;
509 }
510
511 BOOL GetFilePath(LPTSTR lpstrFilePath, int cchLength) const
512 {
513 ATLASSERT(m_hFind != NULL);
514
515 int nLen = lstrlen(m_lpszRoot);
516 ATLASSERT(nLen > 0);
517 if(nLen == 0)
518 return FALSE;
519
520 bool bAddSep = (m_lpszRoot[nLen - 1] != m_chDirSeparator);
521
522 if((lstrlen(m_lpszRoot) + (bAddSep ? 1 : 0)) >= cchLength)
523 return FALSE;
524
525 ATL::Checked::tcscpy_s(lpstrFilePath, cchLength, m_lpszRoot);
526
527 if(bAddSep)
528 {
529 TCHAR szSeparator[2] = { m_chDirSeparator, 0 };
530 ATL::Checked::tcscat_s(lpstrFilePath, cchLength, szSeparator);
531 }
532
533 ATL::Checked::tcscat_s(lpstrFilePath, cchLength, m_fd.cFileName);
534
535 return TRUE;
536 }
537
538 BOOL GetFileTitle(LPTSTR lpstrFileTitle, int cchLength) const
539 {
540 ATLASSERT(m_hFind != NULL);
541
542 TCHAR szBuff[MAX_PATH] = {};
543 if(!GetFileName(szBuff, MAX_PATH))
544 return FALSE;
545
546 if((lstrlen(szBuff) >= cchLength) || (cchLength < 1))
547 return FALSE;
548
549 // find the last dot
550 LPTSTR pstrDot = _tcsrchr(szBuff, _T('.'));
551 if(pstrDot != NULL)
552 *pstrDot = 0;
553
554 ATL::Checked::tcscpy_s(lpstrFileTitle, cchLength, szBuff);
555
556 return TRUE;
557 }
558
559 BOOL GetFileURL(LPTSTR lpstrFileURL, int cchLength) const
560 {
561 ATLASSERT(m_hFind != NULL);
562
563 LPCTSTR lpstrFileURLPrefix = _T("file://");
564 const int cchPrefix = lstrlen(lpstrFileURLPrefix);
565 if(cchPrefix >= cchLength)
566 return FALSE;
567
568 ATL::Checked::tcscpy_s(lpstrFileURL, cchLength, lpstrFileURLPrefix);
569
570 return GetFilePath(&lpstrFileURL[cchPrefix], cchLength - cchPrefix);
571 }
572
573 BOOL GetRoot(LPTSTR lpstrRoot, int cchLength) const
574 {
575 ATLASSERT(m_hFind != NULL);
576 if(lstrlen(m_lpszRoot) >= cchLength)
577 return FALSE;
578
579 ATL::Checked::tcscpy_s(lpstrRoot, cchLength, m_lpszRoot);
580
581 return TRUE;
582 }
583
584 #ifdef __ATLSTR_H__
585 ATL::CString GetFileName() const
586 {
587 ATLASSERT(m_hFind != NULL);
588
589 ATL::CString ret;
590
591 if(m_bFound)
592 ret = m_fd.cFileName;
593 return ret;
594 }
595
596 ATL::CString GetFilePath() const
597 {
598 ATLASSERT(m_hFind != NULL);
599
600 ATL::CString strResult = m_lpszRoot;
601 int nLen = strResult.GetLength();
602 ATLASSERT(nLen > 0);
603 if(nLen == 0)
604 return strResult;
605
606 if(strResult[nLen - 1] != m_chDirSeparator)
607 strResult += m_chDirSeparator;
608 strResult += GetFileName();
609 return strResult;
610 }
611
612 ATL::CString GetFileTitle() const
613 {
614 ATLASSERT(m_hFind != NULL);
615
616 ATL::CString strResult;
617 GetFileTitle(strResult.GetBuffer(MAX_PATH), MAX_PATH);
618 strResult.ReleaseBuffer();
619
620 return strResult;
621 }
622
623 ATL::CString GetFileURL() const
624 {
625 ATLASSERT(m_hFind != NULL);
626
627 ATL::CString strResult("file://");
628 strResult += GetFilePath();
629 return strResult;
630 }
631
632 ATL::CString GetRoot() const
633 {
634 ATLASSERT(m_hFind != NULL);
635
636 ATL::CString str = m_lpszRoot;
637 return str;
638 }
639 #endif // __ATLSTR_H__
640
641 BOOL GetLastWriteTime(FILETIME* pTimeStamp) const
642 {
643 ATLASSERT(m_hFind != NULL);
644 ATLASSERT(pTimeStamp != NULL);
645
646 if(m_bFound && (pTimeStamp != NULL))
647 {
648 *pTimeStamp = m_fd.ftLastWriteTime;
649 return TRUE;
650 }
651
652 return FALSE;
653 }
654
655 BOOL GetLastAccessTime(FILETIME* pTimeStamp) const
656 {
657 ATLASSERT(m_hFind != NULL);
658 ATLASSERT(pTimeStamp != NULL);
659
660 if(m_bFound && (pTimeStamp != NULL))
661 {
662 *pTimeStamp = m_fd.ftLastAccessTime;
663 return TRUE;
664 }
665
666 return FALSE;
667 }
668
669 BOOL GetCreationTime(FILETIME* pTimeStamp) const
670 {
671 ATLASSERT(m_hFind != NULL);
672
673 if(m_bFound && (pTimeStamp != NULL))
674 {
675 *pTimeStamp = m_fd.ftCreationTime;
676 return TRUE;
677 }
678
679 return FALSE;
680 }
681
682 BOOL MatchesMask(DWORD dwMask) const
683 {
684 ATLASSERT(m_hFind != NULL);
685
686 if(m_bFound)
687 return ((m_fd.dwFileAttributes & dwMask) != 0);
688
689 return FALSE;
690 }
691
692 BOOL IsDots() const
693 {
694 ATLASSERT(m_hFind != NULL);
695
696 // return TRUE if the file name is "." or ".." and
697 // the file is a directory
698
699 BOOL bResult = FALSE;
700 if(m_bFound && IsDirectory())
701 {
702 if((m_fd.cFileName[0] == _T('.')) && ((m_fd.cFileName[1] == _T('\0')) || ((m_fd.cFileName[1] == _T('.')) && (m_fd.cFileName[2] == _T('\0')))))
703 bResult = TRUE;
704 }
705
706 return bResult;
707 }
708
709 BOOL IsReadOnly() const
710 {
711 return MatchesMask(FILE_ATTRIBUTE_READONLY);
712 }
713
714 BOOL IsDirectory() const
715 {
716 return MatchesMask(FILE_ATTRIBUTE_DIRECTORY);
717 }
718
719 BOOL IsCompressed() const
720 {
721 return MatchesMask(FILE_ATTRIBUTE_COMPRESSED);
722 }
723
724 BOOL IsSystem() const
725 {
726 return MatchesMask(FILE_ATTRIBUTE_SYSTEM);
727 }
728
729 BOOL IsHidden() const
730 {
731 return MatchesMask(FILE_ATTRIBUTE_HIDDEN);
732 }
733
734 BOOL IsTemporary() const
735 {
736 return MatchesMask(FILE_ATTRIBUTE_TEMPORARY);
737 }
738
739 BOOL IsNormal() const
740 {
741 return MatchesMask(FILE_ATTRIBUTE_NORMAL);
742 }
743
744 BOOL IsArchived() const
745 {
746 return MatchesMask(FILE_ATTRIBUTE_ARCHIVE);
747 }
748
749 // Operations
750 BOOL FindFile(LPCTSTR pstrName = NULL, bool bAutoLongPath = false)
751 {
752 Close();
753
754 if(pstrName == NULL)
755 pstrName = _T("*.*");
756
757 if(bAutoLongPath && (lstrlen(pstrName) >= MAX_PATH))
758 {
759 LPCTSTR lpstrPrefix = _T("\\\\?\\");
760 int cchLongPath = lstrlen(lpstrPrefix) + lstrlen(pstrName) + 1;
761 ATL::CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
762 LPTSTR lpstrLongPath = buff.Allocate(cchLongPath);
763 if(lpstrLongPath != NULL)
764 {
765 ATL::Checked::tcscpy_s(lpstrLongPath, cchLongPath, lpstrPrefix);
766 ATL::Checked::tcscat_s(lpstrLongPath, cchLongPath, pstrName);
767 m_hFind = ::FindFirstFile(lpstrLongPath, &m_fd);
768 }
769 }
770 else
771 {
772 m_hFind = ::FindFirstFile(pstrName, &m_fd);
773 }
774
775 if(m_hFind == INVALID_HANDLE_VALUE)
776 return FALSE;
777
778 int cchRoot = ::GetFullPathName(pstrName, 0, NULL, NULL);
779 if(cchRoot > 0)
780 ATLTRY(m_lpszRoot = new TCHAR[cchRoot]);
781 if(m_lpszRoot == NULL)
782 return FALSE;
783
784 bool bFullPath = (::GetFullPathName(pstrName, cchRoot, m_lpszRoot, NULL) != 0);
785
786 // passed name isn't a valid path but was found by the API
787 ATLASSERT(bFullPath);
788 if(!bFullPath)
789 {
790 Close();
791 ::SetLastError(ERROR_INVALID_NAME);
792 return FALSE;
793 }
794 else
795 {
796 // find the last separator
797 LPTSTR pstrSep = _tcsrchr(m_lpszRoot, m_chDirSeparator);
798 if(pstrSep != NULL)
799 *pstrSep = _T('\0');
800 }
801
802 m_bFound = TRUE;
803
804 return TRUE;
805 }
806
807 BOOL FindNextFile()
808 {
809 ATLASSERT(m_hFind != NULL);
810
811 if(m_hFind == NULL)
812 return FALSE;
813
814 if(!m_bFound)
815 return FALSE;
816
817 m_bFound = ::FindNextFile(m_hFind, &m_fd);
818
819 return m_bFound;
820 }
821
822 void Close()
823 {
824 m_bFound = FALSE;
825
826 delete [] m_lpszRoot;
827 m_lpszRoot = NULL;
828
829 if((m_hFind != NULL) && (m_hFind != INVALID_HANDLE_VALUE))
830 {
831 ::FindClose(m_hFind);
832 m_hFind = NULL;
833 }
834 }
835 };
836
837
838 ///////////////////////////////////////////////////////////////////////////////
839 // CRegProperty and CRegPropertyImpl<> - properties stored in registry
840
841 // How to use: Derive a class from CRegPropertyImpl, add data members
842 // for properties, and add REGPROP map to map properties to registry value names.
843 // You can then call Read() and Write() methods to read and write properties to/from registry.
844 // You can also use CRegProperty class directly, for one time read/write, or for custom stuff.
845
846 #define REGPROP_CURRENTUSER 0x0000
847 #define REGPROP_LOCALMACHINE 0x0001
848 #define REGPROP_READONLY 0x0002
849 #define REGPROP_WRITEONLY 0x0004
850
851 class CRegProperty
852 {
853 public:
854 // Type declarations
855 struct BinaryProp
856 {
857 void* pBinary;
858 ULONG uSize; // buffer size in bytes, used size after read
859
860 BinaryProp() : pBinary(NULL), uSize(0U)
861 { }
862 };
863
864 struct CharArrayProp
865 {
866 LPTSTR lpstrText;
867 ULONG uSize; // buffer size in chars
868
869 CharArrayProp() : lpstrText(NULL), uSize(0U)
870 { }
871 };
872
873 // Data members
874 ATL::CRegKey m_regkey;
875 WORD m_wFlags;
876
877 // Constructor
878 CRegProperty() : m_wFlags(REGPROP_CURRENTUSER)
879 { }
880
881 // Registry key methods
882 LSTATUS OpenRegKey(LPCTSTR lpstrRegKey, bool bWrite)
883 {
884 ATLASSERT(m_regkey.m_hKey == NULL);
885
886 HKEY hKey = ((m_wFlags & REGPROP_LOCALMACHINE) != 0) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
887 REGSAM sam = KEY_READ | KEY_WRITE;
888 LSTATUS lRet = -1;
889 if(bWrite)
890 lRet = m_regkey.Create(hKey, lpstrRegKey, NULL, 0, ((m_wFlags & REGPROP_WRITEONLY) != 0) ? KEY_WRITE : sam);
891 else
892 lRet = m_regkey.Open(hKey, lpstrRegKey, ((m_wFlags & REGPROP_READONLY) != 0) ? KEY_READ : sam);
893
894 return lRet;
895 }
896
897 void CloseRegKey()
898 {
899 LSTATUS lRet = m_regkey.Close();
900 (void)lRet; // avoid level 4 warning
901 ATLASSERT(lRet == ERROR_SUCCESS);
902 }
903
904 // Flag methods
905 WORD GetFlags() const
906 {
907 return m_wFlags;
908 }
909
910 WORD SetFlags(WORD wFlags, WORD wMask = 0)
911 {
912 WORD wPrevFlags = m_wFlags;
913 if(wMask == 0)
914 m_wFlags = wFlags;
915 else
916 m_wFlags = (m_wFlags & ~wMask) | (wFlags & wMask);
917
918 return wPrevFlags;
919 }
920
921 // Generic read/write methods
922 template <class TProp>
923 LSTATUS ReadProp(LPCTSTR lpstrRegValue, TProp& prop)
924 {
925 ATLASSERT(m_regkey.m_hKey != NULL);
926
927 DWORD dwRet = 0;
928 LSTATUS lRet = m_regkey.QueryDWORDValue(lpstrRegValue, dwRet);
929 if(lRet == ERROR_SUCCESS)
930 prop = static_cast<TProp>(dwRet);
931
932 return lRet;
933 }
934
935 template <class TProp>
936 LSTATUS WriteProp(LPCTSTR lpstrRegValue, TProp& prop)
937 {
938 ATLASSERT(m_regkey.m_hKey != NULL);
939
940 return m_regkey.SetDWORDValue(lpstrRegValue, (DWORD)prop);
941 }
942
943 // Specialization for bool
944 template <>
945 LSTATUS ReadProp(LPCTSTR lpstrRegValue, bool& bProp)
946 {
947 ATLASSERT(m_regkey.m_hKey != NULL);
948
949 DWORD dwRet = 0;
950 LSTATUS lRet = m_regkey.QueryDWORDValue(lpstrRegValue, dwRet);
951 if(lRet == ERROR_SUCCESS)
952 bProp = (dwRet != 0);
953
954 return lRet;
955 }
956
957 template <>
958 LSTATUS WriteProp(LPCTSTR lpstrRegValue, bool& bProp)
959 {
960 ATLASSERT(m_regkey.m_hKey != NULL);
961
962 return m_regkey.SetDWORDValue(lpstrRegValue, bProp ? 1 : 0);
963 }
964
965 // Specialization for HFONT
966 template <>
967 LSTATUS ReadProp(LPCTSTR lpstrRegValue, HFONT& hFont)
968 {
969 ATLASSERT(m_regkey.m_hKey != NULL);
970
971 LOGFONT lf = {};
972 ULONG uSize = sizeof(lf);
973 LSTATUS lRet = m_regkey.QueryBinaryValue(lpstrRegValue, &lf, &uSize);
974 if(lRet == ERROR_SUCCESS)
975 {
976 if(hFont != NULL)
977 ::DeleteObject(hFont);
978
979 hFont = ::CreateFontIndirect(&lf);
980 if(hFont == NULL)
981 lRet = ERROR_INVALID_DATA;
982 }
983
984 return lRet;
985 }
986
987 template <>
988 LSTATUS WriteProp(LPCTSTR lpstrRegValue, HFONT& hFont)
989 {
990 ATLASSERT(m_regkey.m_hKey != NULL);
991
992 CLogFont lf(hFont);
993 return m_regkey.SetBinaryValue(lpstrRegValue, &lf, sizeof(lf));
994 }
995
996 // Specialization for BinaryProp
997 template <>
998 LSTATUS ReadProp(LPCTSTR lpstrRegValue, BinaryProp& binProp)
999 {
1000 ATLASSERT(m_regkey.m_hKey != NULL);
1001
1002 ULONG uSize = 0U;
1003 LSTATUS lRet = m_regkey.QueryBinaryValue(lpstrRegValue, NULL, &uSize);
1004 if(lRet == ERROR_SUCCESS)
1005 {
1006 if(uSize <= binProp.uSize)
1007 lRet = m_regkey.QueryBinaryValue(lpstrRegValue, binProp.pBinary, &binProp.uSize);
1008 else
1009 lRet = ERROR_OUTOFMEMORY;
1010 }
1011
1012 return lRet;
1013 }
1014
1015 template <>
1016 LSTATUS WriteProp(LPCTSTR lpstrRegValue, BinaryProp& binProp)
1017 {
1018 ATLASSERT(m_regkey.m_hKey != NULL);
1019
1020 return m_regkey.SetBinaryValue(lpstrRegValue, binProp.pBinary, binProp.uSize);
1021 }
1022
1023 // Specialization for CharArrayProp
1024 template <>
1025 LSTATUS ReadProp(LPCTSTR lpstrRegValue, CharArrayProp& caProp)
1026 {
1027 ATLASSERT(m_regkey.m_hKey != NULL);
1028
1029 ULONG uSize = 0U;
1030 LSTATUS lRet = m_regkey.QueryStringValue(lpstrRegValue, NULL, &uSize);
1031 if(lRet == ERROR_SUCCESS)
1032 {
1033 if(uSize <= caProp.uSize)
1034 lRet = m_regkey.QueryStringValue(lpstrRegValue, caProp.lpstrText, &caProp.uSize);
1035 else
1036 lRet = ERROR_OUTOFMEMORY;
1037 }
1038
1039 return lRet;
1040 }
1041
1042 template <>
1043 LSTATUS WriteProp(LPCTSTR lpstrRegValue, CharArrayProp& caProp)
1044 {
1045 ATLASSERT(m_regkey.m_hKey != NULL);
1046
1047 return m_regkey.SetStringValue(lpstrRegValue, caProp.lpstrText);
1048 }
1049
1050 // Specialization for CString
1051 #ifdef __ATLSTR_H__
1052 template <>
1053 LSTATUS ReadProp(LPCTSTR lpstrRegValue, ATL::CString& strProp)
1054 {
1055 ATLASSERT(m_regkey.m_hKey != NULL);
1056
1057 ULONG uSize = 0U;
1058 LSTATUS lRet = m_regkey.QueryStringValue(lpstrRegValue, NULL, &uSize);
1059 if(lRet == ERROR_SUCCESS)
1060 {
1061 lRet = m_regkey.QueryStringValue(lpstrRegValue, strProp.GetBufferSetLength(uSize), &uSize);
1062 strProp.ReleaseBuffer();
1063 }
1064
1065 return lRet;
1066 }
1067
1068 template <>
1069 LSTATUS WriteProp(LPCTSTR lpstrRegValue, ATL::CString& strProp)
1070 {
1071 ATLASSERT(m_regkey.m_hKey != NULL);
1072
1073 return m_regkey.SetStringValue(lpstrRegValue, (LPCTSTR)strProp);
1074 }
1075 #endif // __ATLSTR_H__
1076
1077 // Static methods for one time read/write
1078 template <class TProp>
1079 static bool ReadOne(LPCTSTR lpstrRegKey, LPCTSTR lpstrRegValue, TProp& prop, WORD wFlags = REGPROP_CURRENTUSER)
1080 {
1081 CRegProperty rp;
1082 rp.SetFlags(wFlags);
1083 LSTATUS lRet = rp.OpenRegKey(lpstrRegKey, false);
1084 if(lRet == ERROR_SUCCESS)
1085 {
1086 lRet = rp.ReadProp(lpstrRegValue, prop);
1087 rp.CloseRegKey();
1088 }
1089
1090 return (lRet == ERROR_SUCCESS) || (lRet == ERROR_FILE_NOT_FOUND);
1091 }
1092
1093 template <class TProp>
1094 static bool WriteOne(LPCTSTR lpstrRegKey, LPCTSTR lpstrRegValue, TProp& prop, WORD wFlags = REGPROP_CURRENTUSER)
1095 {
1096 CRegProperty rp;
1097 rp.SetFlags(wFlags);
1098 LSTATUS lRet = rp.OpenRegKey(lpstrRegKey, true);
1099 if(lRet == ERROR_SUCCESS)
1100 {
1101 lRet = rp.WriteProp(lpstrRegValue, prop);
1102 rp.CloseRegKey();
1103 }
1104
1105 return (lRet == ERROR_SUCCESS);
1106 }
1107 };
1108
1109
1110 #define BEGIN_REGPROP_MAP(class) \
1111 void ReadWriteAll(bool bWrite) \
1112 {
1113
1114 #define REG_PROPERTY(name, prop) \
1115 this->ReadWriteProp(name, prop, bWrite);
1116
1117 #define END_REGPROP_MAP() \
1118 }
1119
1120 template <class T>
1121 class CRegPropertyImpl : public CRegProperty
1122 {
1123 public:
1124 // Methods
1125 void Read(LPCTSTR lpstrRegKey)
1126 {
1127 T* pT = static_cast<T*>(this);
1128 LSTATUS lRet = pT->OpenRegKey(lpstrRegKey, false);
1129 if(lRet == ERROR_SUCCESS)
1130 {
1131 pT->ReadWriteAll(false);
1132 pT->OnRead(lpstrRegKey);
1133
1134 pT->CloseRegKey();
1135 }
1136 else if(lRet != ERROR_FILE_NOT_FOUND)
1137 {
1138 pT->OnReadError(NULL, lRet);
1139 }
1140 }
1141
1142 void Write(LPCTSTR lpstrRegKey)
1143 {
1144 T* pT = static_cast<T*>(this);
1145 LSTATUS lRet = pT->OpenRegKey(lpstrRegKey, true);
1146 if(lRet == ERROR_SUCCESS)
1147 {
1148 pT->ReadWriteAll(true);
1149 pT->OnWrite(lpstrRegKey);
1150
1151 pT->CloseRegKey();
1152 }
1153 else
1154 {
1155 pT->OnWriteError(NULL, lRet);
1156 }
1157 }
1158
1159 // Implementation
1160 template <class TProp>
1161 void ReadWriteProp(LPCTSTR lpstrRegValue, TProp& prop, bool bWrite)
1162 {
1163 T* pT = static_cast<T*>(this);
1164 if(bWrite)
1165 {
1166 LSTATUS lRet = pT->WriteProp(lpstrRegValue, prop);
1167 if(lRet != ERROR_SUCCESS)
1168 pT->OnWriteError(lpstrRegValue, lRet);
1169 }
1170 else
1171 {
1172 LSTATUS lRet = pT->ReadProp(lpstrRegValue, prop);
1173 if((lRet != ERROR_SUCCESS) && (lRet != ERROR_FILE_NOT_FOUND))
1174 pT->OnReadError(lpstrRegValue, lRet);
1175 }
1176 }
1177
1178 // Overrideable handlers
1179 void OnRead(LPCTSTR /*lpstrRegValue*/)
1180 { }
1181
1182 void OnWrite(LPCTSTR /*lpstrRegValue*/)
1183 { }
1184
1185 void OnReadError(LPCTSTR /*lpstrRegValue*/, LSTATUS /*lError*/)
1186 {
1187 ATLASSERT(FALSE);
1188 }
1189
1190 void OnWriteError(LPCTSTR /*lpstrRegValue*/, LSTATUS /*lError*/)
1191 {
1192 ATLASSERT(FALSE);
1193 }
1194 };
1195
1196
1197 ///////////////////////////////////////////////////////////////////////////////
1198 // Global functions for stock GDI objects
1199
1200 inline HPEN AtlGetStockPen(int nPen)
1201 {
1202 ATLASSERT((nPen == WHITE_PEN) || (nPen == BLACK_PEN) || (nPen == NULL_PEN) || (nPen == DC_PEN));
1203 return (HPEN)::GetStockObject(nPen);
1204 }
1205
1206 inline HBRUSH AtlGetStockBrush(int nBrush)
1207 {
1208 ATLASSERT(((nBrush >= WHITE_BRUSH) && (nBrush <= HOLLOW_BRUSH)) || (nBrush == DC_BRUSH));
1209 return (HBRUSH)::GetStockObject(nBrush);
1210 }
1211
1212 inline HFONT AtlGetStockFont(int nFont)
1213 {
1214 ATLASSERT(((nFont >= OEM_FIXED_FONT) && (nFont <= SYSTEM_FIXED_FONT)) || (nFont == DEFAULT_GUI_FONT));
1215 return (HFONT)::GetStockObject(nFont);
1216 }
1217
1218 inline HPALETTE AtlGetStockPalette(int nPalette)
1219 {
1220 ATLASSERT(nPalette == DEFAULT_PALETTE); // the only one supported
1221 return (HPALETTE)::GetStockObject(nPalette);
1222 }
1223
1224
1225 ///////////////////////////////////////////////////////////////////////////////
1226 // Global function for compacting a path by replacing parts with ellipsis
1227
1228 // helper for multi-byte character sets
1229 inline bool _IsDBCSTrailByte(LPCTSTR lpstr, int nChar)
1230 {
1231 #ifndef _UNICODE
1232 int i = nChar;
1233 for( ; i > 0; i--)
1234 {
1235 if(!::IsDBCSLeadByte(lpstr[i - 1]))
1236 break;
1237 }
1238 return ((nChar > 0) && (((nChar - i) & 1) != 0));
1239 #else // _UNICODE
1240 (void)lpstr; // avoid level 4 warning
1241 (void)nChar; // avoid level 4 warning
1242 return false;
1243 #endif // _UNICODE
1244 }
1245
1246 inline bool AtlCompactPath(LPTSTR lpstrOut, LPCTSTR lpstrIn, int cchLen)
1247 {
1248 ATLASSERT(lpstrOut != NULL);
1249 ATLASSERT(lpstrIn != NULL);
1250 ATLASSERT(cchLen > 0);
1251
1252 LPCTSTR szEllipsis = _T("...");
1253 const int cchEndEllipsis = 3;
1254 const int cchMidEllipsis = 4;
1255
1256 if(lstrlen(lpstrIn) < cchLen)
1257 {
1258 ATL::Checked::tcscpy_s(lpstrOut, cchLen, lpstrIn);
1259 return true;
1260 }
1261
1262 lpstrOut[0] = 0;
1263
1264 // check if the separator is a slash or a backslash
1265 TCHAR chSlash = _T('\\');
1266 for(LPTSTR lpstr = (LPTSTR)lpstrIn; *lpstr != 0; lpstr = ::CharNext(lpstr))
1267 {
1268 if((*lpstr == _T('/')) || (*lpstr == _T('\\')))
1269 chSlash = *lpstr;
1270 }
1271
1272 // find the filename portion of the path
1273 LPCTSTR lpstrFileName = lpstrIn;
1274 for(LPCTSTR pPath = lpstrIn; *pPath; pPath = ::CharNext(pPath))
1275 {
1276 if(((pPath[0] == _T('\\')) || (pPath[0] == _T(':')) || (pPath[0] == _T('/')))
1277 && pPath[1] && (pPath[1] != _T('\\')) && (pPath[1] != _T('/')))
1278 lpstrFileName = pPath + 1;
1279 }
1280 int cchFileName = lstrlen(lpstrFileName);
1281
1282 // handle just the filename without a path
1283 if((lpstrFileName == lpstrIn) && (cchLen > cchEndEllipsis))
1284 {
1285 bool bRet = (ATL::Checked::tcsncpy_s(lpstrOut, cchLen, lpstrIn, cchLen - cchEndEllipsis - 1) == 0);
1286 if(bRet)
1287 {
1288 #ifndef _UNICODE
1289 if(_IsDBCSTrailByte(lpstrIn, cchLen - cchEndEllipsis))
1290 lpstrOut[cchLen - cchEndEllipsis - 1] = 0;
1291 #endif // _UNICODE
1292 ATL::Checked::tcscat_s(lpstrOut, cchLen, szEllipsis);
1293 }
1294 return bRet;
1295 }
1296
1297 // handle just ellipsis
1298 if((cchLen < (cchMidEllipsis + cchEndEllipsis)))
1299 {
1300 for(int i = 0; i < cchLen - 1; i++)
1301 lpstrOut[i] = ((i + 1) == cchMidEllipsis) ? chSlash : _T('.');
1302 lpstrOut[cchLen - 1] = 0;
1303 return true;
1304 }
1305
1306 // calc how much we have to copy
1307 int cchToCopy = cchLen - (cchMidEllipsis + cchFileName) - 1;
1308
1309 if(cchToCopy < 0)
1310 cchToCopy = 0;
1311
1312 #ifndef _UNICODE
1313 if((cchToCopy > 0) && _IsDBCSTrailByte(lpstrIn, cchToCopy))
1314 cchToCopy--;
1315 #endif // _UNICODE
1316
1317 bool bRet = (ATL::Checked::tcsncpy_s(lpstrOut, cchLen, lpstrIn, cchToCopy) == 0);
1318 if(!bRet)
1319 return false;
1320
1321 // add ellipsis
1322 ATL::Checked::tcscat_s(lpstrOut, cchLen, szEllipsis);
1323 TCHAR szSlash[2] = { chSlash, 0 };
1324 ATL::Checked::tcscat_s(lpstrOut, cchLen, szSlash);
1325
1326 // add filename (and ellipsis, if needed)
1327 if(cchLen > (cchMidEllipsis + cchFileName))
1328 {
1329 ATL::Checked::tcscat_s(lpstrOut, cchLen, lpstrFileName);
1330 }
1331 else
1332 {
1333 cchToCopy = cchLen - cchMidEllipsis - cchEndEllipsis - 1;
1334 #ifndef _UNICODE
1335 if((cchToCopy > 0) && _IsDBCSTrailByte(lpstrFileName, cchToCopy))
1336 cchToCopy--;
1337 #endif // _UNICODE
1338 bRet = (ATL::Checked::tcsncpy_s(&lpstrOut[cchMidEllipsis], cchLen - cchMidEllipsis, lpstrFileName, cchToCopy) == 0);
1339 if(bRet)
1340 ATL::Checked::tcscat_s(lpstrOut, cchLen, szEllipsis);
1341 }
1342
1343 return bRet;
1344 }
1345
1346 } // namespace WTL
1347
1348 #endif // __ATLMISC_H__