Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possible to specify the start position of an IFileDialog?

I can't find anything in the Microsoft documentation, so I'm just wondering if any one knows, is it possible to specify the starting position for an IFileDialog? Specifically, I'd like the first time the dialog is opened for it to open in the center of the parent window.

I don't see a straightforward way to do this other than somehow hooking into the underlying WM_* messages.

Is it possible to use something like SetWindowPos?

like image 329
erlloyd Avatar asked Nov 20 '12 15:11

erlloyd


2 Answers

The shell's IFileDialog implementation supports the IOleWindow interface (note it does not seem to be documented...). But the dialog must be opened before you can get its window handle.

So the trick is to subscribe to the dialog's events using the IFileDialogEvents interface, get the window handle, and move it, like in the sample below. I choose OnSelectionChange because it seems to be the good place. Of course this is to be adapted (you don't want to move the window each time selection changes...).

class Events : public IFileDialogEvents
{
  // poor man's IUnknown implementation :-)
  STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) { *ppvObject = NULL; return E_NOINTERFACE; }
  STDMETHODIMP_(ULONG) AddRef() { return 1; };
  STDMETHODIMP_(ULONG) Release() { return 1; }
  STDMETHODIMP OnFileOk(IFileDialog *pfd) { return S_OK; }
  STDMETHODIMP OnFolderChanging(IFileDialog *pfd, IShellItem *psiFolder) { return S_OK; }
  STDMETHODIMP OnShareViolation(IFileDialog *pfd, IShellItem *psi, FDE_SHAREVIOLATION_RESPONSE *pResponse) { return S_OK; }
  STDMETHODIMP OnTypeChange(IFileDialog *pfd) { return S_OK; }
  STDMETHODIMP OnOverwrite(IFileDialog *pfd, IShellItem *psi, FDE_OVERWRITE_RESPONSE *pResponse) { return S_OK; }
  STDMETHODIMP OnFolderChange(IFileDialog *pfd) { return S_OK; }

  STDMETHODIMP OnSelectionChange(IFileDialog *pfd) {
    IOleWindow *window;
    if (SUCCEEDED(pfd->QueryInterface(&window)))
    {
      HWND hwnd;
      if (SUCCEEDED(window->GetWindow(&hwnd)))
      {
        MoveWindow(hwnd, 0, 0, 600, 600, FALSE);
      }
      window->Release();
    }
    return S_OK;
  }
};

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
  CoInitialize(NULL);
  IFileOpenDialog *dlg;
  if (SUCCEEDED(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, IID_PPV_ARGS(&dlg))))
  {
    DWORD cookie;
    Events *evts = new Events();
    dlg->Advise(evts, &cookie);
    dlg->Show(NULL);
    dlg->Unadvise(cookie);
    dlg->Release();
    delete evts;
  }
  CoUninitialize();
}
like image 179
Simon Mourier Avatar answered Nov 17 '22 23:11

Simon Mourier


IFileDialog does not provide a way to control the dialog's location, but this can be achieved by subclassing the dialog.

The WM_WINDOWPOSCHANGING message is:

Sent to a window whose size, position, or place in the Z order is about to change as a result of a call to the SetWindowPos function or another window-management function.

A window receives this message through its WindowProc function.

So if we intercept this message, we can change its parameters to point to the location we want, before the window is moved there. We can do this by subclassing the dialog, which changes the dialog's window procedure with our own window procedure. Our custom window procedure may look like this:

LRESULT MyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg == WM_WINDOWPOSCHANGING)
    {
        WINDOWPOS* wp = (WINDOWPOS*)lParam;
        wp->x = 100;
        wp->y = 100;
    }
    return CallWindowProc(owp, hwnd, msg, wParam, lParam);
}

The dialog is subclassed with the SetWindowLong function. However in order to use that function we need to know the dialog's HWND. We can get the dialog's handle, in turn, by setting up a temporary hook with SetWindowsHookEx that would match on the dialog's title, for example, and remove itself upon subclassing the window that matches the given title:

LRESULT CALLBACK GetMsgProc(
    _In_ int    code,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
    )
{
    if (code == HC_ACTION)
    {
        MSG* msg = (MSG*)lParam;
        WCHAR title[120];
        GetWindowText(msg->hwnd, title, sizeof(title));
        if (lstrcmpW(title, L"MyTest01") == 0)
        {
            if (sizeof(long long) == 8)
                owp = (WNDPROC)SetWindowLongPtr(msg->hwnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);
            else
                owp = (WNDPROC)SetWindowLong(msg->hwnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);
            LRESULT r = CallNextHookEx(hhk, code, wParam, lParam);
            UnhookWindowsHookEx(hhk);
            return r;
        }
    }
    return CallNextHookEx(hhk, code, wParam, lParam);
}

The entire example:

#include <windows.h>
#include <shobjidl.h> 

HHOOK hhk = NULL;
WNDPROC owp = NULL;

LRESULT MyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    if (msg == WM_WINDOWPOSCHANGING)
    {
        WINDOWPOS* wp = (WINDOWPOS*)lParam;
        wp->x = 100;
        wp->y = 100;
        (WNDPROC)SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)owp);
    }
    return CallWindowProc(owp, hwnd, msg, wParam, lParam);
}

LRESULT CALLBACK GetMsgProc(
    _In_ int    code,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
    )
{
    if (code == HC_ACTION)
    {
        MSG* msg = (MSG*)lParam;
        WCHAR title[120];
        GetWindowText(msg->hwnd, title, sizeof(title));
        if (lstrcmpW(title, L"MyTestIFileOpenDialog01") == 0)
        {
            if (sizeof(long long) == 8)
                owp = (WNDPROC)SetWindowLongPtr(msg->hwnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);
            else
                owp = (WNDPROC)SetWindowLong(msg->hwnd, GWLP_WNDPROC, (LONG_PTR)MyWndProc);
            LRESULT r = CallNextHookEx(hhk, code, wParam, lParam);
            UnhookWindowsHookEx(hhk);
            return r;
        }
    }
    return CallNextHookEx(hhk, code, wParam, lParam);
}

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED |
        COINIT_DISABLE_OLE1DDE);
    if (SUCCEEDED(hr))
    {
        IFileOpenDialog *pFileOpen;

        // Create the FileOpenDialog object.
        hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL,
            IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));

        if (SUCCEEDED(hr))
        {
            hhk = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, NULL, GetCurrentThreadId());
            pFileOpen->SetTitle(L"MyTestIFileOpenDialog01");
            // Show the Open dialog box.
            hr = pFileOpen->Show(NULL);

            // Get the file name from the dialog box.
            if (SUCCEEDED(hr))
            {
                IShellItem *pItem;
                hr = pFileOpen->GetResult(&pItem);
                if (SUCCEEDED(hr))
                {
                    PWSTR pszFilePath;
                    hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);

                    // Display the file name to the user.
                    if (SUCCEEDED(hr))
                    {
                        MessageBox(NULL, pszFilePath, L"File Path", MB_OK);
                        CoTaskMemFree(pszFilePath);
                    }
                    pItem->Release();
                }
            }
            pFileOpen->Release();
        }
        CoUninitialize();
    }
    return 0;
}
like image 26
mnistic Avatar answered Nov 18 '22 01:11

mnistic