Windows: afs_shl_ext Show icon mount point overlay
[openafs.git] / src / WINNT / client_exp / shell_ext.cpp
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afxpriv.h>
11 #include "stdafx.h"
12 #include <winsock2.h>
13 #include <ws2tcpip.h>
14 #include <shtypes.h>
15 #include <shlwapi.h>
16
17 extern "C" {
18 #include <afs/param.h>
19 #include <afs/stds.h>
20 #include <afs/afs_consts.h>
21 }
22
23 #include <atlconv.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include "afs_shl_ext.h"
27 #include "shell_ext.h"
28 #include "volume_info.h"
29 #include "set_afs_acl.h"
30 #include "gui2fs.h"
31 #include "make_mount_point_dlg.h"
32 #include "msgs.h"
33 #include "server_status_dlg.h"
34 #include "auth_dlg.h"
35 #include "submounts_dlg.h"
36 #include "make_symbolic_link_dlg.h"
37 #include <WINNT\afsreg.h>
38
39 #ifdef _DEBUG
40 #define new DEBUG_NEW
41 #undef THIS_FILE
42 static char THIS_FILE[] = __FILE__;
43 #endif
44
45
46 ULONG nCMRefCount = 0;  // IContextMenu ref count
47 ULONG nSERefCount = 0;  // IShellExtInit ref count
48 ULONG nICRefCount=0;
49 ULONG nTPRefCount=0;
50 ULONG nXPRefCount=0;
51
52 #define PCCHAR(str)     ((char *)(const char *)str)
53 static char space[AFS_PIOCTL_MAXSIZE];
54
55 static BOOL IsADir(const CString& strName)
56 {
57     struct _stat statbuf;
58
59     if (_tstat(strName, &statbuf) < 0)
60         return FALSE;
61
62     return statbuf.st_mode & _S_IFDIR;
63 }
64
65 /////////////////////////////////////////////////////////////////////////////
66 // CShellExt
67
68 IMPLEMENT_DYNCREATE(CShellExt, CCmdTarget)
69 #define REG_CLIENT_PARMS_KEY    "SYSTEM\\CurrentControlSet\\Services\\TransarcAFSDaemon\\Parameters"
70 #define OVERLAYENABLED 1
71
72 CShellExt::CShellExt()
73 {
74     HKEY NPKey;
75     EnableAutomation();
76     nCMRefCount++;
77     HRESULT hr;
78     UINT code;
79     DWORD ShellOption,LSPsize,LSPtype;
80     hr = SHGetMalloc(&m_pAlloc);
81     m_bIsOverlayEnabled=FALSE;
82     if (FAILED(hr))
83         m_pAlloc = NULL;
84     RegOpenKeyEx(HKEY_LOCAL_MACHINE, AFSREG_CLT_SVC_PARAM_SUBKEY,0, (IsWow64()?KEY_WOW64_64KEY:0)|KEY_QUERY_VALUE, &NPKey);
85     LSPsize=sizeof(ShellOption);
86     code=RegQueryValueEx(NPKey, _T("ShellOption"), NULL,
87                           &LSPtype, (LPBYTE)&ShellOption, &LSPsize);
88     RegCloseKey (NPKey);
89     m_bIsOverlayEnabled=((code==0) && (LSPtype==REG_DWORD) && ((ShellOption & OVERLAYENABLED)!=0));
90     TRACE("Create CShellExt, Ref count %d/n",nCMRefCount);
91 }
92
93 CShellExt::~CShellExt()
94 {
95     if(m_pAlloc) 
96         m_pAlloc->Release();
97     nCMRefCount--;
98     TRACE("Destroy CShellExt, Ref count %d/n",nCMRefCount);
99 }
100
101
102 void CShellExt::OnFinalRelease()
103 {
104     // When the last reference for an automation object is released
105     // OnFinalRelease is called.  The base class will automatically
106     // deletes the object.  Add additional cleanup required for your
107     // object before calling the base class.
108
109     CCmdTarget::OnFinalRelease();
110 }       
111
112
113 BEGIN_MESSAGE_MAP(CShellExt, CCmdTarget)
114     //{{AFX_MSG_MAP(CShellExt)
115     // NOTE - the ClassWizard will add and remove mapping macros here.
116     //}}AFX_MSG_MAP
117 END_MESSAGE_MAP()
118
119 BEGIN_DISPATCH_MAP(CShellExt, CCmdTarget)
120     //{{AFX_DISPATCH_MAP(CShellExt)
121     // NOTE - the ClassWizard will add and remove mapping macros here.
122     //}}AFX_DISPATCH_MAP
123 END_DISPATCH_MAP()
124
125 // Note: we add support for IID_IShellExt to support typesafe binding
126 //  from VBA.  This IID must match the GUID that is attached to the 
127 //  dispinterface in the .ODL file.
128
129 #ifndef _WIN64
130 // 32-bit
131 // {DC515C27-6CAC-11D1-BAE7-00C04FD140D2}
132 static const IID IID_IShellExt =
133 { 0xdc515c27, 0x6cac, 0x11d1, { 0xba, 0xe7, 0x0, 0xc0, 0x4f, 0xd1, 0x40, 0xd2 } };
134 #else
135 // 64-bit
136 // {5f820ca1-3dde-11db-b2ce-001558092db5}
137 static const IID IID_IShellExt =
138 { 0x5f820ca1, 0x3dde, 0x11db, {0xb2, 0xce, 0x00, 0x15, 0x58, 0x09, 0x2d, 0xb5 } };
139 #endif
140
141 BEGIN_INTERFACE_MAP(CShellExt, CCmdTarget)
142     INTERFACE_PART(CShellExt, IID_IShellExt, Dispatch)
143     INTERFACE_PART(CShellExt, IID_IContextMenu, MenuExt)
144     INTERFACE_PART(CShellExt, IID_IShellExtInit, ShellInit)
145     INTERFACE_PART(CShellExt, IID_IShellIconOverlayIdentifier, IconExt)
146     INTERFACE_PART(CShellExt, IID_IQueryInfo , ToolTipExt)
147     INTERFACE_PART(CShellExt, IID_IPersistFile , PersistFileExt)
148 END_INTERFACE_MAP()
149
150 #ifndef _WIN64
151     // 32-bit
152 IMPLEMENT_OLECREATE(CShellExt, STR_EXT_TITLE, 0xdc515c27, 0x6cac, 0x11d1, 0xba, 0xe7, 0x0, 0xc0, 0x4f, 0xd1, 0x40, 0xd2)
153 #else
154     // 64-bit
155 IMPLEMENT_OLECREATE(CShellExt, STR_EXT_TITLE, 0x5f820ca1, 0x3dde, 0x11db, 0xb2, 0xce, 0x0, 0x15, 0x58, 0x09, 0x2d, 0xb5)
156 #endif
157
158
159 /////////////////////////////////////////////////////////////////////////////
160 // CShellExt message handlers
161 /////////////////////////////////////////////////////////////////////////////
162
163
164 /////////////////////////////////////////////////////////////////////////////
165 // IUnknown for IContextMenu
166 /////////////////////////////////////////////////////////////////////////////
167 STDMETHODIMP CShellExt::XMenuExt::QueryInterface(REFIID riid, void** ppv)
168 {
169     METHOD_PROLOGUE(CShellExt, MenuExt);
170
171     return pThis->ExternalQueryInterface(&riid, ppv);
172 }
173
174 STDMETHODIMP_(ULONG) CShellExt::XMenuExt::AddRef(void)
175 {
176     return ++nCMRefCount;
177 }
178
179 STDMETHODIMP_(ULONG) CShellExt::XMenuExt::Release(void)
180 {
181     if (nCMRefCount > 0)
182         nCMRefCount--;
183
184     return nCMRefCount;
185 }
186
187 /////////////////////////////////////////////////////////////////////////////
188 // IConextMenu Functions
189 /////////////////////////////////////////////////////////////////////////////
190 STDMETHODIMP CShellExt::XMenuExt::QueryContextMenu(HMENU hMenu,UINT indexMenu,
191                                                    UINT idCmdFirst, UINT idCmdLast,UINT uFlags)
192 {
193     METHOD_PROLOGUE(CShellExt, MenuExt);
194
195     // Don't add any menu items if we're being asked to deal with this file as a shortcut.
196     if (uFlags & CMF_VERBSONLY)
197         return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, (USHORT)0);
198
199     // Check to see if there's already an AFS menu here; if so, remove it
200     int nItemsNow = GetMenuItemCount (hMenu);
201     CString strAfsItemText = GetMessageString(IDS_AFS_ITEM);
202     LPCTSTR pszAfsItemText = (LPCTSTR)strAfsItemText;
203     for (int iItem = 0; iItem < nItemsNow; iItem++) {
204         TCHAR szItemText[256];
205         if (!GetMenuString (hMenu, iItem, szItemText, 256, MF_BYPOSITION))
206             continue;
207         if (!lstrcmp (szItemText, pszAfsItemText)) {
208             DeleteMenu (hMenu, iItem, MF_BYPOSITION);
209             continue;
210         }
211         if ((!lstrcmp(szItemText,_T("&Delete")))&&(pThis->m_bIsSymlink)) {      /*this is a symlink - don't present a delete menu!*/
212             DeleteMenu (hMenu, iItem, MF_BYPOSITION);
213             continue;
214         }
215         if ((!lstrcmp(szItemText,_T("Cu&t")))&&(pThis->m_bIsSymlink)) { /*same for cut*/
216             DeleteMenu (hMenu, iItem, MF_BYPOSITION);
217             continue;
218         }
219     }
220     int indexShellMenu = 0;
221
222     // Create the AFS submenu using the allowed ID's.
223     HMENU hAfsMenu = CreatePopupMenu();
224     int indexAfsMenu = 0;
225
226     // Only enable the ACL menu item if a single directory is selected
227     int nSingleDirOnly = MF_GRAYED;
228     if (pThis->m_bDirSelected && (pThis->m_astrFileNames.GetSize() == 1))
229         nSingleDirOnly = MF_ENABLED;
230     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | nSingleDirOnly, idCmdFirst + IDM_ACL_SET, GetMessageString(IDS_ACLS_ITEM));
231
232     // Volume/Partition submenu of the AFS submenu
233     HMENU hVolPartMenu = CreatePopupMenu();
234     int indexVolPartMenu = 0;
235     ::InsertMenu(hVolPartMenu, indexVolPartMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_VOLUME_PROPERTIES, GetMessageString(IDS_VOL_PART_PROPS_ITEM));
236     ::InsertMenu(hVolPartMenu, indexVolPartMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE, GetMessageString(IDS_VOL_PART_REFRESH_ITEM));
237     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hVolPartMenu, GetMessageString(IDS_VOL_PART_ITEM));
238
239     // Mount Point submenu of the AFS submenu
240     HMENU hMountPointMenu = CreatePopupMenu();
241     int indexMountPointMenu = 0;
242     int nMountPointSelected = MF_GRAYED;
243     for (int n = pThis->m_astrFileNames.GetSize() - 1 ; n >= 0; n--) {
244         if ( IsMountPoint(pThis->m_astrFileNames[n]) ) {
245             nMountPointSelected = MF_ENABLED;
246             break;
247         }
248     }
249     ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_MOUNTPOINT_SHOW, GetMessageString(IDS_MP_SHOW_ITEM));
250     ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION | nMountPointSelected, idCmdFirst + IDM_MOUNTPOINT_REMOVE, GetMessageString(IDS_MP_REMOVE_ITEM));
251     ::InsertMenu(hMountPointMenu, indexMountPointMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_MOUNTPOINT_MAKE, GetMessageString(IDS_MP_MAKE_ITEM));
252     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hMountPointMenu, GetMessageString(IDS_MOUNT_POINT_ITEM));
253
254     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_FLUSH, GetMessageString(IDS_FLUSH_FILE_DIR_ITEM));       
255     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_FLUSH_VOLUME, GetMessageString(IDS_FLUSH_VOLUME_ITEM));
256     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SHOW_SERVER, GetMessageString(IDS_SHOW_FILE_SERVERS_ITEM));
257     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SHOWCELL, GetMessageString(IDS_SHOW_CELL_ITEM));
258     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SERVER_STATUS, GetMessageString(IDS_SHOW_SERVER_STATUS_ITEM));
259
260     HMENU hSymbolicMenu = CreatePopupMenu();
261     int indexSymbolicMenu = 0;
262     int nSymlinkSelected = MF_GRAYED;
263     for (int n = pThis->m_astrFileNames.GetSize() - 1 ; n >= 0; n--) {
264         if ( IsSymlink(pThis->m_astrFileNames[n]) ) {
265             nSymlinkSelected = MF_ENABLED;
266             break;
267         }
268     }
269
270     ::InsertMenu(hSymbolicMenu, indexSymbolicMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SYMBOLICLINK_ADD, GetMessageString(IDS_SYMBOLICLINK_ADD));
271     ::InsertMenu(hSymbolicMenu, indexSymbolicMenu++, MF_STRING | MF_BYPOSITION | nSymlinkSelected, idCmdFirst + IDM_SYMBOLICLINK_SHOW, GetMessageString(IDS_SYMBOLICLINK_SHOW));
272     ::InsertMenu(hSymbolicMenu, indexSymbolicMenu++, MF_STRING | MF_BYPOSITION | nSymlinkSelected, idCmdFirst + IDM_SYMBOLICLINK_REMOVE, GetMessageString(IDS_SYMBOLICLINK_REMOVE));
273     ::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hSymbolicMenu, GetMessageString(IDS_SYMBOLIC_LINK_ITEM));
274
275     // The Submounts menu has been removed because the AFS tray icon
276     // and control panel now support mapping drives directly to an AFS
277     // path.
278     //
279     //HMENU hSubmountMenu = CreatePopupMenu();
280     //int indexSubmountMenu = 0;
281     //::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION | nSingleDirOnly, idCmdFirst + IDM_SUBMOUNTS_CREATE, GetMessageString(IDS_SUBMOUNTS_CREATE_ITEM));
282     //::InsertMenu(hSubmountMenu, indexSubmountMenu++, MF_STRING | MF_BYPOSITION, idCmdFirst + IDM_SUBMOUNTS_EDIT, GetMessageString(IDS_SUBMOUNTS_EDIT_ITEM));
283     //::InsertMenu(hAfsMenu, indexAfsMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hSubmountMenu, GetMessageString(IDS_SUBMOUNTS_ITEM));
284
285     // Add a separator
286     ::InsertMenu (hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_SEPARATOR, 0, TEXT(""));
287
288     // Add the AFS submenu to the shell's menu
289     ::InsertMenu(hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT)hAfsMenu, GetMessageString(IDS_AFS_ITEM));
290
291     // Add a separator after us
292     ::InsertMenu (hMenu, indexMenu + indexShellMenu++, MF_STRING | MF_BYPOSITION | MF_SEPARATOR, 0, TEXT(""));
293
294     return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 
295                          (USHORT)indexAfsMenu + indexVolPartMenu + indexMountPointMenu + indexShellMenu + indexSymbolicMenu);
296 }       
297
298 STDMETHODIMP CShellExt::XMenuExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
299 {
300     METHOD_PROLOGUE(CShellExt, MenuExt);
301
302     if (HIWORD(lpici->lpVerb ))
303         return E_FAIL;
304
305     AddRef();
306
307     CStringArray &files = pThis->m_astrFileNames;
308
309     switch (LOWORD(lpici->lpVerb))
310     {
311     case IDM_AUTHENTICATION:    {
312         CAuthDlg dlg;
313         dlg.DoModal();
314         break;
315     }
316
317     case IDM_ACL_SET:                   { 
318         CSetAfsAcl dlg;
319         ASSERT(files.GetSize() == 1);
320         dlg.SetDir(files[0]);
321         dlg.DoModal();
322         break;
323     }
324
325     case IDM_VOLUME_PROPERTIES: {
326         CVolumeInfo dlg;
327         dlg.SetFiles(files);
328         dlg.DoModal();
329         break;
330     }
331
332     case IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE:
333         CheckVolumes();
334         break;
335
336     case IDM_MOUNTPOINT_SHOW:   
337         ListMount(files);
338         break;
339
340     case IDM_MOUNTPOINT_REMOVE: {
341         int nChoice = ShowMessageBox(IDS_REALLY_DEL_MOUNT_POINTS, MB_ICONQUESTION | MB_YESNO, IDS_REALLY_DEL_MOUNT_POINTS);
342         if (nChoice == IDYES)
343             RemoveMount(files);
344         break;
345     }
346
347     case IDM_MOUNTPOINT_MAKE:   {
348         CMakeMountPointDlg dlg;
349         ASSERT(files.GetSize() == 1);
350         dlg.SetDir(files[0]);
351         dlg.DoModal();
352         break;
353     }
354
355     case IDM_FLUSH:                             
356         Flush(files);
357         break;
358
359     case IDM_FLUSH_VOLUME:              
360         FlushVolume(files);
361         break;
362
363     case IDM_SHOW_SERVER:              
364         WhereIs(files);
365         break;
366
367     case IDM_SHOWCELL:
368         WhichCell(files);
369         break;
370
371     case IDM_SERVER_STATUS: {
372         CServerStatusDlg dlg;
373         dlg.DoModal();
374         break;
375     }
376
377         /*
378         case IDM_SUBMOUNTS_EDIT:        {
379         CSubmountsDlg dlg;
380         dlg.DoModal();
381         break;
382         }
383         case IDM_SUBMOUNTS_CREATE:      {
384         ASSERT(files.GetSize() == 1);
385         CSubmountsDlg dlg;
386         dlg.SetAddOnlyMode(files[0]);
387         dlg.DoModal();
388         break;
389         }
390         */
391     case IDM_SYMBOLICLINK_REMOVE: {
392         if (files.GetSize()>1)
393             break;
394         CString msg=files.GetAt(0);
395         int i;
396         if ((i=msg.ReverseFind('\\'))>0)
397             msg=msg.Left(i+1);
398         else if ((i=msg.ReverseFind(':'))>0)
399             msg=msg.Left(i+1)+"\\";
400         if (!SetCurrentDirectory(msg))
401         {
402             MessageBeep((UINT)-1);
403             ShowMessageBox(IDS_UNABLE_TO_SET_CURRENT_DIRECTORY,MB_OK,IDS_UNABLE_TO_SET_CURRENT_DIRECTORY);
404             break;
405         }
406         msg=files.GetAt(0);
407         if ((i=msg.ReverseFind('\\'))>0||((i=msg.ReverseFind(':'))>0))
408             msg=msg.Right(msg.GetLength()-i-1);
409         int nChoice = ShowMessageBox(IDS_REALLY_REMOVE_SYMLINK, MB_ICONQUESTION | MB_YESNO, IDS_REALLY_REMOVE_SYMLINK,msg);
410         if (nChoice == IDYES)
411             RemoveSymlink(files.GetAt(0));
412         break;
413     }
414
415     case IDM_SYMBOLICLINK_ADD: {
416         CStringA msg(files.GetAt(0));
417         int i;
418         if ((i=msg.ReverseFind('\\'))>0)
419             msg=msg.Left(i+1);
420         else if ((i=msg.ReverseFind(':'))>0)
421             msg=msg.Left(i+1)+"\\";
422         CMakeSymbolicLinkDlg dlg;
423         dlg.Setbase(msg);
424         dlg.DoModal();
425         break;
426     }
427
428     case IDM_SYMBOLICLINK_SHOW: 
429         ListSymlink(files);
430         break;
431
432     case IDM_REMOVE_SYMLINK:    {
433         if (files.GetSize()>1)
434             break;
435         int nChoice = ShowMessageBox(IDS_REALLY_REMOVE_SYMLINK, MB_ICONQUESTION | MB_YESNO, IDS_REALLY_REMOVE_SYMLINK);
436         if (nChoice == IDYES)
437             RemoveSymlink(files.GetAt(0));
438         }       
439         break;
440     default:
441         ASSERT(FALSE);
442         Release();
443         return E_INVALIDARG;
444     }
445
446     Release();
447
448     return NOERROR;
449 }       
450
451 STDMETHODIMP CShellExt::XMenuExt::GetCommandString(UINT_PTR idCmd, UINT uType,
452     UINT* pwReserved, LPSTR pszName, UINT cchMax)
453 {
454     if (uType != GCS_HELPTEXT)
455         return NOERROR;                 // ?????????????????????????????????????????????????
456
457     UINT nCmdStrID;
458
459     AfxSetResourceHandle(theApp.m_hInstance);
460
461     switch (idCmd)
462     {
463     case IDM_AUTHENTICATION: 
464         nCmdStrID = ID_AUTHENTICATE;
465         break;
466
467     case IDM_ACL_SET: 
468         nCmdStrID = ID_ACL_SET;
469         break;
470
471     case IDM_VOLUME_PROPERTIES:
472         nCmdStrID = ID_VOLUME_PROPERTIES;
473         break;
474
475     case IDM_VOLUMEPARTITION_UPDATENAMEIDTABLE: 
476         nCmdStrID = ID_VOLUMEPARTITION_UPDATENAMEIDTABLE;
477         break;
478
479     case IDM_MOUNTPOINT_SHOW:  
480         nCmdStrID = ID_MOUNTPOINT_SHOW;
481         break;
482
483     case IDM_MOUNTPOINT_REMOVE: 
484         nCmdStrID = ID_MOUNTPOINT_REMOVE;
485         break;
486                 
487     case IDM_MOUNTPOINT_MAKE: 
488         nCmdStrID = ID_MOUNTPOINT_MAKE;
489         break;
490
491     case IDM_FLUSH:  
492         nCmdStrID = ID_FLUSH;
493         break;
494
495     case IDM_FLUSH_VOLUME:   
496         nCmdStrID = ID_VOLUME_FLUSH;
497         break;
498
499     case IDM_SHOW_SERVER: 
500         nCmdStrID = ID_WHEREIS;
501         break;
502
503     case IDM_SHOWCELL:  
504         nCmdStrID = ID_SHOWCELL;
505         break;
506
507     case IDM_SERVER_STATUS: 
508         nCmdStrID = ID_SERVER_STATUS;
509         break;
510
511     case IDM_SYMBOLICLINK_ADD:
512         nCmdStrID = ID_SYMBOLICLINK_ADD;
513         break;
514                 
515     case IDM_SYMBOLICLINK_SHOW:
516         nCmdStrID = ID_SYMBOLICLINK_SHOW;
517         break;
518
519     case IDM_SYMBOLICLINK_REMOVE: 
520         nCmdStrID = ID_SYMBOLICLINK_REMOVE;
521         break;
522
523     case IDM_REMOVE_SYMLINK:
524         nCmdStrID= ID_REMOVE_SYMLINK;
525         break;
526                 
527     default:
528         ASSERT(FALSE);
529         Release();
530         return E_INVALIDARG;
531     }
532
533     CString strMsg;
534     LoadString (strMsg, nCmdStrID);
535
536     _tcsncpy((LPTSTR) pszName, strMsg, cchMax);
537
538     return NOERROR;
539 }
540
541 /////////////////////////////////////////////////////////////////////////////
542 // IUnknown for IShellExtInit
543 /////////////////////////////////////////////////////////////////////////////
544 STDMETHODIMP CShellExt::XShellInit::QueryInterface(REFIID riid, void** ppv)
545 {
546     METHOD_PROLOGUE(CShellExt, ShellInit);
547
548     return pThis->ExternalQueryInterface(&riid, ppv);
549 }
550
551 STDMETHODIMP_(ULONG) CShellExt::XShellInit::AddRef(void)
552 {
553     return ++nSERefCount;
554 }
555
556 STDMETHODIMP_(ULONG) CShellExt::XShellInit::Release(void)
557 {
558     if (nSERefCount > 0)
559         nSERefCount--;
560         
561     return nSERefCount;
562 }
563
564 /////////////////////////////////////////////////////////////////////////////
565 // IShellInit Functions
566 /////////////////////////////////////////////////////////////////////////////
567 STDMETHODIMP CShellExt::XShellInit::Initialize(LPCITEMIDLIST pidlFolder, IDataObject *pdobj, HKEY hkeyProgID)
568 {
569     METHOD_PROLOGUE(CShellExt, ShellInit);
570
571     HRESULT hres = E_FAIL;
572     FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
573     STGMEDIUM medium;
574
575     // We must have a data object
576     if ((pdobj == NULL) && (pidlFolder == NULL))
577         return E_FAIL;
578
579     if (pdobj) {
580         //  Use the given IDataObject to get a list of filenames (CF_HDROP)
581         hres = pdobj->GetData(&fmte, &medium);
582         if (FAILED(hres)) {
583         return E_FAIL;
584         }
585
586         int nNumFiles = DragQueryFile((HDROP)medium.hGlobal, 0xFFFFFFFF, NULL, 0);
587         if (nNumFiles == 0)
588             hres = E_FAIL;
589         else {
590             pThis->m_bDirSelected = FALSE;
591
592             for (int ii = 0; ii < nNumFiles; ii++) {
593                 CString strFileName;
594
595                 // Get the size of the file name string
596                 int nNameLen = DragQueryFile((HDROP)medium.hGlobal, ii, 0, 0);
597
598                 // Make room for it in our string object
599                 LPTSTR pszFileNameBuf = strFileName.GetBuffer(nNameLen + 1);    // +1 for the terminating NULL
600                 ASSERT(pszFileNameBuf);
601
602                 // Get the file name
603                 DragQueryFile((HDROP)medium.hGlobal, ii, pszFileNameBuf, nNameLen + 1);
604
605                 strFileName.ReleaseBuffer();
606                 if (!IsPathInAfs(strFileName)) {
607                 pThis->m_astrFileNames.RemoveAll();
608                 break;
609                 } else {
610                 pThis->m_bIsSymlink=IsSymlink(strFileName);
611                 }
612
613                 if (IsADir(strFileName))
614                 pThis->m_bDirSelected = TRUE;
615
616                 pThis->m_astrFileNames.Add(strFileName);
617             }
618             //  Release the data
619             ReleaseStgMedium(&medium);
620         }
621     }
622     if ((pThis->m_astrFileNames.GetSize() == 0)&&(pidlFolder)) {
623         // if there are no valid files selected, try the folder background
624         IShellFolder *parentFolder = NULL;
625         STRRET name;
626         TCHAR * szDisplayName = NULL;
627
628         hres = ::SHGetDesktopFolder(&parentFolder);
629         if (FAILED(hres))
630             return hres;
631
632         hres = parentFolder->GetDisplayNameOf(pidlFolder, SHGDN_NORMAL | SHGDN_FORPARSING, &name);
633         if (FAILED(hres)) {
634             parentFolder->Release();
635             return hres;
636         }
637
638         hres = StrRetToStr (&name, pidlFolder, &szDisplayName);
639         if (FAILED(hres))
640             return hres;
641         parentFolder->Release();
642         if (szDisplayName) {
643             pThis->m_bDirSelected = TRUE;
644             CString strFileName = CString(szDisplayName);
645             if (IsPathInAfs(strFileName)) {
646                 pThis->m_bIsSymlink=IsSymlink(strFileName);
647                 pThis->m_astrFileNames.Add(strFileName);
648             }
649             CoTaskMemFree(szDisplayName);
650         }
651     }
652         if (pThis->m_astrFileNames.GetSize() > 0)
653             hres = NOERROR;
654         else
655             hres = E_FAIL;
656  
657     return hres;
658 }
659
660 STDMETHODIMP CShellExt::XIconExt::QueryInterface(REFIID riid, void** ppv)
661 {
662     METHOD_PROLOGUE(CShellExt, IconExt);
663     return pThis->ExternalQueryInterface(&riid, ppv);
664 }
665
666 STDMETHODIMP_(ULONG) CShellExt::XIconExt::AddRef(void)
667 {
668     return ++nICRefCount;
669 }
670
671 STDMETHODIMP_(ULONG) CShellExt::XIconExt::Release(void)
672 {
673     if (nICRefCount > 0)
674         nICRefCount--;
675
676     return nICRefCount;
677 }       
678
679
680 /////////////////////////////////////////////////////////////////////////////
681 // IIconHandler Functions
682 /////////////////////////////////////////////////////////////////////////////
683
684 STDMETHODIMP CShellExt::XIconExt::GetOverlayInfo(LPWSTR pwszIconFile
685         ,int cchMax,int* pIndex,DWORD* pdwFlags)
686 {
687     if(IsBadWritePtr(pIndex, sizeof(int)))
688         return E_INVALIDARG;
689     if(IsBadWritePtr(pdwFlags, sizeof(DWORD)))
690         return E_INVALIDARG;
691
692     HMODULE hModule=GetModuleHandle(_T("shell32.dll"));
693     TCHAR szModule[MAX_PATH];
694     DWORD z=GetModuleFileName(hModule,szModule,sizeof(szModule));
695 #ifndef UNICODE
696     MultiByteToWideChar( CP_ACP,0,szModule,-1,pwszIconFile,cchMax); 
697 #else
698     _tcsncpy(pwszIconFile, szModule, cchMax);
699 #endif
700     *pIndex = 30;
701     *pdwFlags = ISIOI_ICONFILE|ISIOI_ICONINDEX;
702     return S_OK;
703 }
704
705 STDMETHODIMP CShellExt::XIconExt::GetPriority(int* pPriority)
706 {
707     if(IsBadWritePtr(pPriority, sizeof(int)))
708         return E_INVALIDARG;
709     *pPriority = 0;
710     return S_OK;
711 }
712
713 STDMETHODIMP CShellExt::XIconExt::IsMemberOf(LPCWSTR pwszPath,DWORD dwAttrib)
714 {
715     TCHAR szPath[MAX_PATH];
716 #ifdef UNICODE
717     _tcscpy(szPath, pwszPath);
718 #else
719     WideCharToMultiByte( CP_ACP,0,pwszPath,-1,szPath,MAX_PATH,NULL,NULL);
720 #endif
721     if (IsSymlink(szPath) || IsMountPoint(szPath)) {
722         return S_OK;
723     }
724     return S_FALSE;
725 }       
726
727 /*  TOOL TIP INFO IMPLIMENTION   */
728
729 STDMETHODIMP CShellExt::XToolTipExt::QueryInterface(REFIID riid, void** ppv)
730 {
731     METHOD_PROLOGUE(CShellExt, ToolTipExt);
732     return pThis->ExternalQueryInterface(&riid, ppv);
733 }
734
735 STDMETHODIMP_(ULONG) CShellExt::XToolTipExt::AddRef(void)
736 {
737     return ++nTPRefCount;
738 }
739
740 STDMETHODIMP_(ULONG) CShellExt::XToolTipExt::Release(void)
741 {
742     if (nTPRefCount> 0)
743         nTPRefCount--;
744
745     return nTPRefCount;
746 }
747
748 STDMETHODIMP CShellExt::XToolTipExt::GetInfoTip(DWORD dwFlags, LPWSTR *ppwszTip)
749 {
750     METHOD_PROLOGUE(CShellExt, ToolTipExt);
751
752     if (!IsSymlink(pThis->m_szFile))
753     {
754         ppwszTip=NULL;
755         return S_OK;
756     }
757     USES_CONVERSION;
758     // dwFlags is currently unused.
759     *ppwszTip = (WCHAR*) (pThis->m_pAlloc)->Alloc((1+lstrlen(pThis->m_szFile))*sizeof(WCHAR));
760     if (*ppwszTip)
761     {
762         wcscpy(*ppwszTip, (WCHAR*)T2OLE(pThis->m_szFile));
763     }
764
765     return S_OK;
766 }
767 STDMETHODIMP CShellExt::XToolTipExt::GetInfoFlags(LPDWORD pdwFlags)
768 {
769     return S_OK;
770 }
771
772 //////////                          IPersistFile
773 ///////                            PersistFileExt
774
775 STDMETHODIMP CShellExt::XPersistFileExt::QueryInterface(REFIID riid, void** ppv)
776 {
777     METHOD_PROLOGUE(CShellExt, PersistFileExt);
778     return pThis->ExternalQueryInterface(&riid, ppv);
779 }
780
781 STDMETHODIMP_(ULONG) CShellExt::XPersistFileExt::AddRef(void)
782 {
783     return ++nXPRefCount;
784 }
785
786 STDMETHODIMP_(ULONG) CShellExt::XPersistFileExt::Release(void)
787 {
788     if (nXPRefCount> 0)
789         nXPRefCount--;
790
791     return nXPRefCount;
792 }
793
794 STDMETHODIMP    CShellExt::XPersistFileExt::Load(LPCOLESTR wszFile, DWORD dwMode)
795 {
796     METHOD_PROLOGUE(CShellExt, PersistFileExt);
797     USES_CONVERSION;
798     _tcscpy(pThis->m_szFile, OLE2T((WCHAR*)wszFile)); 
799     return S_OK;        
800 }
801
802 STDMETHODIMP CShellExt::XPersistFileExt::GetClassID(LPCLSID)
803
804     return E_NOTIMPL;   
805 }
806
807 STDMETHODIMP CShellExt::XPersistFileExt::IsDirty(VOID)
808
809     return E_NOTIMPL; 
810 }
811
812 STDMETHODIMP CShellExt::XPersistFileExt::Save(LPCOLESTR, BOOL)
813
814     return E_NOTIMPL; 
815 }
816
817 STDMETHODIMP CShellExt::XPersistFileExt::SaveCompleted(LPCOLESTR)
818
819     return E_NOTIMPL; 
820 }
821
822 STDMETHODIMP CShellExt::XPersistFileExt::GetCurFile(LPOLESTR FAR*)
823
824     return E_NOTIMPL; 
825 }