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