1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2001
20 * the Initial Developer. All Rights Reserved.
23 * Conrad Carlen <ccarlen@netscape.com>
24 * Ere Maijala <emaijala@kolumbus.fi>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsPluginDirServiceProvider.h"
43 #include "nsILocalFile.h"
44 #include "nsIPrefBranch.h"
45 #include "nsIPrefService.h"
46 #include "nsDependentString.h"
47 #include "nsXPIDLString.h"
49 #include "nsArrayEnumerator.h"
51 typedef struct structVer
60 ClearVersion(verBlock
*ver
)
69 FileExists(LPCSTR szFile
)
71 return GetFileAttributes(szFile
) != 0xFFFFFFFF;
74 // Get file version information from a file
76 GetFileVersion(LPSTR szFile
, verBlock
*vbVersion
)
84 VS_FIXEDFILEINFO
*lpBuffer2
;
86 ClearVersion(vbVersion
);
87 if (FileExists(szFile
)) {
89 dwLen
= GetFileVersionInfoSize(szFile
, &dwHandle
);
90 lpData
= (LPVOID
)malloc(dwLen
);
93 if (lpData
&& GetFileVersionInfo(szFile
, dwHandle
, dwLen
, lpData
) != 0) {
94 if (VerQueryValue(lpData
, "\\", &lpBuffer
, &uLen
) != 0) {
95 lpBuffer2
= (VS_FIXEDFILEINFO
*)lpBuffer
;
97 vbVersion
->wMajor
= HIWORD(lpBuffer2
->dwFileVersionMS
);
98 vbVersion
->wMinor
= LOWORD(lpBuffer2
->dwFileVersionMS
);
99 vbVersion
->wRelease
= HIWORD(lpBuffer2
->dwFileVersionLS
);
100 vbVersion
->wBuild
= LOWORD(lpBuffer2
->dwFileVersionLS
);
106 /* File does not exist */
113 // Will deep copy ver2 into ver1
115 CopyVersion(verBlock
*ver1
, verBlock
*ver2
)
117 ver1
->wMajor
= ver2
->wMajor
;
118 ver1
->wMinor
= ver2
->wMinor
;
119 ver1
->wRelease
= ver2
->wRelease
;
120 ver1
->wBuild
= ver2
->wBuild
;
123 // Convert a string version to a version struct
125 TranslateVersionStr(const char* szVersion
, verBlock
*vbVersion
)
131 LPSTR szJavaBuild
= NULL
;
133 char *strVer
= nsnull
;
135 strVer
= PL_strdup(szVersion
);
140 ClearVersion(vbVersion
);
144 // Java may be using an underscore instead of a dot for the build ID
145 szJavaBuild
= strchr(strVer
, '_');
147 szJavaBuild
[0] = '.';
150 szNum1
= strtok(strVer
, ".");
151 szNum2
= strtok(NULL
, ".");
152 szNum3
= strtok(NULL
, ".");
153 szNum4
= strtok(NULL
, ".");
155 vbVersion
->wMajor
= szNum1
? atoi(szNum1
) : 0;
156 vbVersion
->wMinor
= szNum2
? atoi(szNum2
) : 0;
157 vbVersion
->wRelease
= szNum3
? atoi(szNum3
) : 0;
158 vbVersion
->wBuild
= szNum4
? atoi(szNum4
) : 0;
163 // Compare two version struct, return zero if the same
165 CompareVersion(verBlock vbVersionOld
, verBlock vbVersionNew
)
167 if (vbVersionOld
.wMajor
> vbVersionNew
.wMajor
) {
169 } else if (vbVersionOld
.wMajor
< vbVersionNew
.wMajor
) {
173 if (vbVersionOld
.wMinor
> vbVersionNew
.wMinor
) {
175 } else if (vbVersionOld
.wMinor
< vbVersionNew
.wMinor
) {
179 if (vbVersionOld
.wRelease
> vbVersionNew
.wRelease
) {
181 } else if (vbVersionOld
.wRelease
< vbVersionNew
.wRelease
) {
185 if (vbVersionOld
.wBuild
> vbVersionNew
.wBuild
) {
187 } else if (vbVersionOld
.wBuild
< vbVersionNew
.wBuild
) {
191 /* the versions are all the same */
196 // Indicate whether we should try to use the new NPRuntime-based Java
197 // Plug-In if it's available
199 TryToUseNPRuntimeJavaPlugIn(const char* javaVersion
)
202 char keyName
[_MAX_PATH
];
204 PL_strcat(keyName
, "Software\\JavaSoft\\Java Plug-in\\");
205 PL_strcat(keyName
, javaVersion
);
207 DWORD valSize
= sizeof(DWORD
);
209 if (ERROR_SUCCESS
!= ::RegOpenKeyEx(HKEY_LOCAL_MACHINE
,
210 keyName
, 0, KEY_READ
, &javaKey
)) {
214 // Look for "UseNewJavaPlugin"
215 if (ERROR_SUCCESS
!= ::RegQueryValueEx(javaKey
, "UseNewJavaPlugin",
222 ::RegCloseKey(javaKey
);
223 return (val
== 0) ? PR_FALSE
: PR_TRUE
;
227 //*****************************************************************************
228 // nsPluginDirServiceProvider::Constructor/Destructor
229 //*****************************************************************************
231 nsPluginDirServiceProvider::nsPluginDirServiceProvider()
235 nsPluginDirServiceProvider::~nsPluginDirServiceProvider()
239 //*****************************************************************************
240 // nsPluginDirServiceProvider::nsISupports
241 //*****************************************************************************
243 NS_IMPL_THREADSAFE_ISUPPORTS1(nsPluginDirServiceProvider
,
244 nsIDirectoryServiceProvider
)
246 //*****************************************************************************
247 // nsPluginDirServiceProvider::nsIDirectoryServiceProvider
248 //*****************************************************************************
251 nsPluginDirServiceProvider::GetFile(const char *prop
, PRBool
*persistant
,
254 nsCOMPtr
<nsILocalFile
> localFile
;
255 nsresult rv
= NS_ERROR_FAILURE
;
259 *persistant
= PR_FALSE
;
261 nsCOMPtr
<nsIPrefBranch
> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID
));
266 if (nsCRT::strcmp(prop
, NS_WIN_4DOTX_SCAN_KEY
) == 0) {
267 // Check our prefs to see if scanning the 4.x folder has been
268 // explictly overriden failure to get the pref is okay, we'll do
269 // what we've been doing -- a filtered scan
271 if (NS_SUCCEEDED(prefs
->GetBoolPref(NS_WIN_4DOTX_SCAN_KEY
, &bScan4x
)) &&
276 // Look for the plugin folder that the user has in their
277 // Communicator 4x install
281 char szKey
[_MAX_PATH
] = "Software\\Netscape\\Netscape Navigator";
282 char path
[_MAX_PATH
];
284 result
= ::RegOpenKeyEx(HKEY_LOCAL_MACHINE
, szKey
, 0, KEY_READ
, &keyloc
);
286 if (result
== ERROR_SUCCESS
) {
287 char current_version
[80];
288 DWORD length
= sizeof(current_version
);
290 result
= ::RegQueryValueEx(keyloc
, "CurrentVersion", NULL
, &type
,
291 (LPBYTE
)¤t_version
, &length
);
293 ::RegCloseKey(keyloc
);
294 PL_strcat(szKey
, "\\");
295 PL_strcat(szKey
, current_version
);
296 PL_strcat(szKey
, "\\Main");
297 result
= ::RegOpenKeyEx(HKEY_LOCAL_MACHINE
, szKey
, 0, KEY_READ
, &keyloc
);
299 if (result
== ERROR_SUCCESS
) {
300 DWORD pathlen
= sizeof(path
);
302 result
= ::RegQueryValueEx(keyloc
, "Plugins Directory", NULL
, &type
,
303 (LPBYTE
)&path
, &pathlen
);
304 if (result
== ERROR_SUCCESS
) {
305 rv
= NS_NewNativeLocalFile(nsDependentCString(path
), PR_TRUE
,
306 getter_AddRefs(localFile
));
309 ::RegCloseKey(keyloc
);
312 } else if (nsCRT::strcmp(prop
, NS_WIN_JRE_SCAN_KEY
) == 0) {
313 nsXPIDLCString strVer
;
314 if (NS_FAILED(prefs
->GetCharPref(prop
, getter_Copies(strVer
))))
315 return NS_ERROR_FAILURE
;
317 TranslateVersionStr(strVer
.get(), &minVer
);
319 // Look for the Java OJI plugin via the JRE install path
326 DWORD numChars
= _MAX_PATH
;
329 ClearVersion(&maxVer
);
330 char curKey
[_MAX_PATH
] = "Software\\JavaSoft\\Java Runtime Environment";
331 char path
[_MAX_PATH
];
332 // Add + 15 to prevent buffer overrun when adding \bin (+ optionally
334 #define JAVA_PATH_SIZE _MAX_PATH + 15
335 char newestPath
[JAVA_PATH_SIZE
];
336 const char mozPath
[_MAX_PATH
] = "Software\\mozilla.org\\Mozilla";
337 char browserJavaVersion
[_MAX_PATH
];
338 PRBool tryNPRuntimeJavaPlugIn
=
346 LONG result
= ::RegOpenKeyEx(HKEY_LOCAL_MACHINE
, curKey
, 0, KEY_READ
,
348 if (ERROR_SUCCESS
!= result
)
349 return NS_ERROR_FAILURE
;
351 // Look for "BrowserJavaVersion"
352 if (ERROR_SUCCESS
!= ::RegQueryValueEx(baseloc
, "BrowserJavaVersion", NULL
,
353 NULL
, (LPBYTE
)&browserJavaVersion
,
355 browserJavaVersion
[0] = 0;
357 // We must enumerate through the keys because what if there is
358 // more than one version?
361 numChars
= _MAX_PATH
;
362 pathlen
= sizeof(path
);
363 result
= ::RegEnumKeyEx(baseloc
, index
, curKey
, &numChars
, NULL
, NULL
,
367 // Skip major.minor as it always points to latest in its family
369 for (char *p
= curKey
; *p
; p
++) {
377 if (ERROR_SUCCESS
== result
) {
378 if (ERROR_SUCCESS
== ::RegOpenKeyEx(baseloc
, curKey
, 0,
379 KEY_QUERY_VALUE
, &keyloc
)) {
381 if (ERROR_SUCCESS
== ::RegQueryValueEx(keyloc
, "JavaHome", NULL
,
382 &type
, (LPBYTE
)&path
,
385 TranslateVersionStr(curKey
, &curVer
);
386 if (CompareVersion(curVer
, minVer
) >= 0) {
387 if (!strncmp(browserJavaVersion
, curKey
, _MAX_PATH
)) {
388 PL_strcpy(newestPath
, path
);
390 tryNPRuntimeJavaPlugIn
= TryToUseNPRuntimeJavaPlugIn(curKey
);
392 ::RegCloseKey(keyloc
);
396 if (CompareVersion(curVer
, maxVer
) >= 0) {
397 PL_strcpy(newestPath
, path
);
398 CopyVersion(&maxVer
, &curVer
);
400 tryNPRuntimeJavaPlugIn
= TryToUseNPRuntimeJavaPlugIn(curKey
);
405 ::RegCloseKey(keyloc
);
408 } while (ERROR_SUCCESS
== result
);
410 ::RegCloseKey(baseloc
);
412 // If nothing is found, then don't add \bin dir and don't set
413 // CurrentVersion for Mozilla
414 if (newestPath
[0] != 0) {
415 if (ERROR_SUCCESS
== ::RegCreateKeyEx(HKEY_LOCAL_MACHINE
, mozPath
, 0,
416 NULL
, REG_OPTION_NON_VOLATILE
,
417 KEY_SET_VALUE
|KEY_QUERY_VALUE
,
418 NULL
, &entryloc
, NULL
)) {
419 if (ERROR_SUCCESS
!= ::RegQueryValueEx(entryloc
, "CurrentVersion", 0,
421 ::RegSetValueEx(entryloc
, "CurrentVersion", 0, REG_SZ
,
422 (const BYTE
*)MOZILLA_VERSION
,
423 sizeof(MOZILLA_VERSION
));
425 ::RegCloseKey(entryloc
);
428 PL_strcat(newestPath
,"\\bin");
430 // See whether we should use the new NPRuntime-based Java Plug-In:
431 // - If tryNPRuntimeJavaPlugIn is true, and
432 // - If the appropriate subdirectory actually exists
433 // Note that this is a temporary code path until the old
434 // OJI-based Java Plug-In isn't being shipped alongside the new
436 if (tryNPRuntimeJavaPlugIn
) {
437 // See whether the "new_plugin" directory exists
438 char tmpPath
[JAVA_PATH_SIZE
];
439 PL_strcpy(tmpPath
, newestPath
);
440 PL_strcat(tmpPath
, "\\new_plugin");
441 nsCOMPtr
<nsILocalFile
> tmpFile
;
442 if (NS_SUCCEEDED(NS_NewNativeLocalFile(nsDependentCString(tmpPath
),
444 getter_AddRefs(tmpFile
))) &&
446 PRBool exists
= PR_FALSE
;
447 PRBool isDir
= PR_FALSE
;
448 if (NS_SUCCEEDED(tmpFile
->Exists(&exists
)) && exists
&&
449 NS_SUCCEEDED(tmpFile
->IsDirectory(&isDir
)) && isDir
) {
450 // Assume we're supposed to use this as the search
451 // directory for the Java Plug-In instead of the normal
453 PL_strcpy(newestPath
, tmpPath
);
458 rv
= NS_NewNativeLocalFile(nsDependentCString(newestPath
), PR_TRUE
,
459 getter_AddRefs(localFile
));
461 } else if (nsCRT::strcmp(prop
, NS_WIN_QUICKTIME_SCAN_KEY
) == 0) {
462 nsXPIDLCString strVer
;
463 if (NS_FAILED(prefs
->GetCharPref(prop
, getter_Copies(strVer
))))
464 return NS_ERROR_FAILURE
;
466 TranslateVersionStr(strVer
.get(), &minVer
);
468 // Look for the Quicktime system installation plugins directory
473 ClearVersion(&qtVer
);
474 char path
[_MAX_PATH
];
475 DWORD pathlen
= sizeof(path
);
477 // First we need to check the version of Quicktime via checking
478 // the EXE's version table
479 if (ERROR_SUCCESS
== ::RegOpenKeyEx(HKEY_LOCAL_MACHINE
, "software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\QuickTimePlayer.exe", 0, KEY_READ
, &keyloc
)) {
480 if (ERROR_SUCCESS
== ::RegQueryValueEx(keyloc
, NULL
, NULL
, &type
,
481 (LPBYTE
)&path
, &pathlen
)) {
482 GetFileVersion((char*)path
, &qtVer
);
484 ::RegCloseKey(keyloc
);
486 if (CompareVersion(qtVer
, minVer
) < 0)
489 if (ERROR_SUCCESS
== ::RegOpenKeyEx(HKEY_LOCAL_MACHINE
, "software\\Apple Computer, Inc.\\QuickTime", 0, KEY_READ
, &keyloc
)) {
490 DWORD pathlen
= sizeof(path
);
492 result
= ::RegQueryValueEx(keyloc
, "InstallDir", NULL
, &type
,
493 (LPBYTE
)&path
, &pathlen
);
494 PL_strcat(path
, "\\Plugins");
495 if (result
== ERROR_SUCCESS
)
496 rv
= NS_NewNativeLocalFile(nsDependentCString(path
), PR_TRUE
,
497 getter_AddRefs(localFile
));
498 ::RegCloseKey(keyloc
);
500 } else if (nsCRT::strcmp(prop
, NS_WIN_WMP_SCAN_KEY
) == 0) {
501 nsXPIDLCString strVer
;
502 if (NS_FAILED(prefs
->GetCharPref(prop
, getter_Copies(strVer
))))
503 return NS_ERROR_FAILURE
;
505 TranslateVersionStr(strVer
.get(), &minVer
);
507 // Look for Windows Media Player system installation plugins directory
511 ClearVersion(&wmpVer
);
512 char path
[_MAX_PATH
];
513 DWORD pathlen
= sizeof(path
);
515 // First we need to check the version of WMP
516 if (ERROR_SUCCESS
== ::RegOpenKeyEx(HKEY_LOCAL_MACHINE
, "software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\wmplayer.exe", 0, KEY_READ
, &keyloc
)) {
517 if (ERROR_SUCCESS
== ::RegQueryValueEx(keyloc
, NULL
, NULL
, &type
,
518 (LPBYTE
)&path
, &pathlen
)) {
519 GetFileVersion((char*)path
, &wmpVer
);
521 ::RegCloseKey(keyloc
);
523 if (CompareVersion(wmpVer
, minVer
) < 0)
526 if (ERROR_SUCCESS
== ::RegOpenKeyEx(HKEY_LOCAL_MACHINE
,
527 "software\\Microsoft\\MediaPlayer", 0,
528 KEY_READ
, &keyloc
)) {
529 if (ERROR_SUCCESS
== ::RegQueryValueEx(keyloc
, "Installation Directory",
530 NULL
, &type
, (LPBYTE
)&path
,
532 rv
= NS_NewNativeLocalFile(nsDependentCString(path
), PR_TRUE
,
533 getter_AddRefs(localFile
));
536 ::RegCloseKey(keyloc
);
538 } else if (nsCRT::strcmp(prop
, NS_WIN_ACROBAT_SCAN_KEY
) == 0) {
539 nsXPIDLCString strVer
;
540 if (NS_FAILED(prefs
->GetCharPref(prop
, getter_Copies(strVer
)))) {
541 return NS_ERROR_FAILURE
;
545 TranslateVersionStr(strVer
.get(), &minVer
);
547 // Look for Adobe Acrobat system installation plugins directory
553 DWORD numChars
= _MAX_PATH
;
556 ClearVersion(&maxVer
);
557 char curKey
[_MAX_PATH
] = "software\\Adobe\\Acrobat Reader";
558 char path
[_MAX_PATH
];
559 // Add + 8 to prevent buffer overrun when adding \browser
560 char newestPath
[_MAX_PATH
+ 8];
563 if (ERROR_SUCCESS
!= ::RegOpenKeyEx(HKEY_LOCAL_MACHINE
, curKey
, 0,
564 KEY_READ
, &baseloc
)) {
565 PL_strcpy(curKey
, "software\\Adobe\\Adobe Acrobat");
566 if (ERROR_SUCCESS
!= ::RegOpenKeyEx(HKEY_LOCAL_MACHINE
, curKey
, 0,
567 KEY_READ
, &baseloc
)) {
568 return NS_ERROR_FAILURE
;
572 // We must enumerate through the keys because what if there is
573 // more than one version?
574 LONG result
= ERROR_SUCCESS
;
575 while (ERROR_SUCCESS
== result
) {
577 numChars
= _MAX_PATH
;
578 pathlen
= sizeof(path
);
579 result
= ::RegEnumKeyEx(baseloc
, index
, curKey
, &numChars
, NULL
, NULL
,
583 if (ERROR_SUCCESS
== result
) {
585 TranslateVersionStr(curKey
, &curVer
);
586 PL_strcat(curKey
, "\\InstallPath");
587 if (ERROR_SUCCESS
== ::RegOpenKeyEx(baseloc
, curKey
, 0,
588 KEY_QUERY_VALUE
, &keyloc
)) {
590 if (ERROR_SUCCESS
== ::RegQueryValueEx(keyloc
, NULL
, NULL
, &type
,
591 (LPBYTE
)&path
, &pathlen
)) {
592 if (CompareVersion(curVer
, maxVer
) >= 0 &&
593 CompareVersion(curVer
, minVer
) >= 0) {
594 PL_strcpy(newestPath
, path
);
595 CopyVersion(&maxVer
, &curVer
);
599 ::RegCloseKey(keyloc
);
604 ::RegCloseKey(baseloc
);
606 if (newestPath
[0] != 0) {
607 PL_strcat(newestPath
,"\\browser");
608 rv
= NS_NewNativeLocalFile(nsDependentCString(newestPath
), PR_TRUE
,
609 getter_AddRefs(localFile
));
614 if (localFile
&& NS_SUCCEEDED(rv
))
615 return CallQueryInterface(localFile
, _retval
);
621 nsPluginDirServiceProvider::GetPLIDDirectories(nsISimpleEnumerator
**aEnumerator
)
623 NS_ENSURE_ARG_POINTER(aEnumerator
);
624 *aEnumerator
= nsnull
;
626 nsCOMArray
<nsILocalFile
> dirs
;
628 GetPLIDDirectoriesWithHKEY(HKEY_CURRENT_USER
, dirs
);
629 GetPLIDDirectoriesWithHKEY(HKEY_LOCAL_MACHINE
, dirs
);
631 return NS_NewArrayEnumerator(aEnumerator
, dirs
);
635 nsPluginDirServiceProvider::GetPLIDDirectoriesWithHKEY(HKEY aKey
, nsCOMArray
<nsILocalFile
> &aDirs
)
637 char subkey
[_MAX_PATH
] = "Software\\MozillaPlugins";
640 if (ERROR_SUCCESS
!= ::RegOpenKeyEx(aKey
, subkey
, 0, KEY_READ
, &baseloc
))
641 return NS_ERROR_FAILURE
;
644 DWORD subkeylen
= _MAX_PATH
;
646 while (ERROR_SUCCESS
== ::RegEnumKeyEx(baseloc
, index
++, subkey
, &subkeylen
,
647 NULL
, NULL
, NULL
, &modTime
)) {
648 subkeylen
= _MAX_PATH
;
651 if (ERROR_SUCCESS
== ::RegOpenKeyEx(baseloc
, subkey
, 0, KEY_QUERY_VALUE
,
654 char path
[_MAX_PATH
];
655 DWORD pathlen
= sizeof(path
);
657 if (ERROR_SUCCESS
== ::RegQueryValueEx(keyloc
, "Path", NULL
, &type
,
658 (LPBYTE
)&path
, &pathlen
)) {
659 nsCOMPtr
<nsILocalFile
> localFile
;
660 if (NS_SUCCEEDED(NS_NewNativeLocalFile(nsDependentCString(path
),
662 getter_AddRefs(localFile
))) &&
664 // Some vendors use a path directly to the DLL so chop off
666 PRBool isDir
= PR_FALSE
;
667 if (NS_SUCCEEDED(localFile
->IsDirectory(&isDir
)) && !isDir
) {
668 nsCOMPtr
<nsIFile
> temp
;
669 localFile
->GetParent(getter_AddRefs(temp
));
671 localFile
= do_QueryInterface(temp
);
674 // Now we check to make sure it's actually on disk and
675 // To see if we already have this directory in the array
676 PRBool isFileThere
= PR_FALSE
;
677 PRBool isDupEntry
= PR_FALSE
;
678 if (NS_SUCCEEDED(localFile
->Exists(&isFileThere
)) && isFileThere
) {
679 PRInt32 c
= aDirs
.Count();
680 for (PRInt32 i
= 0; i
< c
; i
++) {
681 nsIFile
*dup
= static_cast<nsIFile
*>(aDirs
[i
]);
683 NS_SUCCEEDED(dup
->Equals(localFile
, &isDupEntry
)) &&
690 aDirs
.AppendObject(localFile
);
695 ::RegCloseKey(keyloc
);
698 ::RegCloseKey(baseloc
);