VZ: I removed (CASTWNDPROC) from ::CallWndProc - it doesn't compile here with it
[wxWidgets.git] / src / msw / registry.cpp
0 / 773 (  0%)
CommitLineData
1///////////////////////////////////////////////////////////////////////////////
2// Name: msw/registry.cpp
3// Purpose: implementation of registry classes and functions
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 03.04.98
7// RCS-ID: $Id$
8// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9// Licence: wxWindows license
10// TODO: - parsing of registry key names
11// - support of other (than REG_SZ/REG_DWORD) registry types
12// - add high level functions (RegisterOleServer, ...)
13///////////////////////////////////////////////////////////////////////////////
14
15// ============================================================================
16// declarations
17// ============================================================================
18
19// ----------------------------------------------------------------------------
20// headers
21// ----------------------------------------------------------------------------
22
23// for compilers that support precompilation, includes "wx.h".
24#include "wx/wxprec.h"
25
26#ifdef __BORLANDC__
27#pragma hdrstop
28#endif
29
30// other wxWindows headers
31#include "wx/string.h"
32#include "wx/intl.h"
33#include "wx/log.h"
34
35// Windows headers
36/*
37#define STRICT
38#define WIN32_LEAN_AND_MEAN
39*/
40
41#include <windows.h>
42
43// other std headers
44#include <stdlib.h> // for _MAX_PATH
45
46#ifndef _MAX_PATH
47 #define _MAX_PATH 512
48#endif
49
50// our header
51#define HKEY_DEFINED // already defined in windows.h
52#include "wx/msw/registry.h"
53
54// some registry functions don't like signed chars
55typedef unsigned char *RegString;
56
57// ----------------------------------------------------------------------------
58// constants
59// ----------------------------------------------------------------------------
60
61// the standard key names, short names and handles all bundled together for
62// convenient access
63static struct
64{
65 HKEY hkey;
66 const char *szName;
67 const char *szShortName;
68}
69aStdKeys[] =
70{
71 { HKEY_CLASSES_ROOT, "HKEY_CLASSES_ROOT", "HKCR" },
72#ifdef __WIN32__
73 { HKEY_CURRENT_USER, "HKEY_CURRENT_USER", "HKCU" },
74 { HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", "HKLM" },
75 { HKEY_USERS, "HKEY_USERS", "HKU" }, // short name?
76 { HKEY_PERFORMANCE_DATA, "HKEY_PERFORMANCE_DATA", "HKPD" },
77#if WINVER >= 0x0400
78 { HKEY_CURRENT_CONFIG, "HKEY_CURRENT_CONFIG", "HKCC" },
79#ifndef __GNUWIN32__
80 { HKEY_DYN_DATA, "HKEY_DYN_DATA", "HKDD" }, // short name?
81#endif //GNUWIN32
82#endif //WINVER >= 4.0
83#endif //WIN32
84};
85
86// the registry name separator (perhaps one day MS will change it to '/' ;-)
87#define REG_SEPARATOR '\\'
88
89// ----------------------------------------------------------------------------
90// macros
91// ----------------------------------------------------------------------------
92// @ const_cast<> is not yet supported by all compilers
93#define CONST_CAST ((wxRegKey *)this)->
94
95#if !USE_MUTABLE
96 #define m_dwLastError CONST_CAST m_dwLastError
97#endif
98
99// ----------------------------------------------------------------------------
100// non member functions
101// ----------------------------------------------------------------------------
102
103// removes the trailing backslash from the string if it has one
104static inline void RemoveTrailingSeparator(wxString& str);
105
106// returns TRUE if given registry key exists
107static bool KeyExists(HKEY hRootKey, const char *szKey);
108
109// combines value and key name (uses static buffer!)
110static const char *GetFullName(const wxRegKey *pKey,
111 const char *szValue = NULL);
112
113// ============================================================================
114// implementation of wxRegKey class
115// ============================================================================
116
117// ----------------------------------------------------------------------------
118// static functions and variables
119// ----------------------------------------------------------------------------
120
121const size_t wxRegKey::nStdKeys = WXSIZEOF(aStdKeys);
122
123// @@ should take a `StdKey key', but as it's often going to be used in loops
124// it would require casts in user code.
125const char *wxRegKey::GetStdKeyName(uint key)
126{
127 // return empty string if key is invalid
128 wxCHECK_MSG( key < nStdKeys, "", "invalid key in wxRegKey::GetStdKeyName" );
129
130 return aStdKeys[key].szName;
131}
132
133const char *wxRegKey::GetStdKeyShortName(uint key)
134{
135 // return empty string if key is invalid
136 wxCHECK( key < nStdKeys, "" );
137
138 return aStdKeys[key].szShortName;
139}
140
141wxRegKey::StdKey wxRegKey::ExtractKeyName(wxString& strKey)
142{
143 wxString strRoot = strKey.Left(REG_SEPARATOR);
144
145 HKEY hRootKey;
146 uint ui;
147 for ( ui = 0; ui < nStdKeys; ui++ ) {
148 if ( strRoot.CmpNoCase(aStdKeys[ui].szName) == 0 ||
149 strRoot.CmpNoCase(aStdKeys[ui].szShortName) == 0 ) {
150 hRootKey = aStdKeys[ui].hkey;
151 break;
152 }
153 }
154
155 if ( ui == nStdKeys ) {
156 wxFAIL_MSG("invalid key prefix in wxRegKey::ExtractKeyName.");
157
158 hRootKey = HKEY_CLASSES_ROOT;
159 }
160 else {
161 strKey = strKey.After(REG_SEPARATOR);
162 if ( !strKey.IsEmpty() && strKey.Last() == REG_SEPARATOR )
163 strKey.Truncate(strKey.Len() - 1);
164 }
165
166 return (wxRegKey::StdKey)(int)hRootKey;
167}
168
169wxRegKey::StdKey wxRegKey::GetStdKeyFromHkey(HKEY hkey)
170{
171 for ( uint ui = 0; ui < nStdKeys; ui++ ) {
172 if ( aStdKeys[ui].hkey == hkey )
173 return (StdKey)ui;
174 }
175
176 wxFAIL_MSG("non root hkey passed to wxRegKey::GetStdKeyFromHkey.");
177
178 return HKCR;
179}
180
181// ----------------------------------------------------------------------------
182// ctors and dtor
183// ----------------------------------------------------------------------------
184
185wxRegKey::wxRegKey()
186{
187 m_hKey = 0;
188 m_hRootKey = aStdKeys[HKCR].hkey;
189 m_dwLastError = 0;
190}
191
192wxRegKey::wxRegKey(const wxString& strKey) : m_strKey(strKey)
193{
194 m_hRootKey = aStdKeys[ExtractKeyName(m_strKey)].hkey;
195 m_hKey = NULL;
196 m_dwLastError = 0;
197}
198
199// parent is a predefined (and preopened) key
200wxRegKey::wxRegKey(StdKey keyParent, const wxString& strKey) : m_strKey(strKey)
201{
202 RemoveTrailingSeparator(m_strKey);
203 m_hRootKey = aStdKeys[keyParent].hkey;
204 m_hKey = NULL;
205 m_dwLastError = 0;
206}
207
208// parent is a normal regkey
209wxRegKey::wxRegKey(const wxRegKey& keyParent, const wxString& strKey)
210 : m_strKey(keyParent.m_strKey)
211{
212 // combine our name with parent's to get the full name
213 if ( !strKey.IsEmpty() && strKey[0] != REG_SEPARATOR )
214 m_strKey += REG_SEPARATOR;
215
216 m_strKey += strKey;
217 RemoveTrailingSeparator(m_strKey);
218
219 m_hRootKey = keyParent.m_hRootKey;
220 m_hKey = NULL;
221 m_dwLastError = 0;
222}
223
224// dtor closes the key releasing system resource
225wxRegKey::~wxRegKey()
226{
227 Close();
228}
229
230// ----------------------------------------------------------------------------
231// change the key name/hkey
232// ----------------------------------------------------------------------------
233
234// set the full key name
235void wxRegKey::SetName(const wxString& strKey)
236{
237 Close();
238
239 m_strKey = strKey;
240 m_hRootKey = aStdKeys[ExtractKeyName(m_strKey)].hkey;
241}
242
243// the name is relative to the parent key
244void wxRegKey::SetName(StdKey keyParent, const wxString& strKey)
245{
246 Close();
247
248 m_strKey = strKey;
249 RemoveTrailingSeparator(m_strKey);
250 m_hRootKey = aStdKeys[keyParent].hkey;
251}
252
253// the name is relative to the parent key
254void wxRegKey::SetName(const wxRegKey& keyParent, const wxString& strKey)
255{
256 Close();
257
258 // combine our name with parent's to get the full name
259 m_strKey = keyParent.m_strKey;
260 if ( !strKey.IsEmpty() && strKey[0] != REG_SEPARATOR )
261 m_strKey += REG_SEPARATOR;
262 m_strKey += strKey;
263
264 RemoveTrailingSeparator(m_strKey);
265
266 m_hRootKey = keyParent.m_hRootKey;
267}
268
269// hKey should be opened and will be closed in wxRegKey dtor
270void wxRegKey::SetHkey(HKEY hKey)
271{
272 Close();
273
274 m_hKey = hKey;
275}
276
277// ----------------------------------------------------------------------------
278// info about the key
279// ----------------------------------------------------------------------------
280
281// returns TRUE if the key exists
282bool wxRegKey::Exists() const
283{
284 // opened key has to exist, try to open it if not done yet
285 return IsOpened() ? TRUE : KeyExists(m_hRootKey, m_strKey);
286}
287
288// returns the full name of the key (prefix is abbreviated if bShortPrefix)
289wxString wxRegKey::GetName(bool bShortPrefix) const
290{
291 StdKey key = GetStdKeyFromHkey(m_hRootKey);
292 wxString str = bShortPrefix ? aStdKeys[key].szShortName
293 : aStdKeys[key].szName;
294 if ( !m_strKey.IsEmpty() )
295 str << "\\" << m_strKey;
296
297 return str;
298}
299
300bool wxRegKey::GetKeyInfo(ulong *pnSubKeys,
301 ulong *pnMaxKeyLen,
302 ulong *pnValues,
303 ulong *pnMaxValueLen) const
304{
305#ifdef __WIN32__
306 m_dwLastError = ::RegQueryInfoKey
307 (
308 m_hKey,
309 NULL, // class name
310 NULL, // (ptr to) size of class name buffer
311 RESERVED,
312 pnSubKeys, // [out] number of subkeys
313 pnMaxKeyLen, // [out] max length of a subkey name
314 NULL, // longest subkey class name
315 pnValues, // [out] number of values
316 pnMaxValueLen, // [out] max length of a value name
317 NULL, // longest value data
318 NULL, // security descriptor
319 NULL // time of last modification
320 );
321
322 if ( m_dwLastError != ERROR_SUCCESS ) {
323 wxLogSysError(m_dwLastError, _("can't get info about registry key '%s'"),
324 GetName().c_str());
325 return FALSE;
326 }
327 else
328 return TRUE;
329#else // Win16
330 wxFAIL_MSG("GetKeyInfo() not implemented");
331
332 return FALSE;
333#endif
334}
335
336// ----------------------------------------------------------------------------
337// operations
338// ----------------------------------------------------------------------------
339
340// opens key (it's not an error to call Open() on an already opened key)
341bool wxRegKey::Open()
342{
343 if ( IsOpened() )
344 return TRUE;
345
346 m_dwLastError = RegOpenKey(m_hRootKey, m_strKey, &m_hKey);
347 if ( m_dwLastError != ERROR_SUCCESS ) {
348 wxLogSysError(m_dwLastError, _("can't open registry key '%s'"),
349 GetName().c_str());
350 return FALSE;
351 }
352 else
353 return TRUE;
354}
355
356// creates key, failing if it exists and !bOkIfExists
357bool wxRegKey::Create(bool bOkIfExists)
358{
359 // check for existence only if asked (i.e. order is important!)
360 if ( !bOkIfExists && Exists() ) {
361 return FALSE;
362 }
363
364 if ( IsOpened() )
365 return TRUE;
366
367 m_dwLastError = RegCreateKey(m_hRootKey, m_strKey, &m_hKey);
368 if ( m_dwLastError != ERROR_SUCCESS ) {
369 wxLogSysError(m_dwLastError, _("can't create registry key '%s'"),
370 GetName().c_str());
371 return FALSE;
372 }
373 else
374 return TRUE;
375}
376
377// close the key, it's not an error to call it when not opened
378bool wxRegKey::Close()
379{
380 if ( IsOpened() ) {
381 m_dwLastError = RegCloseKey(m_hKey);
382 if ( m_dwLastError != ERROR_SUCCESS ) {
383 wxLogSysError(m_dwLastError, _("can't close registry key '%s'"),
384 GetName().c_str());
385
386 m_hKey = 0;
387 return FALSE;
388 }
389 else {
390 m_hKey = 0;
391 }
392 }
393
394 return TRUE;
395}
396
397// ----------------------------------------------------------------------------
398// delete keys/values
399// ----------------------------------------------------------------------------
400bool wxRegKey::DeleteSelf()
401{
402 {
403 wxLogNull nolog;
404 if ( !Open() ) {
405 // it already doesn't exist - ok!
406 return TRUE;
407 }
408 }
409
410 // we can't delete keys while enumerating because it confuses GetNextKey, so
411 // we first save the key names and then delete them all
412 wxArrayString astrSubkeys;
413
414 wxString strKey;
415 long lIndex;
416 bool bCont = GetFirstKey(strKey, lIndex);
417 while ( bCont ) {
418 astrSubkeys.Add(strKey);
419
420 bCont = GetNextKey(strKey, lIndex);
421 }
422
423 uint nKeyCount = astrSubkeys.Count();
424 for ( uint nKey = 0; nKey < nKeyCount; nKey++ ) {
425 wxRegKey key(*this, astrSubkeys[nKey]);
426 if ( !key.DeleteSelf() )
427 return FALSE;
428 }
429
430 // now delete this key itself
431 Close();
432
433 m_dwLastError = RegDeleteKey(m_hRootKey, m_strKey);
434 if ( m_dwLastError != ERROR_SUCCESS ) {
435 wxLogSysError(m_dwLastError, _("can't delete key '%s'"),
436 GetName().c_str());
437 return FALSE;
438 }
439
440 return TRUE;
441}
442
443bool wxRegKey::DeleteKey(const char *szKey)
444{
445 if ( !Open() )
446 return FALSE;
447
448 wxRegKey key(*this, szKey);
449 return key.DeleteSelf();
450}
451
452bool wxRegKey::DeleteValue(const char *szValue)
453{
454 if ( !Open() )
455 return FALSE;
456
457 #ifdef __WIN32__
458 m_dwLastError = RegDeleteValue(m_hKey, szValue);
459 if ( m_dwLastError != ERROR_SUCCESS ) {
460 wxLogSysError(m_dwLastError, _("can't delete value '%s' from key '%s'"),
461 szValue, GetName().c_str());
462 return FALSE;
463 }
464 #else //WIN16
465 // named registry values don't exist in Win16 world
466 wxASSERT( IsEmpty(szValue) );
467
468 // just set the (default and unique) value of the key to ""
469 m_dwLastError = RegSetValue(m_hKey, NULL, REG_SZ, "", RESERVED);
470 if ( m_dwLastError != ERROR_SUCCESS ) {
471 wxLogSysError(m_dwLastError, _("can't delete value of key '%s'"),
472 GetName().c_str());
473 return FALSE;
474 }
475 #endif //WIN16/32
476
477 return TRUE;
478}
479
480// ----------------------------------------------------------------------------
481// access to values and subkeys
482// ----------------------------------------------------------------------------
483
484// return TRUE if value exists
485bool wxRegKey::HasValue(const char *szValue) const
486{
487 #ifdef __WIN32__
488 if ( CONST_CAST Open() ) {
489 return RegQueryValueEx(m_hKey, szValue, RESERVED,
490 NULL, NULL, NULL) == ERROR_SUCCESS;
491 }
492 else
493 return FALSE;
494 #else // WIN16
495 // only unnamed value exists
496 return IsEmpty(szValue);
497 #endif // WIN16/32
498}
499
500// returns TRUE if this key has any subkeys
501bool wxRegKey::HasSubkeys() const
502{
503 // just call GetFirstKey with dummy parameters
504 wxString str;
505 long l;
506 return CONST_CAST GetFirstKey(str, l);
507}
508
509// returns TRUE if given subkey exists
510bool wxRegKey::HasSubKey(const char *szKey) const
511{
512 if ( CONST_CAST Open() )
513 return KeyExists(m_hKey, szKey);
514 else
515 return FALSE;
516}
517
518wxRegKey::ValueType wxRegKey::GetValueType(const char *szValue)
519{
520 #ifdef __WIN32__
521 if ( !Open() )
522 return Type_None;
523
524 DWORD dwType;
525 m_dwLastError = RegQueryValueEx(m_hKey, szValue, RESERVED,
526 &dwType, NULL, NULL);
527 if ( m_dwLastError != ERROR_SUCCESS ) {
528 wxLogSysError(m_dwLastError, _("can't read value of key '%s'"),
529 GetName().c_str());
530 return Type_None;
531 }
532
533 return (ValueType)dwType;
534 #else //WIN16
535 return IsEmpty(szValue) ? Type_String : Type_None;
536 #endif //WIN16/32
537}
538
539#ifdef __WIN32__
540bool wxRegKey::SetValue(const char *szValue, long lValue)
541{
542 if ( CONST_CAST Open() ) {
543 m_dwLastError = RegSetValueEx(m_hKey, szValue, RESERVED, REG_DWORD,
544 (RegString)&lValue, sizeof(lValue));
545 if ( m_dwLastError == ERROR_SUCCESS )
546 return TRUE;
547 }
548
549 wxLogSysError(m_dwLastError, _("can't set value of '%s'"),
550 GetFullName(this, szValue));
551 return FALSE;
552}
553
554bool wxRegKey::QueryValue(const char *szValue, long *plValue) const
555{
556 if ( CONST_CAST Open() ) {
557 DWORD dwType, dwSize = sizeof(DWORD);
558 RegString pBuf = (RegString)plValue;
559 m_dwLastError = RegQueryValueEx(m_hKey, szValue, RESERVED,
560 &dwType, pBuf, &dwSize);
561 if ( m_dwLastError != ERROR_SUCCESS ) {
562 wxLogSysError(m_dwLastError, _("can't read value of key '%s'"),
563 GetName().c_str());
564 return FALSE;
565 }
566 else {
567 // check that we read the value of right type
568 wxASSERT_MSG( dwType == REG_DWORD,
569 "Type mismatch in wxRegKey::QueryValue()." );
570
571 return TRUE;
572 }
573 }
574 else
575 return FALSE;
576}
577
578#endif //Win32
579
580bool wxRegKey::QueryValue(const char *szValue, wxString& strValue) const
581{
582 if ( CONST_CAST Open() ) {
583 #ifdef __WIN32__
584 // first get the type and size of the data
585 DWORD dwType, dwSize;
586 m_dwLastError = RegQueryValueEx(m_hKey, szValue, RESERVED,
587 &dwType, NULL, &dwSize);
588 if ( m_dwLastError == ERROR_SUCCESS ) {
589 RegString pBuf = (RegString)strValue.GetWriteBuf(dwSize);
590 m_dwLastError = RegQueryValueEx(m_hKey, szValue, RESERVED,
591 &dwType, pBuf, &dwSize);
592 strValue.UngetWriteBuf();
593 if ( m_dwLastError == ERROR_SUCCESS ) {
594 // check that it was the right type
595 wxASSERT_MSG( dwType == REG_SZ,
596 "Type mismatch in wxRegKey::QueryValue()." );
597
598 return TRUE;
599 }
600 }
601 #else //WIN16
602 // named registry values don't exist in Win16
603 wxASSERT( IsEmpty(szValue) );
604
605 m_dwLastError = RegQueryValue(m_hKey, 0, strValue.GetWriteBuf(256), &l);
606 strValue.UngetWriteBuf();
607 if ( m_dwLastError == ERROR_SUCCESS )
608 return TRUE;
609 #endif //WIN16/32
610 }
611
612 wxLogSysError(m_dwLastError, _("can't read value of '%s'"),
613 GetFullName(this, szValue));
614 return FALSE;
615}
616
617bool wxRegKey::SetValue(const char *szValue, const wxString& strValue)
618{
619 if ( CONST_CAST Open() ) {
620 #ifdef __WIN32__
621 m_dwLastError = RegSetValueEx(m_hKey, szValue, RESERVED, REG_SZ,
622 (RegString)strValue.c_str(),
623 strValue.Len() + 1);
624 if ( m_dwLastError == ERROR_SUCCESS )
625 return TRUE;
626 #else //WIN16
627 // named registry values don't exist in Win16
628 wxASSERT( IsEmpty(szValue) );
629
630 m_dwLastError = RegSetValue(m_hKey, NULL, REG_SZ, strValue, NULL);
631 if ( m_dwLastError == ERROR_SUCCESS )
632 return TRUE;
633 #endif //WIN16/32
634 }
635
636 wxLogSysError(m_dwLastError, _("can't set value of '%s'"),
637 GetFullName(this, szValue));
638 return FALSE;
639}
640
641wxRegKey::operator wxString() const
642{
643 wxString str;
644 QueryValue(NULL, str);
645 return str;
646}
647
648// ----------------------------------------------------------------------------
649// enumeration
650// NB: all these functions require an index variable which allows to have
651// several concurrently running indexations on the same key
652// ----------------------------------------------------------------------------
653
654bool wxRegKey::GetFirstValue(wxString& strValueName, long& lIndex)
655{
656 if ( !Open() )
657 return FALSE;
658
659 lIndex = 0;
660 return GetNextValue(strValueName, lIndex);
661}
662
663bool wxRegKey::GetNextValue(wxString& strValueName, long& lIndex) const
664{
665 wxASSERT( IsOpened() );
666
667 // are we already at the end of enumeration?
668 if ( lIndex == -1 )
669 return FALSE;
670
671 #ifdef __WIN32__
672 char szValueName[1024]; // @@ use RegQueryInfoKey...
673 DWORD dwValueLen = WXSIZEOF(szValueName);
674
675 lIndex++;
676 m_dwLastError = RegEnumValue(m_hKey, lIndex,
677 szValueName, &dwValueLen,
678 RESERVED,
679 NULL, // [out] type
680 NULL, // [out] buffer for value
681 NULL); // [i/o] it's length
682
683 if ( m_dwLastError != ERROR_SUCCESS ) {
684 if ( m_dwLastError == ERROR_NO_MORE_ITEMS ) {
685 m_dwLastError = ERROR_SUCCESS;
686 lIndex = -1;
687 }
688 else {
689 wxLogSysError(m_dwLastError, _("can't enumerate values of key '%s'"),
690 GetName().c_str());
691 }
692
693 return FALSE;
694 }
695
696 strValueName = szValueName;
697 #else //WIN16
698 // only one unnamed value
699 wxASSERT( lIndex == 0 );
700
701 lIndex = -1;
702 strValueName.Empty();
703 #endif
704
705 return TRUE;
706}
707
708bool wxRegKey::GetFirstKey(wxString& strKeyName, long& lIndex)
709{
710 if ( !Open() )
711 return FALSE;
712
713 lIndex = 0;
714 return GetNextKey(strKeyName, lIndex);
715}
716
717bool wxRegKey::GetNextKey(wxString& strKeyName, long& lIndex) const
718{
719 wxASSERT( IsOpened() );
720
721 // are we already at the end of enumeration?
722 if ( lIndex == -1 )
723 return FALSE;
724
725 char szKeyName[_MAX_PATH + 1];
726 m_dwLastError = RegEnumKey(m_hKey, lIndex++, szKeyName, WXSIZEOF(szKeyName));
727
728 if ( m_dwLastError != ERROR_SUCCESS ) {
729 if ( m_dwLastError == ERROR_NO_MORE_ITEMS ) {
730 m_dwLastError = ERROR_SUCCESS;
731 lIndex = -1;
732 }
733 else {
734 wxLogSysError(m_dwLastError, _("can't enumerate subkeys of key '%s'"),
735 GetName().c_str());
736 }
737
738 return FALSE;
739 }
740
741 strKeyName = szKeyName;
742 return TRUE;
743}
744
745// ============================================================================
746// implementation of global functions
747// ============================================================================
748bool KeyExists(HKEY hRootKey, const char *szKey)
749{
750 HKEY hkeyDummy;
751 if ( RegOpenKey(hRootKey, szKey, &hkeyDummy) == ERROR_SUCCESS ) {
752 RegCloseKey(hkeyDummy);
753 return TRUE;
754 }
755 else
756 return FALSE;
757}
758
759const char *GetFullName(const wxRegKey *pKey, const char *szValue)
760{
761 static wxString s_str;
762 s_str = pKey->GetName();
763 if ( !IsEmpty(szValue) )
764 s_str << "\\" << szValue;
765
766 return s_str.c_str();
767}
768
769void RemoveTrailingSeparator(wxString& str)
770{
771 if ( !str.IsEmpty() && str.Last() == REG_SEPARATOR )
772 str.Truncate(str.Len() - 1);
773}