|
|
/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
nwshmenu.cxx
Abstract:
This module implements the IContextMenu member functions necessary to support the context menu of NetWare shell extension.
Author:
Yi-Hsin Sung (yihsins) 25-Oct-1995
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <shellapi.h>
#include <shlobj.h>
#define DONT_WANT_SHELLDEBUG
#include <shlobjp.h>
#include <winnetwk.h>
#include <ntddnwfs.h>
//extern "C"
//{
#include "nwshrc.h"
#include "nwwks.h"
#include "nwutil.h"
//}
#include "nwshcmn.h"
#include "nwshext.h"
#define MAX_VERB_SIZE 128
#define MAX_SHELL_IDLIST_SIZE 512
BOOL g_cfNetResource = 0; // Clipboard format
BOOL g_cfIDList = 0;
NWMENUITEM aServerVerbs[] = { { IDO_VERB_WHOAMI, 0 }, { IDO_VERB_LOGOUT, 0 }, { IDO_VERB_ATTACHAS, 0 }, { 0, 0 } };
NWMENUITEM aDSVerbs[] = { { IDO_VERB_TREEWHOAMI, 0 }, // { IDO_VERB_SETDEFAULTCONTEXT, 0 },
{ 0, 0 } };
NWMENUITEM aDSTreeVerbs[] = { { IDO_VERB_TREEWHOAMI, 0 }, { 0, 0 } };
NWMENUITEM aGlobalVerbs[] = { { IDO_VERB_GLOBALWHOAMI, 0 }, { 0, 0 } };
NWMENUITEM aDirectoryVerbs[] = { { IDO_VERB_MAPNETWORKDRIVE, 0 }, { 0, 0 } };
NWMENUITEM aHoodVerbs[] = { { IDO_VERB_GLOBALWHOAMI, 0 }, { 0, 0 } };
HRESULT InsertCommandsArray( HMENU hMenu, UINT indexMenu, UINT idCmdFirst, LPNWMENUITEM aVerbs );
UINT LookupCommand( LPNWMENUITEM aVerbs, LPCSTR pszCmd );
UINT LookupResource( LPNWMENUITEM aVerbs, UINT uiResourceOffset );
UINT WINAPI HIDA_GetIDList( LPIDA hida, UINT i, LPITEMIDLIST pidlOut, UINT cbMax);
//
// FUNCTION: CNWObjContextMenu::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT)
//
// PURPOSE: Called by the shell just before the context menu is displayed.
// This is where you add your specific menu items.
//
// PARAMETERS:
// hMenu - Handle to the context menu
// indexMenu - Index of where to begin inserting menu items
// idCmdFirst - Lowest value for new menu ID's
// idCmtLast - Highest value for new menu ID's
// uFlags - Specifies the context of the menu event
//
// RETURN VALUE:
//
//
// COMMENTS:
//
STDMETHODIMP CNWObjContextMenu::QueryContextMenu( HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags ) { HRESULT hres; LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
if ( !::GetNetResourceFromShell( _pDataObj, pNetRes, sizeof( _buffer ))) { // We cannot get the net resource of the selected object.
// Must return number of menu items we added.
// Nothing added here.
return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, 0 )); }
// First, add a menu separator
if ( InsertMenu( hMenu, indexMenu, MF_SEPARATOR | MF_BYPOSITION, 0, NULL)) indexMenu++;
// Next, add menu items depending on display types
switch ( pNetRes->dwDisplayType ) { case RESOURCEDISPLAYTYPE_ROOT: case RESOURCEDISPLAYTYPE_NETWORK: hres = InsertCommandsArray( hMenu, indexMenu, idCmdFirst, _pIdTable = aGlobalVerbs );
break;
case RESOURCEDISPLAYTYPE_TREE: hres = InsertCommandsArray( hMenu, indexMenu, idCmdFirst, _pIdTable = aDSTreeVerbs ); break;
case RESOURCEDISPLAYTYPE_NDSCONTAINER: hres = InsertCommandsArray( hMenu, indexMenu, idCmdFirst, _pIdTable = aDSVerbs ); break;
case RESOURCEDISPLAYTYPE_SERVER: { // Do we need to check if the server name is local
// and disallow operation???
hres = InsertCommandsArray( hMenu, indexMenu, idCmdFirst, _pIdTable = aServerVerbs );
if (!SUCCEEDED(hres)) break;
LPBYTE pBuffer = NULL; DWORD EntriesRead = 0; DWORD_PTR ResumeKey = 0; WCHAR szServerName[MAX_PATH + 1];
NwExtractServerName( pNetRes->lpRemoteName, szServerName );
// See if we are connected.
DWORD err = NwGetConnectionStatus( szServerName, &ResumeKey, &pBuffer, &EntriesRead );
if ( err == NO_ERROR && EntriesRead > 0 ) { PCONN_STATUS pConnStatus = (PCONN_STATUS) pBuffer;
ASSERT( EntriesRead == 1 );
if ( pConnStatus->fPreferred ) { // This is a implicit preferred server connection
// so, don't show the connection and don't let the user
// logout of it since rdr doesn't allow it.
::EnableMenuItem( hMenu, LookupResource( aServerVerbs, IDO_VERB_LOGOUT), MF_GRAYED | MF_BYCOMMAND);
} else if ( pConnStatus->fNds ) { BOOL fInDefaultTree = FALSE;
err = NwIsServerInDefaultTree( pNetRes->lpRemoteName, &fInDefaultTree );
if ( (err == NO_ERROR) && fInDefaultTree ) { // NDS connection and in the default tree, disable the Attach As button
::EnableMenuItem( hMenu, LookupResource( aServerVerbs, IDO_VERB_ATTACHAS), MF_GRAYED | MF_BYCOMMAND ); } } } else { // If we are not attached or if error occurred when getting
// connection status, then disable the Logout button.
::EnableMenuItem( hMenu, LookupResource( aServerVerbs, IDO_VERB_LOGOUT), MF_GRAYED | MF_BYCOMMAND); }
if ( pBuffer != NULL ) { LocalFree( pBuffer ); pBuffer = NULL; } break; }
default: // Must return number of menu items we added.
// Nothing added here.
hres = ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, 0 )); break;
}
return hres; }
//
// FUNCTION: CNWObjContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO)
//
// PURPOSE: Called by the shell after the user has selected on of the
// menu items that was added in QueryContextMenu().
//
// PARAMETERS:
// lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
//
// RETURN VALUE:
//
//
// COMMENTS:
//
STDMETHODIMP CNWObjContextMenu::InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi ) { HRESULT hres = ResultFromScode(E_INVALIDARG); UINT idCmd = LookupCommand( _pIdTable , lpcmi->lpVerb );
if ( !idCmd ) return hres;
LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
switch ( idCmd ) { case IDO_VERB_GLOBALWHOAMI: hres = NWUIGlobalWhoAmI( lpcmi->hwnd ); break;
case IDO_VERB_TREEWHOAMI: hres = NWUIWhoAmI( lpcmi->hwnd, pNetRes ); break;
#if 0
case IDO_VERB_SETDEFAULTCONTEXT: hres = NWUISetDefaultContext( lpcmi->hwnd, pNetRes ); break; #endif
case IDO_VERB_WHOAMI: hres = NWUIWhoAmI( lpcmi->hwnd, pNetRes ); break;
case IDO_VERB_LOGOUT: { BOOL fDisconnected = FALSE; hres = NWUILogOut( lpcmi->hwnd, pNetRes, &fDisconnected ); if ( hres == NOERROR && fDisconnected ) { // Logout is successful, need to notify shell
FORMATETC fmte = { g_cfIDList ? g_cfIDList : (g_cfIDList=RegisterClipboardFormat( CFSTR_SHELLIDLIST)), (DVTARGETDEVICE FAR *)NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; STGMEDIUM medium;
hres = _pDataObj->GetData( &fmte, &medium);
if (SUCCEEDED(hres)) { // We got pointer to IDList
LPIDA pida = (LPIDA)GlobalLock(medium.hGlobal);
if ( pida ) { BYTE BufIDList[MAX_SHELL_IDLIST_SIZE]; LPITEMIDLIST pidl = (LPITEMIDLIST) BufIDList;
if ( pidl ) { // Convert IDA to IDList for this call
HIDA_GetIDList( pida, 0, // One object should present
pidl , MAX_SHELL_IDLIST_SIZE);
// Call SHchangeNotify
g_pFuncSHChangeNotify( SHCNE_SERVERDISCONNECT, SHCNF_IDLIST, pidl, NULL); }
GlobalUnlock(medium.hGlobal); } }
} break; }
case IDO_VERB_ATTACHAS: hres = NWUIAttachAs( lpcmi->hwnd, pNetRes ); break; }
return hres; }
//
// FUNCTION: CNWObjContextMenu::GetCommandString( UINT, UINT, UINT FAR *, LPSTR, UINT )
//
// PURPOSE: Called by the shell after the user has selected on of the
// menu items that was added in QueryContextMenu().
//
// PARAMETERS:
//
// RETURN VALUE:
//
//
// COMMENTS:
//
STDMETHODIMP CNWObjContextMenu::GetCommandString( UINT_PTR idCmd, UINT uFlags, UINT FAR *reserved, LPSTR pszName, UINT cchMax ) { if ( uFlags == GCS_HELPTEXT && _pIdTable != NULL ) { ::LoadString( ::hmodNW, IDS_VERBS_HELP_BASE + _pIdTable[idCmd].idResourceString, (LPWSTR) pszName, cchMax );
return NOERROR; }
return E_NOTIMPL; }
//
// FUNCTION: CNWFldContextMenu::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT)
//
// PURPOSE: Called by the shell just before the context menu is displayed.
// This is where you add your specific menu items.
//
// PARAMETERS:
// hMenu - Handle to the context menu
// indexMenu - Index of where to begin inserting menu items
// idCmdFirst - Lowest value for new menu ID's
// idCmtLast - Highest value for new menu ID's
// uFlags - Specifies the context of the menu event
//
// RETURN VALUE:
//
//
// COMMENTS:
//
STDMETHODIMP CNWFldContextMenu::QueryContextMenu( HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags ) { UINT idCmd = idCmdFirst;
if ( IsNetWareObject() ) { WCHAR szFullPath[MAX_PATH+1];
if ( GetFSObject( szFullPath, sizeof( szFullPath )) == NOERROR ) { BOOL fUNC = FALSE;
// Check if the name at least contains the "\\server\share\dir"
// We need to add "Map Network Drive" menu in this case.
if (( szFullPath[0] == L'\\') && ( szFullPath[1] == L'\\')) { LPWSTR pszLastSlash = wcschr( szFullPath + 2, L'\\'); if ( pszLastSlash ) pszLastSlash = wcschr( pszLastSlash+1, L'\\');
if ( pszLastSlash != NULL ) fUNC = TRUE; }
if ( fUNC ) { LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer; WCHAR szProvider[MAX_PATH+1];
// Build a net resource that can be used to connect
// store the provider name first
wcscpy( szProvider, pNetRes->lpProvider );
// zero out the memory cause it is filled by IsNetWareObject
RtlZeroMemory( pNetRes, sizeof(NETRESOURCE));
pNetRes->dwType = RESOURCETYPE_DISK; pNetRes->lpRemoteName = (LPWSTR) ((DWORD_PTR)pNetRes + sizeof(NETRESOURCE)); wcscpy( pNetRes->lpRemoteName, szFullPath );
pNetRes->lpProvider = (LPWSTR) ((DWORD_PTR)pNetRes->lpRemoteName + (wcslen(szFullPath)+1)*sizeof(WCHAR)); wcscpy( pNetRes->lpProvider, szProvider );
if ( InsertMenu(hMenu, indexMenu, MF_SEPARATOR | MF_BYPOSITION, 0, NULL)) { indexMenu++; }
return InsertCommandsArray( hMenu, indexMenu, idCmdFirst, aDirectoryVerbs ); } } }
// Must return number of menu items we added.
// Nothing added here.
return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, 0 ));
}
//
// FUNCTION: CNWFldContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO)
//
// PURPOSE: Called by the shell after the user has selected on of the
// menu items that was added in QueryContextMenu().
//
// PARAMETERS:
// lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
//
// RETURN VALUE:
//
//
// COMMENTS:
//
STDMETHODIMP CNWFldContextMenu::InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi ) { HRESULT hres = ResultFromScode(E_INVALIDARG); UINT idCmd = LookupCommand( aDirectoryVerbs , lpcmi->lpVerb );
if ( !idCmd ) return hres;
LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
switch ( idCmd ) { case IDO_VERB_MAPNETWORKDRIVE: hres = NWUIMapNetworkDrive( lpcmi->hwnd, pNetRes ); break; }
return hres; }
//
// FUNCTION: CNWFldContextMenu::GetCommandString( UINT, UINT, UINT FAR *, LPSTR, UINT )
//
// PURPOSE: Called by the shell after the user has selected on of the
// menu items that was added in QueryContextMenu().
//
// PARAMETERS:
//
// RETURN VALUE:
//
//
// COMMENTS:
//
STDMETHODIMP CNWFldContextMenu::GetCommandString( UINT_PTR idCmd, UINT uFlags, UINT FAR *reserved, LPSTR pszName, UINT cchMax ) { if ( uFlags == GCS_HELPTEXT ) { ::LoadString( ::hmodNW, IDS_VERBS_HELP_BASE + IDO_VERB_MAPNETWORKDRIVE, (LPWSTR) pszName, cchMax );
return NOERROR; }
return E_NOTIMPL; }
//
// Method checks if the selected object belongs the netware provider
//
BOOL CNWFldContextMenu::IsNetWareObject( VOID ) { LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
if ( !::GetNetResourceFromShell( _pDataObj, pNetRes, sizeof(_buffer))) { // Cannot get the NETRESOURCE of the selected object,
// hence assume that the object is not a NetWare object.
return FALSE; }
if ( ( pNetRes->lpProvider != NULL ) && ( _wcsicmp( pNetRes->lpProvider, g_szProviderName ) == 0 ) ) { return TRUE; }
return FALSE; }
//
// Method obtains file system name associated with selected shell object
//
HRESULT CNWFldContextMenu::GetFSObject( LPWSTR pszPath, UINT cbMaxPath ) { FORMATETC fmte = { CF_HDROP, (DVTARGETDEVICE FAR *) NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM medium; HRESULT hres = _pDataObj->GetData( &fmte, &medium);
if (SUCCEEDED(hres)) { if ( g_pFuncSHDragQueryFile ) { HDROP hdrop = (HDROP) medium.hGlobal; UINT cFiles = (*g_pFuncSHDragQueryFile)( hdrop, (UINT)-1, NULL, 0 );
(*g_pFuncSHDragQueryFile)( hdrop, 0, pszPath, cbMaxPath );
ODS(L"CNWFldContextMenu::GetFSObject()\n"); ODS( pszPath ); ODS(L"\n"); }
//
// HACK: We are supposed to call ReleaseStgMedium. This is a temporary
// hack until OLE 2.01 for Chicago is released.
//
if (medium.pUnkForRelease) { medium.pUnkForRelease->Release(); } else { GlobalFree(medium.hGlobal); } }
return hres; }
// FUNCTION: CNWHoodContextMenu::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT)
//
// PURPOSE: Called by the shell just before the context menu is displayed.
// This is where you add your specific menu items.
//
// PARAMETERS:
// hMenu - Handle to the context menu
// indexMenu - Index of where to begin inserting menu items
// idCmdFirst - Lowest value for new menu ID's
// idCmtLast - Highest value for new menu ID's
// uFlags - Specifies the context of the menu event
//
// RETURN VALUE:
//
//
// COMMENTS:
//
STDMETHODIMP CNWHoodContextMenu::QueryContextMenu( HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags ) { // First, insert a menu separator
if ( InsertMenu(hMenu, indexMenu, MF_SEPARATOR | MF_BYPOSITION, 0, NULL)) { indexMenu++; }
// Then, insert the verbs
return InsertCommandsArray( hMenu, indexMenu, idCmdFirst, aHoodVerbs ); }
//
// FUNCTION: CNWHoodContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO)
//
// PURPOSE: Called by the shell after the user has selected on of the
// menu items that was added in QueryContextMenu().
//
// PARAMETERS:
// lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
//
// RETURN VALUE:
//
//
// COMMENTS:
//
STDMETHODIMP CNWHoodContextMenu::InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi ) { HRESULT hres = ResultFromScode(E_INVALIDARG); UINT idCmd = LookupCommand( aHoodVerbs , lpcmi->lpVerb );
if ( !idCmd ) return hres;
switch ( idCmd ) { case IDO_VERB_GLOBALWHOAMI: hres = NWUIGlobalWhoAmI( lpcmi->hwnd ); break; }
return hres; }
//
// FUNCTION: CNWHoodContextMenu::GetCommandString( UINT, UINT, UINT FAR *, LPSTR, UINT)
//
// PURPOSE: Called by the shell after the user has selected on of the
// menu items that was added in QueryContextMenu().
//
// PARAMETERS:
//
// RETURN VALUE:
//
//
// COMMENTS:
//
STDMETHODIMP CNWHoodContextMenu::GetCommandString( UINT_PTR idCmd, UINT uFlags, UINT FAR *reserved, LPSTR pszName, UINT cchMax ) { if ( uFlags == GCS_HELPTEXT ) { ::LoadString( ::hmodNW, IDS_VERBS_HELP_BASE + IDO_VERB_GLOBALWHOAMI, (LPWSTR) pszName, cchMax );
return NOERROR; }
return E_NOTIMPL; }
//
// Method gets the NETRESOURCE of the selected object
//
BOOL GetNetResourceFromShell( LPDATAOBJECT pDataObj, LPNETRESOURCE pNetRes, UINT dwBufferSize ) { FORMATETC fmte = { g_cfNetResource ? g_cfNetResource : (g_cfNetResource=RegisterClipboardFormat(CFSTR_NETRESOURCES)), (DVTARGETDEVICE FAR *) NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM medium; UINT cItems;
if ( pNetRes == NULL ) return FALSE;
memset( pNetRes, 0, dwBufferSize );
if ( !g_pFuncSHGetNetResource ) // Not loaded
return FALSE;
HRESULT hres = pDataObj->GetData( &fmte, &medium );
if (!SUCCEEDED(hres)) return FALSE;
HNRES hnres = medium.hGlobal;
// Get the number of selected items
cItems = (*g_pFuncSHGetNetResource)( hnres, (UINT)-1, NULL, 0);
if ( cItems == 0 ) // Nothing selected
return FALSE;
// Get the NETRESOURCE of the first item
(*g_pFuncSHGetNetResource)( hnres, 0, pNetRes, dwBufferSize);
#if DBG
WCHAR szTemp[32]; wsprintf(szTemp, L"DisplayType = %d\n", pNetRes->dwDisplayType );
ODS(L"\n**** GetNetResourceFromShell ***\n"); ODS(pNetRes->lpProvider ); ODS(L"\n"); ODS(pNetRes->lpRemoteName ); ODS(L"\n"); ODS(szTemp ); ODS(L"\n\n"); #endif
//
// HACK: We are supposed to call ReleaseStgMedium. This is a temporary
// hack until OLE 2.01 for Chicago is released.
//
if (medium.pUnkForRelease) { medium.pUnkForRelease->Release(); } else { GlobalFree(medium.hGlobal); }
return TRUE; }
//-------------------------------------------------------------------//
HRESULT InsertCommandsArray( HMENU hMenu, UINT indexMenu, UINT idCmdFirst, LPNWMENUITEM aVerbs ) { UINT idNewCmdFirst = idCmdFirst; WCHAR szVerb[MAX_VERB_SIZE];
for ( int i = 0; aVerbs[i].idResourceString ; i++) { if ( ::LoadString( ::hmodNW, aVerbs[i].idResourceString + IDS_VERBS_BASE, szVerb, sizeof(szVerb)/sizeof(szVerb[0]))) { if (::InsertMenu( hMenu, indexMenu, MF_STRING | MF_BYPOSITION, idNewCmdFirst, szVerb)) { // Add command id to the array
aVerbs[i].idCommand = idNewCmdFirst;
// Update command id and index
idNewCmdFirst++; if (indexMenu != (UINT)-1) indexMenu++; } } }
return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, (USHORT)(idNewCmdFirst-idCmdFirst))); }
UINT LookupCommand( LPNWMENUITEM aVerbs, LPCSTR pszCmd ) { if ((UINT_PTR)pszCmd > 0xFFFF) { // Possible that shell will use string commands, but unlikely
WCHAR szVerb[MAX_VERB_SIZE]; for ( int i=0; aVerbs[i].idResourceString; i++) { if ( ::LoadString( ::hmodNW, aVerbs[i].idResourceString + IDS_VERBS_BASE, szVerb, sizeof(szVerb)/sizeof(szVerb[0]))) { if( ::lstrcmpi( (LPCWSTR) pszCmd, szVerb) == 0) return( aVerbs[i].idResourceString); } }
return 0; } else { return( aVerbs[LOWORD(pszCmd)].idResourceString); } }
UINT LookupResource( LPNWMENUITEM aVerbs, UINT uiResourceOffset ) { for ( int i = 0; aVerbs[i].idResourceString; i++ ) { if ( aVerbs[i].idResourceString == uiResourceOffset ) return aVerbs[i].idCommand; }
return 0; }
//-------------------------------------------------------------------//
#define _ILSkip(pidl, cb) ((LPITEMIDLIST)(((BYTE*)(pidl))+cb))
#define _ILNext(pidl) _ILSkip(pidl, (pidl)->mkid.cb)
#define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
#define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
static UINT WINAPI MyILGetSize(LPCITEMIDLIST pidl) { UINT cbTotal = 0; if (pidl) { cbTotal += sizeof(pidl->mkid.cb); // Null terminator
while (pidl->mkid.cb) { cbTotal += pidl->mkid.cb; pidl = _ILNext(pidl); } }
return cbTotal; }
UINT WINAPI HIDA_GetIDList( LPIDA hida, UINT i, LPITEMIDLIST pidlOut, UINT cbMax) { LPCITEMIDLIST pidlFolder = HIDA_GetPIDLFolder((LPIDA)hida); LPCITEMIDLIST pidlItem = HIDA_GetPIDLItem((LPIDA)hida, i);
UINT cbFolder = MyILGetSize(pidlFolder)-sizeof(USHORT); UINT cbItem = MyILGetSize(pidlItem);
if (cbMax < cbFolder+cbItem) { if (pidlOut) pidlOut->mkid.cb = 0; } else { memmove(pidlOut, pidlFolder, cbFolder); memmove(((LPBYTE)pidlOut)+cbFolder, pidlItem, cbItem); }
return (cbFolder+cbItem); }
|