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