DEVEL15-windows-explorer-shell-symlinks-show-20080112
authorJeffrey Altman <jaltman@secure-endpoints.com>
Sat, 12 Jan 2008 23:31:24 +0000 (23:31 +0000)
committerJeffrey Altman <jaltman@secure-endpoints.com>
Sat, 12 Jan 2008 23:31:24 +0000 (23:31 +0000)
LICENSE MIT

Add AFS->Symlnks->Show dialog to the explorer shell menu

(cherry picked from commit 7a40c73476e270aac9d76663ff71e956bb630328)

src/WINNT/client_exp/NTMakefile
src/WINNT/client_exp/gui2fs.cpp
src/WINNT/client_exp/gui2fs.h
src/WINNT/client_exp/help.h
src/WINNT/client_exp/lang/en_US/afs_shl_ext.rc
src/WINNT/client_exp/resource.h
src/WINNT/client_exp/shell_ext.cpp
src/WINNT/client_exp/symlinks_dlg.cpp [new file with mode: 0644]
src/WINNT/client_exp/symlinks_dlg.h [new file with mode: 0644]

index 55c7911..0ad2f6c 100644 (file)
@@ -44,6 +44,7 @@ DLLOBJS =\
        $(OUT)\unlog_dlg.obj \
        $(OUT)\volumeinfo.obj \
        $(OUT)\make_symbolic_link_dlg.obj \
+        $(OUT)\symlinks_dlg.obj \
        $(OUT)\AFS_component_version_number.obj
 
 AFSD = ..\afsd
index aa50116..f5b9846 100644 (file)
@@ -21,6 +21,7 @@ extern "C" {
 #include "results_dlg.h"
 #include "volume_inf.h"
 #include "mount_points_dlg.h"
+#include "symlinks_dlg.h"
 #include "hourglass.h"
 #include "down_servers_dlg.h"
 
@@ -992,6 +993,15 @@ CString ParseMountPoint(const CString strFile, CString strMountPoint)
     return strMountPointInfo;
 }       
 
+CString ParseSymlink(const CString strFile, CString strSymlink)
+{
+    CString strSymlinkInfo;
+
+    strSymlinkInfo = strFile + "\t" + strSymlink;
+
+    return strSymlinkInfo;
+}       
+
 BOOL IsPathInAfs(const CHAR *strPath)
 {
     struct ViceIoctl blob;
@@ -999,13 +1009,23 @@ BOOL IsPathInAfs(const CHAR *strPath)
 
     HOURGLASS hourglass;
 
+    char buf[512];
+    sprintf(buf, "IsPathInAfs(%s)", strPath);
+    OutputDebugString(buf);
+
     blob.in_size = 0;
     blob.out_size = MAXSIZE;
     blob.out = space;
 
     code = pioctl((LPTSTR)((LPCTSTR)strPath), VIOC_FILE_CELL_NAME, &blob, 1);
-    if (code)
+
+    sprintf(buf, "VIOC_FILE_CELL_NAME=%d", code);
+    OutputDebugString(buf);
+
+    if (code) {
+       if ((errno == EINVAL) || (errno == ENOENT))
         return FALSE;
+    }
     return TRUE;
 }
 
@@ -1514,6 +1534,10 @@ BOOL IsSymlink(const char * true_name)
 
     HOURGLASS hourglass;
 
+    char buf[512];
+    sprintf(buf, "IsSymlink(%s)", true_name);
+    OutputDebugString(buf);
+
     last_component = (char *) strrchr(true_name, '\\');
     if (!last_component)
         last_component = (char *) strrchr(true_name, '/');
@@ -1550,6 +1574,9 @@ BOOL IsSymlink(const char * true_name)
         fs_StripDriveLetter(true_name, strip_name, sizeof(strip_name));
     }
 
+    sprintf(buf, "last_component=%s", last_component);
+    OutputDebugString(buf);
+
     blob.in = last_component;
     blob.in_size = strlen(last_component)+1;
     blob.out_size = MAXSIZE;
@@ -1569,8 +1596,14 @@ BOOL IsMountPoint(const char * name)
     register char *tp;
     char szCurItem[1024];
 
+    HOURGLASS hourglass;
+
     strcpy(szCurItem, name);
        
+    char buf[512];
+    sprintf(buf, "IsMountPoint(%s)", name);
+    OutputDebugString(buf);
+
     tp = (char *)strrchr(szCurItem, '\\');
     if (tp) {
         strncpy(tbuffer, szCurItem, code = tp - szCurItem + 1);  /* the dir name */
@@ -1596,6 +1629,9 @@ BOOL IsMountPoint(const char * name)
         fs_StripDriveLetter(tp, tp, 0);
     }
 
+    sprintf(buf, "last_component=%s", tp);
+    OutputDebugString(buf);
+
     blob.in = tp;
     blob.in_size = strlen(tp)+1;
     blob.out = lsbuffer;
@@ -2082,3 +2118,89 @@ void ListSymbolicLinkPath(const char *strName,char *strPath,UINT nlenPath)
     ASSERT(strlen(space)<MAX_PATH);
     strncpy(strPath,space,nlenPath);
 }       
+
+BOOL ListSymlink(CStringArray& files)
+{
+    register LONG code;
+    struct ViceIoctl blob;
+    int error;
+    char orig_name[1024];                      /* Original name, may be modified */
+    char true_name[1024];                      /* ``True'' dirname (e.g., symlink target) */
+    char parent_dir[1024];                     /* Parent directory of true name */
+    register char *last_component;     /* Last component of true name */
+    CStringArray symlinks;
+    
+    HOURGLASS hourglass;
+
+    error = 0;
+
+    for (int i = 0; i < files.GetSize(); i++) {
+        strcpy(orig_name, files[i]);
+        strcpy(true_name, orig_name);
+
+        /*
+         * Find rightmost slash, if any.
+         */
+        last_component = (char *)strrchr(true_name, '\\');
+        if (last_component) {
+            /*
+             * Found it.  Designate everything before it as the parent directory,
+             * everything after it as the final component.
+             */
+            strncpy(parent_dir, true_name, last_component - true_name + 1);
+            parent_dir[last_component - true_name + 1] = 0;
+            last_component++;   /* Skip the slash */
+
+           if (!IsPathInAfs(parent_dir)) {
+               const char * nbname = NetbiosName();
+               int len = strlen(nbname);
+
+               if (parent_dir[0] == '\\' && parent_dir[1] == '\\' &&
+                   parent_dir[len+2] == '\\' &&
+                   parent_dir[len+3] == '\0' &&
+                   !strnicmp(nbname,&parent_dir[2],len))
+               {
+                   sprintf(parent_dir,"\\\\%s\\all\\", nbname);
+               }
+           }
+        }
+        else {
+            /*
+             * No slash appears in the given file name.  Set parent_dir to the current
+             * directory, and the last component as the given name.
+             */
+            fs_ExtractDriveLetter(true_name, parent_dir);
+            strcat(parent_dir, ".");
+            last_component = true_name;
+            fs_StripDriveLetter(true_name, true_name, sizeof(true_name));
+        }
+
+        blob.in = last_component;
+        blob.in_size = strlen(last_component) + 1;
+        blob.out_size = MAXSIZE;
+        blob.out = space;
+        memset(space, 0, MAXSIZE);
+
+        code = pioctl(parent_dir, VIOC_LISTSYMLINK, &blob, 1);
+        if (code == 0) {
+            int nPos = strlen(space) - 1;
+            if (space[nPos] == '.')
+                space[nPos] = 0;
+            symlinks.Add(ParseSymlink(StripPath(files[i]), space));
+        } else {
+            error = 1;
+            if (errno == EINVAL)
+                symlinks.Add(GetMessageString(IDS_NOT_SYMLINK_ERROR, StripPath(files[i])));
+            else
+                symlinks.Add(GetMessageString(IDS_LIST_MOUNT_POINT_ERROR, GetAfsError(errno, StripPath(files[i]))));
+        }
+    }
+
+    CSymlinksDlg dlg;
+    dlg.SetSymlinks(symlinks);
+    dlg.DoModal();
+
+    return !error;
+}
+
+
index 5a5138e..dccd1f3 100644 (file)
@@ -42,5 +42,6 @@ BOOL IsSymlink(const char * true_name);
 BOOL IsMountPoint(const char * name);
 UINT MakeSymbolicLink(const char *,const char *);
 void ListSymbolicLinkPath(const char *strName,char *strPath,UINT nlenPath);
+BOOL ListSymlink(CStringArray& files);
 const char * NetbiosName(void);
 #endif //__GUI2FS_H__
index 507d03d..0dcab65 100644 (file)
@@ -32,6 +32,7 @@
 #define SUBMOUNTS_HELP_ID                      44
 #define ADD_SUBMT_HELP_ID                      45
 #define EDIT_PATH_NAME_HELP_ID         46
+#define SYMLINK_HELP_ID                 47
 
 void SetHelpPath(const char *pszDefaultHelpFilePath);
 void ShowHelp(HWND hWnd, DWORD nHelpID);
index 53874cd..8c90943 100644 (file)
@@ -318,12 +318,26 @@ BEGIN
     LISTBOX         IDC_LIST,10,20,280,115,LBS_SORT | LBS_USETABSTOPS | 
                     LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | 
                     WS_TABSTOP
-    LTEXT           "File",IDC_STATIC,10,11,13,8
+    LTEXT           "Name",IDC_STATIC,10,11,20,8
     LTEXT           "Volume",IDC_STATIC,75,11,27,8
     LTEXT           "Cell",IDC_STATIC,155,11,15,8
     LTEXT           "Type",IDC_STATIC,230,11,20,8
 END
 
+IDD_SYMLINKS DIALOG DISCARDABLE  0, 0, 299, 168
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Symlinks"
+FONT 8, "MS Sans Serif"
+BEGIN
+    DEFPUSHBUTTON   "Close",IDOK,65,145,50,14
+    PUSHBUTTON      "Help",9,180,145,50,14
+    LISTBOX         IDC_LIST,10,20,280,115,LBS_SORT | LBS_USETABSTOPS | 
+                    LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | 
+                    WS_TABSTOP
+    LTEXT           "Name",IDC_STATIC,10,11,20,8
+    LTEXT           "Target",IDC_STATIC,75,11,27,8
+END
+
 IDD_DOWN_SERVERS DIALOG DISCARDABLE  0, 0, 209, 163
 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
 CAPTION "Down Servers"
@@ -650,7 +664,7 @@ END
 STRINGTABLE DISCARDABLE 
 BEGIN
     IDS_SYMBOLICLINK_ADD                  "&Add"
-    IDS_SYMBOLICLINK_EDIT                 "&Edit..."
+    IDS_SYMBOLICLINK_SHOW                 "&Show"
     IDS_SYMBOLICLINK_REMOVE               "&Remove"
     IDS_SYMBOLIC_LINK_ITEM                "Symbolic &Link"
     IDS_UNABLE_TO_CREATE_SYMBOLIC_LINK    "Unable to create symbolic link: %o."
@@ -661,6 +675,10 @@ BEGIN
     IDS_NOT_AFS_CLIENT_ADMIN_ERROR        "Must be AFS Client Administrators to modify the root.afs volume."
     IDS_WARNING                                  "Warning"
     IDS_VOLUME_NOT_IN_CELL_WARNING       "Volume %s does not exist in cell %s.\n"
+    IDS_NOT_SYMLINK_ERROR                 "%o is not a symbolic link."
+    ID_SYMBOLICLINK_ADD         "symlink make"
+    ID_SYMBOLICLINK_SHOW        "symlink list"
+    ID_SYMBOLICLINK_REMOVE      "symlink remove"
 END
 
 STRINGTABLE DISCARDABLE 
index 310604c..9a381ca 100644 (file)
 #define IDS_REALLY_REMOVE_SYMLINK              118
 
 #define IDS_SYMBOLICLINK_ADD                   128
-#define IDS_SYMBOLICLINK_EDIT                  129
+#define IDS_SYMBOLICLINK_SHOW                  129
 #define IDS_SYMBOLICLINK_REMOVE                130
 #define IDS_SYMBOLIC_LINK_ITEM                 131
 #define IDS_UNABLE_TO_CREATE_SYMBOLIC_LINK     132
 #define IDS_NOT_AFS_CLIENT_ADMIN_ERROR         137
 #define IDS_WARNING                           138
 #define IDS_VOLUME_NOT_IN_CELL_WARNING         139
+#define IDS_NOT_SYMLINK_ERROR                  140
 
 #define IDM_AUTHENTICATION                    0
 #define IDM_ACL_SET                           1
 #define IDM_ACL_CLEAN                         15
 #define IDM_SUBMOUNTS_EDIT                    16
 #define IDM_REMOVE_SYMLINK                    17
-                                           
+#define IDM_SYMBOLICLINK_SHOW                18
+
 #define ID_GET_TOKENS                   917
 #define ID_DISCARD_TOKENS               918
 #define IDD_KLOG_DIALOG                 920
 #define ID_REMOVE_SYMLINK               957
 #define ID_SYMBOLICLINK_ADD             958
 #define ID_SYMBOLICLINK_REMOVE          959
+#define ID_SYMBOLICLINK_SHOW            960
+#define IDD_SYMLINKS                    961
 #define IDC_LIST                        1001
 #define IDC_PASSWORD                    1002
 #define IDC_OFFLINE_MSG                 1003
index 773156c..c2f6e13 100644 (file)
@@ -183,7 +183,7 @@ STDMETHODIMP_(ULONG) CShellExt::XMenuExt::Release(void)
 // IConextMenu Functions
 /////////////////////////////////////////////////////////////////////////////
 STDMETHODIMP CShellExt::XMenuExt::QueryContextMenu(HMENU hMenu,UINT indexMenu,
-    UINT idCmdFirst, UINT idCmdLast,UINT uFlags)
+                                                  UINT idCmdFirst, UINT idCmdLast,UINT uFlags)
 {
     METHOD_PROLOGUE(CShellExt, MenuExt);
 
@@ -207,7 +207,7 @@ STDMETHODIMP CShellExt::XMenuExt::QueryContextMenu(HMENU hMenu,UINT indexMenu,
            DeleteMenu (hMenu, iItem, MF_BYPOSITION);
            continue;
        }
-       if ((!lstrcmp(szItemText,"Cu&t"))&&(pThis->m_bIsSymlink)) {             /*same for cut*/
+       if ((!lstrcmp(szItemText,"Cu&t"))&&(pThis->m_bIsSymlink)) {     /*same for cut*/
            DeleteMenu (hMenu, iItem, MF_BYPOSITION);
            continue;
        }
@@ -218,11 +218,6 @@ STDMETHODIMP CShellExt::XMenuExt::QueryContextMenu(HMENU hMenu,UINT indexMenu,
     HMENU hAfsMenu = CreatePopupMenu();
     int indexAfsMenu = 0;
 
-    // The Authentication item has been removed from the AFS menu because
-    // there is now a tray icon to handle authentication.
-    //
-    //::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_AUTHENTICATION, GetMessageString(IDS_AUTHENTICATION_ITEM));
-
     // Only enable the ACL menu item if a single directory is selected
     int nSingleDirOnly = MF_GRAYED;
     if (pThis->m_bDirSelected && (pThis->m_astrFileNames.GetSize() == 1))
@@ -259,22 +254,28 @@ STDMETHODIMP CShellExt::XMenuExt::QueryContextMenu(HMENU hMenu,UINT indexMenu,
 
     HMENU hSymbolicMenu = CreatePopupMenu();
     int indexSymbolicMenu = 0;
+    int nSymlinkSelected = MF_GRAYED;
+    for (int n = pThis->m_astrFileNames.GetSize() - 1 ; n >= 0; n--) {
+       if ( IsSymlink(pThis->m_astrFileNames[n]) ) {
+           nSymlinkSelected = MF_ENABLED;
+           break;
+       }
+    }
+
     ::InsertMenu(hSymbolicMenu, indexSymbolicMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SYMBOLICLINK_ADD, GetMessageString(IDS_SYMBOLICLINK_ADD));
-    // ::InsertMenu(hSymbolicMenu, indexSymbolicMenu, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SYMBOLICLINK_EDIT, GetMessageString(IDS_SYMBOLICLINK_EDIT));
-    // ::EnableMenuItem(hSymbolicMenu,indexSymbolicMenu++,((pThis->m_bIsSymlink)?MF_ENABLED:MF_GRAYED)|MF_BYPOSITION);
-    ::InsertMenu(hSymbolicMenu, indexSymbolicMenu, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SYMBOLICLINK_REMOVE, GetMessageString(IDS_SYMBOLICLINK_REMOVE));
-    ::EnableMenuItem(hSymbolicMenu,indexSymbolicMenu++,((pThis->m_bIsSymlink)?MF_ENABLED:MF_GRAYED)|MF_BYPOSITION);
+    ::InsertMenu(hSymbolicMenu, indexSymbolicMenu++, MF_STRING | MF_BYPOSITION | nSymlinkSelected, idCmdFirst + IDM_SYMBOLICLINK_SHOW, GetMessageString(IDS_SYMBOLICLINK_SHOW));
+    ::InsertMenu(hSymbolicMenu, indexSymbolicMenu++, MF_STRING | MF_BYPOSITION | nSymlinkSelected, idCmdFirst + IDM_SYMBOLICLINK_REMOVE, GetMessageString(IDS_SYMBOLICLINK_REMOVE));
     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hSymbolicMenu, GetMessageString(IDS_SYMBOLIC_LINK_ITEM));
 
     // The Submounts menu has been removed because the AFS tray icon
     // and control panel now support mapping drives directly to an AFS
     // path.
     //
-    // HMENU hSubmountMenu = CreatePopupMenu();
-    // int indexSubmountMenu = 0;
-    // ::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION | nSingleDirOnly, idCmdFirst + IDM_SUBMOUNTS_CREATE, GetMessageString(IDS_SUBMOUNTS_CREATE_ITEM));
-    // ::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SUBMOUNTS_EDIT, GetMessageString(IDS_SUBMOUNTS_EDIT_ITEM));
-    // ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hSubmountMenu, GetMessageString(IDS_SUBMOUNTS_ITEM));
+    //HMENU hSubmountMenu = CreatePopupMenu();
+    //int indexSubmountMenu = 0;
+    //::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION | nSingleDirOnly, idCmdFirst + IDM_SUBMOUNTS_CREATE, GetMessageString(IDS_SUBMOUNTS_CREATE_ITEM));
+    //::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SUBMOUNTS_EDIT, GetMessageString(IDS_SUBMOUNTS_EDIT_ITEM));
+    //::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hSubmountMenu, GetMessageString(IDS_SUBMOUNTS_ITEM));
 
     // Add a separator
     ::InsertMenu (hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_SEPARATOR, 0, TEXT(""));
@@ -418,7 +419,11 @@ STDMETHODIMP CShellExt::XMenuExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
        dlg.DoModal();
        break;
     }
-               
+
+    case IDM_SYMBOLICLINK_SHOW:        
+       ListSymlink(files);
+       break;
+
     case IDM_REMOVE_SYMLINK:   {
        if (files.GetSize()>1)
            break;
@@ -502,6 +507,10 @@ STDMETHODIMP CShellExt::XMenuExt::GetCommandString(UINT_PTR idCmd, UINT uType,
        nCmdStrID = ID_SYMBOLICLINK_ADD;
        break;
                
+    case IDM_SYMBOLICLINK_SHOW:
+       nCmdStrID = ID_SYMBOLICLINK_SHOW;
+       break;
+
     case IDM_SYMBOLICLINK_REMOVE: 
        nCmdStrID = ID_SYMBOLICLINK_REMOVE;
        break;
diff --git a/src/WINNT/client_exp/symlinks_dlg.cpp b/src/WINNT/client_exp/symlinks_dlg.cpp
new file mode 100644 (file)
index 0000000..efde63e
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+extern "C" {
+#include <afs/param.h>
+#include <afs/stds.h>
+}
+
+#include "stdafx.h"
+#include "afs_shl_ext.h"
+#include "symlinks_dlg.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// CSymlinksDlg dialog
+
+
+CSymlinksDlg::CSymlinksDlg(CWnd* pParent /*=NULL*/)
+       : CDialog()
+{
+       InitModalIndirect (TaLocale_GetDialogResource (CSymlinksDlg::IDD), pParent);
+
+       //{{AFX_DATA_INIT(CSymlinksDlg)
+               // NOTE: the ClassWizard will add member initialization here
+       //}}AFX_DATA_INIT
+}
+
+
+void CSymlinksDlg::DoDataExchange(CDataExchange* pDX)
+{
+       CDialog::DoDataExchange(pDX);
+       //{{AFX_DATA_MAP(CSymlinksDlg)
+       DDX_Control(pDX, IDC_LIST, m_List);
+       //}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CSymlinksDlg, CDialog)
+       //{{AFX_MSG_MAP(CSymlinksDlg)
+       ON_BN_CLICKED(IDHELP, OnHelp)
+       //}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+/////////////////////////////////////////////////////////////////////////////
+// CSymlinksDlg message handlers
+
+BOOL CSymlinksDlg::OnInitDialog() 
+{
+       CDialog::OnInitDialog();
+
+       int tabs[] = { 64 };
+       
+       m_List.SetTabStops(sizeof(tabs) / sizeof(int), tabs);
+
+       for (int i = 0; i < m_Symlinks.GetSize(); i++)
+               m_List.AddString(m_Symlinks[i]);
+
+       return TRUE;  // return TRUE unless you set the focus to a control
+                     // EXCEPTION: OCX Property Pages should return FALSE
+}
+
+void CSymlinksDlg::SetSymlinks(const CStringArray& symlinks)
+{
+       m_Symlinks.RemoveAll();
+
+       m_Symlinks.Copy(symlinks);
+}
+
+void CSymlinksDlg::OnHelp() 
+{
+       ShowHelp(m_hWnd, SYMLINK_HELP_ID);
+}
+
diff --git a/src/WINNT/client_exp/symlinks_dlg.h b/src/WINNT/client_exp/symlinks_dlg.h
new file mode 100644 (file)
index 0000000..d8b8b60
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2000, International Business Machines Corporation and others.
+ * All Rights Reserved.
+ * 
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ */
+
+class CSymlinksDlg : public CDialog
+{
+       CStringArray m_Symlinks;
+       
+// Construction
+public:
+       CSymlinksDlg(CWnd* pParent = NULL);   // standard constructor
+
+       void SetSymlinks(const CStringArray& mountPoints);
+
+// Dialog Data
+       //{{AFX_DATA(CSymlinksDlg)
+       enum { IDD = IDD_SYMLINKS };
+       CListBox        m_List;
+       //}}AFX_DATA
+
+
+// Overrides
+       // ClassWizard generated virtual function overrides
+       //{{AFX_VIRTUAL(CSymlinksDlg)
+       protected:
+       virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+       //}}AFX_VIRTUAL
+
+// Implementation
+protected:
+
+       // Generated message map functions
+       //{{AFX_MSG(CSymlinksDlg)
+       virtual BOOL OnInitDialog();
+       afx_msg void OnHelp();
+       //}}AFX_MSG
+       DECLARE_MESSAGE_MAP()
+};