none
Emulation "paste" into a folder programatically RRS feed

  • Question

  • 	HRESULT hr = S_FALSE;
    	IContextMenu *cm;
    	HWND hwnd = GetDesktopWindow();
    
    	hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    
    	IShellFolder *desktopFolder;
    	// Retrieves the IShellFolder interface for the desktop folder, which is the root of the Shell's namespace.
    	SHGetDesktopFolder(&desktopFolder);
    
    	LPITEMIDLIST parentPidl;
    	DWORD eaten;
    	hr = desktopFolder->ParseDisplayName(hwnd, 0, L"D:\\target", &eaten, &parentPidl, 0);
    
    	IShellFolder *parentFolder;
    	LPCITEMIDLIST pidlChild;
    
    	hr = SHBindToParent(parentPidl, IID_IShellFolder, (void **)&parentFolder, &pidlChild);
    	hr = parentFolder->GetUIObjectOf(hwnd, 1, (LPCITEMIDLIST *)&pidlChild, IID_IContextMenu, NULL, (void **)&cm);
    
    	HMENU hmenu = CreatePopupMenu();
    	hr = cm->QueryContextMenu(hmenu, 0, 1, 0x7FFF, CMF_NORMAL); //|CMF_EXPLORE|CMF_EXTENDEDVERBS);
    
    	for(UINT_PTR i=0; i<0x7FFF;i++)
    	{
    		char szShortCut[MAX_PATH]={0};
    		hr = cm->GetCommandString(i, GCS_VERBA, NULL, szShortCut, (UINT)(sizeof(szShortCut)));
    		if( strlen(szShortCut) ) printf("i=%d,menu=%s\n", i, szShortCut);
    	}
    
    	CMINVOKECOMMANDINFOEX ci = {0};
    	const char *command = "paste";
    
    	ZeroMemory(&ci, sizeof(ci));
    	ci.cbSize = sizeof(ci);
    	ci.hwnd = hwnd;
    	ci.lpVerb = command;
    	ci.lpParameters = NULL;
    	ci.lpDirectory = NULL;
    	ci.nShow = SW_SHOWNORMAL;
    	hr = cm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ci);
    
    	DestroyMenu(hmenu);
    
    	CoUninitialize();

    After ctrl+c for a target file, how to paste it into a folder programatically?

    I extracted "paste" menu but it couldn't invoke.

    Others, like a "NewFolder" menu worked well.

    Monday, August 29, 2016 9:02 AM

Answers

  • The following sample console program uses the methods described at Handling Shell Data Transfer Scenarios to either move or copy files that have been copied or cut to the clipboard using ctrl+c or ctrl+x in Explorer .  As a sample it doesn't include extensive error checking.

    // ShellPaste.cpp : Defines the entry point for the console application.
    //
    
    #define NTDDI_VERSION NTDDI_VISTA
    #define _WIN32_WINNT _WIN32_WINNT_VISTA
    
    #include <sdkddkver.h>
    #include <Ole2.h>
    #include <ShlObj.h>
    
    #include <string>
    #include <memory>
    #include <iostream>
    
    using namespace std;
    
    
    int main()
    {
    	wstring strTarget(L"C:\\Users\\RLWA32\\Documents\\Junk");
    
    	strTarget += L'\0'; //Make sure target path is terminated by double null
    	strTarget += L'\0'; //required by SHFileOperation
    
    	if (SUCCEEDED(OleInitialize(NULL))) //Use OleInitialize for clipboard operations
    	{
    		IDataObject* pData = NULL;
    
    		CLIPFORMAT CF_PREFERRED = RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
    		CLIPFORMAT CF_PASTESUCCEEDED = RegisterClipboardFormat(CFSTR_PASTESUCCEEDED);
    		
    		HRESULT hr = OleGetClipboard(&pData);
    
    		if (pData)
    		{
    			FORMATETC ft = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    			FORMATETC ftdrop = { CF_PREFERRED, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    			FORMATETC ftpaste = { CF_PASTESUCCEEDED, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    
    			STGMEDIUM stg = { 0 }, stgdrop = { 0 }, stgpaste = { 0 };
    			DWORD dwDropEffect = DROPEFFECT_NONE;
    
    			if (SUCCEEDED(pData->GetData(&ftdrop, &stgdrop)))
    			{
    				LPDWORD pDropEffect = (LPDWORD)GlobalLock(stgdrop.hGlobal);
    
    				dwDropEffect = (*pDropEffect & DROPEFFECT_MOVE);
    
    				if (dwDropEffect == DROPEFFECT_NONE)
    					dwDropEffect = (*pDropEffect & DROPEFFECT_COPY);
    
    				GlobalUnlock(stgdrop.hGlobal);
    
    				ReleaseStgMedium(&stgdrop);
    			}
    
    
    			hr = pData->GetData(&ft, &stg);
    
    			if (SUCCEEDED(hr))
    			{
    				wstring strSource;
    				SHFILEOPSTRUCT sfo = { 0 };
    
    				HDROP hdrp = (HDROP)stg.hGlobal;
    
    				UINT nFiles = DragQueryFile(hdrp, -1, NULL, 0);
    
    				for (UINT i = 0; i < nFiles; i++)
    				{
    					unique_ptr<wchar_t> pFileName;
    
    					UINT cch = DragQueryFile(hdrp, i, NULL, 0); // Get path length not including null terminator
    
    					cch++; // Add space for null terminator
    
    					pFileName.reset( new wchar_t[cch]);
    
    					UINT nCopied = DragQueryFile(hdrp, i, pFileName.get(), cch);
    
    					strSource.append(pFileName.get());
    					strSource += L'\0'; 
    				}
    
    				strSource += L'\0';  //Add another null terminator for SHFileOperation
    
    				sfo.fFlags = FOF_NOCONFIRMMKDIR | FOF_ALLOWUNDO;
    				sfo.pFrom = strSource.c_str();
    				sfo.pTo = strTarget.c_str();
    
    				if (dwDropEffect == DROPEFFECT_MOVE)
    					sfo.wFunc = FO_MOVE;
    				else
    					sfo.wFunc = FO_COPY;
    
    				if (SHFileOperation(&sfo) == 0)
    				{
    					// If files were cut to the clipboard using Ctrl+x tell the shell we moved them
    					if (dwDropEffect == DROPEFFECT_MOVE)
    					{
    						stgpaste.tymed = TYMED_HGLOBAL;
    						
    						stgpaste.hGlobal = GlobalAlloc(GPTR, sizeof(DWORD));
    						
    						LPDWORD pEffect = (LPDWORD)stgpaste.hGlobal;
    
    						*pEffect = DROPEFFECT_MOVE;
    
    						hr = pData->SetData(&ftpaste, &stgpaste, TRUE);
    					}
    				}
    				
    				ReleaseStgMedium(&stg);
    			}
    
    			pData->Release();
    		}
    	}
    
    	OleUninitialize();
    
        return 0;
    }
    
    

    • Proposed as answer by Baron Bi Tuesday, August 30, 2016 5:57 AM
    • Marked as answer by Hart WangModerator Wednesday, September 7, 2016 1:57 AM
    Tuesday, August 30, 2016 12:21 AM

All replies

  • What is your real objective here?

    If you want to paste files copied to the clipboard by ctrl+c to a new location it can be easily accomplished without trying to programmatically invoke the shell's context menu command.

    Monday, August 29, 2016 12:28 PM
  • I know that. just by ctrl+v will do it easily but I'm trying to do it programmatically for making a emulator something.

    The point this question is shell's context menu command doesn't work well so how can I fix it up.

    Monday, August 29, 2016 11:57 PM
  • The following sample console program uses the methods described at Handling Shell Data Transfer Scenarios to either move or copy files that have been copied or cut to the clipboard using ctrl+c or ctrl+x in Explorer .  As a sample it doesn't include extensive error checking.

    // ShellPaste.cpp : Defines the entry point for the console application.
    //
    
    #define NTDDI_VERSION NTDDI_VISTA
    #define _WIN32_WINNT _WIN32_WINNT_VISTA
    
    #include <sdkddkver.h>
    #include <Ole2.h>
    #include <ShlObj.h>
    
    #include <string>
    #include <memory>
    #include <iostream>
    
    using namespace std;
    
    
    int main()
    {
    	wstring strTarget(L"C:\\Users\\RLWA32\\Documents\\Junk");
    
    	strTarget += L'\0'; //Make sure target path is terminated by double null
    	strTarget += L'\0'; //required by SHFileOperation
    
    	if (SUCCEEDED(OleInitialize(NULL))) //Use OleInitialize for clipboard operations
    	{
    		IDataObject* pData = NULL;
    
    		CLIPFORMAT CF_PREFERRED = RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
    		CLIPFORMAT CF_PASTESUCCEEDED = RegisterClipboardFormat(CFSTR_PASTESUCCEEDED);
    		
    		HRESULT hr = OleGetClipboard(&pData);
    
    		if (pData)
    		{
    			FORMATETC ft = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    			FORMATETC ftdrop = { CF_PREFERRED, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    			FORMATETC ftpaste = { CF_PASTESUCCEEDED, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    
    			STGMEDIUM stg = { 0 }, stgdrop = { 0 }, stgpaste = { 0 };
    			DWORD dwDropEffect = DROPEFFECT_NONE;
    
    			if (SUCCEEDED(pData->GetData(&ftdrop, &stgdrop)))
    			{
    				LPDWORD pDropEffect = (LPDWORD)GlobalLock(stgdrop.hGlobal);
    
    				dwDropEffect = (*pDropEffect & DROPEFFECT_MOVE);
    
    				if (dwDropEffect == DROPEFFECT_NONE)
    					dwDropEffect = (*pDropEffect & DROPEFFECT_COPY);
    
    				GlobalUnlock(stgdrop.hGlobal);
    
    				ReleaseStgMedium(&stgdrop);
    			}
    
    
    			hr = pData->GetData(&ft, &stg);
    
    			if (SUCCEEDED(hr))
    			{
    				wstring strSource;
    				SHFILEOPSTRUCT sfo = { 0 };
    
    				HDROP hdrp = (HDROP)stg.hGlobal;
    
    				UINT nFiles = DragQueryFile(hdrp, -1, NULL, 0);
    
    				for (UINT i = 0; i < nFiles; i++)
    				{
    					unique_ptr<wchar_t> pFileName;
    
    					UINT cch = DragQueryFile(hdrp, i, NULL, 0); // Get path length not including null terminator
    
    					cch++; // Add space for null terminator
    
    					pFileName.reset( new wchar_t[cch]);
    
    					UINT nCopied = DragQueryFile(hdrp, i, pFileName.get(), cch);
    
    					strSource.append(pFileName.get());
    					strSource += L'\0'; 
    				}
    
    				strSource += L'\0';  //Add another null terminator for SHFileOperation
    
    				sfo.fFlags = FOF_NOCONFIRMMKDIR | FOF_ALLOWUNDO;
    				sfo.pFrom = strSource.c_str();
    				sfo.pTo = strTarget.c_str();
    
    				if (dwDropEffect == DROPEFFECT_MOVE)
    					sfo.wFunc = FO_MOVE;
    				else
    					sfo.wFunc = FO_COPY;
    
    				if (SHFileOperation(&sfo) == 0)
    				{
    					// If files were cut to the clipboard using Ctrl+x tell the shell we moved them
    					if (dwDropEffect == DROPEFFECT_MOVE)
    					{
    						stgpaste.tymed = TYMED_HGLOBAL;
    						
    						stgpaste.hGlobal = GlobalAlloc(GPTR, sizeof(DWORD));
    						
    						LPDWORD pEffect = (LPDWORD)stgpaste.hGlobal;
    
    						*pEffect = DROPEFFECT_MOVE;
    
    						hr = pData->SetData(&ftpaste, &stgpaste, TRUE);
    					}
    				}
    				
    				ReleaseStgMedium(&stg);
    			}
    
    			pData->Release();
    		}
    	}
    
    	OleUninitialize();
    
        return 0;
    }
    
    

    • Proposed as answer by Baron Bi Tuesday, August 30, 2016 5:57 AM
    • Marked as answer by Hart WangModerator Wednesday, September 7, 2016 1:57 AM
    Tuesday, August 30, 2016 12:21 AM
  • Thanks for your reply. Attached code works well!

    But SHFileOperation doesn't use "PASTE" operation. You know, it uses copy, move or rename and so on.

    BTW, Is it possible to implement a paste operation without SHFileOperation?

    My goal is only paste operation like "click mouse right-button and select paste menu in explore".

    Thank You.


    Tuesday, August 30, 2016 2:36 AM
  • "Paste" is just a word.  Terms like "cut and paste" refer to the action of copying or moving an item from one location to another and then deleting the source object.  What I have given you is exactly what is recommended in the Microsoft reference about how to handle shell data transfer.  Another way to accomplish the same thing would be to use the IFileOperation interface and work with IShellItem pointers and Pidls. 

    Explorer is doing the same thing behind the scenes in response to context menu commands -- copying or moving shell items and deleting them from the source location if necessary. For example, the reference material says -

    "Remember that the Shell might use optimized moves or delete-on-paste operations when moving files. "



    • Edited by RLWA32 Tuesday, August 30, 2016 4:16 AM added reference
    Tuesday, August 30, 2016 3:51 AM
  • The following functions when invoked from a Windows GUI app (not  console application) will successfully invoke the shell's paste context menu command after files have been copied (Ctrl+c) or cut (Ctrl+x) to the clipboard.  A sample app that demonstrates can be downloaded from Win32Context.zip

    Use Shell Scripting Interfaces -

    void DoPaste2()
    {
    	HRESULT hr;
    	IShellDispatch *pShell = NULL;
    	Folder *pf = NULL;
    	Folder2 *pf2 = NULL;
    	FolderItem *pfi = NULL;
    	FolderItemVerbs *pverbs = NULL;
    	VARIANT vTarget;
    
    	if (SUCCEEDED((hr = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pShell)))))
    	{
    		VariantInit(&vTarget);
    
    		vTarget.vt = VT_BSTR;
    		vTarget.bstrVal = SysAllocString(L"C:\\Users\\RLWA32\\Documents\\Junk");
    
    		if (SUCCEEDED((hr = pShell->NameSpace(vTarget, &pf))))
    		{
    			hr = pf->QueryInterface(IID_PPV_ARGS(&pf2));
    
    			if (pf2)
    			{
    				hr = pf2->get_Self(&pfi);
    
    				if (pfi)
    				{
    					long Count = 0;
    
    					hr = pfi->Verbs(&pverbs);
    
    					hr = pverbs->get_Count(&Count);
    
    					for (long i = 0; i < Count; i++)
    					{
    						FolderItemVerb *pverb = NULL;
    						BSTR bstrName;
    						VARIANT vIndex;
    
    						VariantInit(&vIndex);
    
    						vIndex.vt = VT_I4;
    						vIndex.lVal = i;
    
    						hr = pverbs->Item(vIndex, &pverb);
    
    						hr = pverb->get_Name(&bstrName);
    
    						if (_wcsicmp(bstrName, L"&Paste") == 0)
    						{
    							hr = pverb->DoIt();
    							i = Count;  //Force loop to terminate
    						}
    
    						SysFreeString(bstrName);
    						VariantClear(&vIndex);
    						pverb->Release();
    					}
    
    					pverbs->Release();
    					pfi->Release();
    				}
    
    				pf2->Release();
    			}
    
    			pf->Release();
    		}
    		pShell->Release();
    	}
    }
    
    Use Shell functions/interfaces
    void DoPaste(HWND hwnd)
    {
    	HRESULT hr;
    	LPITEMIDLIST pidl;
    	SFGAOF sfgao;
    
    	if (SUCCEEDED(hr = SHParseDisplayName(L"C:\\Users\\RLWA32\\Documents\\Junk", NULL, &pidl, 0, &sfgao)))
    	{
    		IShellFolder *psf = NULL;
    		IContextMenu *pcm = NULL;
    		LPCITEMIDLIST pidlChild;
    
    		if (SUCCEEDED(hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&psf, &pidlChild)))
    		{
    			if (SUCCEEDED((hr = psf->GetUIObjectOf(hwnd, 1, &pidlChild, IID_IContextMenu, NULL, (void**)&pcm))))
    			{
    				HMENU hMenu = CreatePopupMenu();
    				if (hMenu)
    				{
    					if (SUCCEEDED((hr = pcm->QueryContextMenu(hMenu, 0, 1, 0x7FFF, CMF_NORMAL))))
    					{
    						CMINVOKECOMMANDINFO cmi = { sizeof(CMINVOKECOMMANDINFO) };
    
    						cmi.lpVerb = "paste";
    
    						hr = pcm->InvokeCommand(&cmi);
    					}
    					DestroyMenu(hMenu);
    				}
    				pcm->Release();
    			}
    
    			psf->Release();
    		}
    
    		CoTaskMemFree(pidl);
    	}
    }
    


    • Edited by RLWA32 Wednesday, August 31, 2016 1:59 AM added scripting
    Tuesday, August 30, 2016 10:19 PM