Search code examples
winapicomiconsdesktopwindows-shell

windows desktop new folder c++


Purpose : New folder creation function in Windows Explorer (Desktop) (Icon coordinate setting + label correction)

I am wondering how to create a new folder like Windows Explorer (Desktop).

I want to know the same function as "Right click on desktop → New → Folder".

What I want is to make it to specific coordinates.

++ It would be better if you could also modify the label.

This is how I did it.

CreateDirectory → FindWindow (Desktop Listview) → SendMessage (LVM_SETITEMPOSITION)

But when I do CreateDirectory it appears too slow on the desktop. (Default 1000ms or more)

So I tried SHChangeNotify but it appears slow.

That source code functions to move the mouse to a specific location on the desktop and press a shortcut to create a folder with that coordinate. However, I can see the folder appearing slow and moving (SendMessage (LVM_SETITEMPOSITION)).

// this is Console Application
HWND hDesk = FindWindow(NULL, L"Program Manager");
HWND hDesk2 = FindWindowEx(hDesk, NULL, L"SHELLDLL_DefView", NULL);
HWND hListView = FindWindowEx(hDesk2, NULL, L"SysListView32", NULL);

int iconCount = (int)SendMessage(hListView, LVM_GETITEMCOUNT, NULL, NULL);

printf("컨트롤(Control) + F2\n");
RegisterHotKey(NULL, 1, MOD_CONTROL, VK_F2);

MSG msg;
while (GetMessage(&msg, 0, 0, 0))
{
    PeekMessage(&msg, 0, 0, 0, 0x0001);
    switch (msg.message)
    {
    case WM_HOTKEY:
        if (msg.wParam == 1)
        {
            printf("ㅇㅋ 누름 (Hotkey Event)\n");
            int a = SendMessage(hListView, LVM_GETITEMCOUNT, 0, 0);
            int b;
            int ms = GetTickCount();
            CreateDirectory(L"C:\\Users\\root\\Desktop\\new Folder", NULL);
            //SendMessage(hListView, LVM_REDRAWITEMS, 0, a+5);
            //SendMessage(hListView, LVM_UPDATE, a, 0);
            //SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST| SHCNF_PATHW, L"C:\\Users\\root\\Desktop", NULL);
            while (true)
            {
                b = SendMessage(hListView, LVM_GETITEMCOUNT, 0, 0);
                if (a != b) {
                    ms = GetTickCount() - ms;
                    break;
                }
            }
            Sleep(1);
            POINT mouse;
            GetCursorPos(&mouse);
            SendMessage(hListView, LVM_SETITEMPOSITION, b - 1, MAKEWPARAM(mouse.x, mouse.y));
            SendMessage(hListView, LVM_EDITLABEL, b - 1, 0);

        }

    }
}

I thought CreateDirectory was the problem and tried to create a directory using NtCreateFile (User Space / Ring3), but it is still slow. The icon still appears slow on the desktop.

Is there WinAPI / DesktopAPI / COM / ATL for this?


Solution

  • Here is a code that simulates what's the New... Folder menu does from a Console Application.

    It opens a view on some folder and create a child folder in it, and enter name edit mode. It's using the Shell's IFileOperation interface and also ATL's smart pointers only for simplicity.

    int main()
    {
      CoInitialize(NULL);
      {
        CComHeapPtr<ITEMIDLIST> pidl;
        CComHeapPtr<ITEMIDLIST> newFolderPidl;
        CComPtr<IFileOperationProgressSink> sink;
        CComPtr<IFileOperation> fo;
        CComPtr<IShellItem> folder;
        LPCITEMIDLIST pidls = { nullptr };
        CFileOperationProgressSink csink;
    
        // get some folder's PIDL
        HRESULT hr = SHParseDisplayName(L"c:\\temp", NULL, &pidl, 0, NULL);
        if (SUCCEEDED(hr))
        {
          // open it and, for demo purposes here, selects nothing (pass a terminator PIDL)
          ITEMIDLIST idl = { 0 };
          pidls = { &idl };
          hr = SHOpenFolderAndSelectItems(pidl, 1, &pidls, 0);
          if (SUCCEEDED(hr))
          {
            // get a Shell Item from this PIDL
            hr = SHCreateItemFromIDList(pidl, IID_PPV_ARGS(&folder));
          }
        }
    
        if (SUCCEEDED(hr))
        {
          // we want to operate on files and directory using Shell's API
          hr = fo.CoCreateInstance(CLSID_FileOperation);
          if (SUCCEEDED(hr))
          {
            csink.QueryInterface(IID_PPV_ARGS(&sink));
    
            // create the new folder "New Folder", using the sink
            // we need a sink to be advised of what really is the new folder
            // we can't use the name we've passed
            // because it could have been changed like 'New Folder (2)' if there's already a 'New Folder', etc.
            hr = fo->NewItem(folder, FILE_ATTRIBUTE_DIRECTORY, L"New Folder", NULL, sink);
            if (SUCCEEDED(hr))
            {
              // commit
              hr = fo->PerformOperations();
            }
          }
        }
    
        if (SUCCEEDED(hr))
        {
          // we want the new child's relative-to-the-parent PIDL
          CComPtr<IParentAndItem> pai;
          hr = csink.m_newItem->QueryInterface(&pai);
          if (SUCCEEDED(hr))
          {
            hr = pai->GetParentAndItem(NULL, NULL, &newFolderPidl);
          }
        }
    
        if (SUCCEEDED(hr))
        {
          // now, select the new child and get into name-edit mode (from the parent again)
          pidls = { newFolderPidl };
          hr = SHOpenFolderAndSelectItems(pidl, 1, &pidls, OFASI_EDIT);
        }
      }
    
      CoUninitialize();
      return 0;
    }
    
    // the sink. this implementation's only interested by PostNewItem
    class CFileOperationProgressSink : public IFileOperationProgressSink
    {
      LONG m_cRef;
    
    public:
      CComPtr<IShellItem> m_newItem;
    
      CFileOperationProgressSink() : m_cRef(1)
      {
      }
    
      STDMETHOD(QueryInterface)(REFIID riid, void** ppv)
      {
        static const QITAB qit[] =
        {
          QITABENT(CFileOperationProgressSink, IFileOperationProgressSink),
          { 0 },
        };
        return QISearch(this, qit, riid, ppv);
      }
    
      STDMETHOD_(ULONG, AddRef)() { return InterlockedIncrement(&m_cRef); }
      STDMETHOD_(ULONG, Release)()
      {
        if (InterlockedDecrement(&m_cRef))
          return m_cRef;
    
        delete this;
        return 0;
      }
    
      STDMETHOD(StartOperations)() { return S_OK; };
      STDMETHOD(FinishOperations)(HRESULT hrResult) { return S_OK; };
      STDMETHOD(PreRenameItem)(DWORD dwFlags, IShellItem* psiItem, LPCWSTR pszNewName) { return S_OK; };
      STDMETHOD(PostRenameItem)(DWORD dwFlags, IShellItem* psiItem, LPCWSTR pszNewName, HRESULT hrRename, IShellItem* psiNewlyCreated) { return S_OK; };
      STDMETHOD(PreMoveItem)(DWORD dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, LPCWSTR pszNewName) { return S_OK; };
      STDMETHOD(PostMoveItem)(DWORD dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, LPCWSTR pszNewName, HRESULT hrMove, IShellItem* psiNewlyCreated) { return S_OK; };
      STDMETHOD(PreCopyItem)(DWORD dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, LPCWSTR pszNewName) { return S_OK; };
      STDMETHOD(PostCopyItem)(DWORD dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, LPCWSTR pszNewName, HRESULT hrCopy, IShellItem* psiNewlyCreated) { return S_OK; };
      STDMETHOD(PreDeleteItem)(DWORD dwFlags, IShellItem* psiItem) { return S_OK; };
      STDMETHOD(PostDeleteItem)(DWORD dwFlags, IShellItem* psiItem, HRESULT hrDelete, IShellItem* psiNewlyCreated) { return S_OK; };
      STDMETHOD(PreNewItem)(DWORD dwFlags, IShellItem* psiDestinationFolder, LPCWSTR pszNewName) { return S_OK; };
      STDMETHOD(PostNewItem)(DWORD dwFlags, IShellItem* psiDestinationFolder, LPCWSTR pszNewName, LPCWSTR pszTemplateName, DWORD dwFileAttributes, HRESULT hrNew, IShellItem* psiNewItem)
      {
        if (SUCCEEDED(hrNew))
        {
          psiNewItem->QueryInterface(&m_newItem);
        }
        return S_OK;
      }
      STDMETHOD(UpdateProgress)(UINT iWorkTotal, UINT iWorkSoFar) { return S_OK; };
      STDMETHOD(ResetTimer)() { return S_OK; };
      STDMETHOD(PauseTimer)() { return S_OK; };
      STDMETHOD(ResumeTimer)() { return S_OK; };
    };