🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: borland.public.cppbuilder.nativeapi
10 messages
10 total messages Started by Dennis Jones Mon, 06 Mar 2006 23:36
Positioning the SHBrowseForFolder window
#4208
Author: Dennis Jones
Date: Mon, 06 Mar 2006 23:36
20 lines
1712 bytes
Hi,

I would like to center the SHBrowseForFolder window over the window that it
was called from.  I have code that can position the window, but I cannot
figure out where and when to execute that code.

The problem is that to correctly position the window, I must know its size
first.  Unfortunately, the browse window always opens to its minimum size,
and then updates its size only after all of the initialization has occured.
Thus, if I try to execute my centering code from anywhere within the
BrowseCallbackProc function, it is always too early because the window's
size has not been updated yet.

I was hoping I could post a custom message that would be received by
BrowseCallbackProc, but I don't see an obvious way to do that.

Does anyone know how I can execute some custom code after the browse window
has been displayed and initialized?

- Dennis
Re: Positioning the SHBrowseForFolder window
#4209
Author: Remy Lebeau (TeamB)
Date: Tue, 07 Mar 2006 00:54
79 lines
3377 bytes
"Dennis Jones" <nospam@nospam.com> wrote in message
news:440cc788$1@newsgroups.borland.com...

> I would like to center the SHBrowseForFolder window
> over the window that it was called from.

You need to provide a callback function in the BROWSEINFO structure.

> The problem is that to correctly position the window, I must know
> its size first.  Unfortunately, the browse window always opens to its
> minimum size, and then updates its size only after all of the
initialization
> has occured.

The callback function receives the BFFM_INITIALIZED notification when the
dialog has been fully initialized.  You can then query the dialog's current
size and then re-position it as needed.

> Thus, if I try to execute my centering code from anywhere within the
> BrowseCallbackProc function, it is always too early because the
> window's size has not been updated yet.

Works fine when I use it.

> I was hoping I could post a custom message that would be received
> by BrowseCallbackProc, but I don't see an obvious way to do that.

You cannot post custom messages to that dialog.

> Does anyone know how I can execute some custom code after
> the browse window has been displayed and initialized?

Use the BFFM_INITIALIZED notification.  For example:

    int __stdcall BrowseCallbackProc(HWND hDlg, UINT uMsg, LPARAM lParam,
LPARAM lpData)
    {
        if( uMsg == BFFM_INITIALIZED )
        {
            TForm1 *pForm = reinterpret_cast<TForm1*>(lpData);

            RECT r = {0};
            ::GetWindowRect(hDlg, &r);

            ::SetWindowPos(
                hDlg,
                NULL,
                pForm->Left + (pForm->Width/2) - ((r.right-r.left)/2),
                pForm->Top + (pForm->Height/2) - ((r.bottom-r.top)/2),
                0,
                0,
                SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
        }
        return 0;
    }

    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
        TCHAR buffer[MAX_PATH+1];

        BROWSEINFO bi = {0};
        bi.hwndOwner = Handle;
        bi.pszDisplayName = buffer;
        bi.lpszTitle = "";
        bi.ulFlags = BIF_RETURNONLYFSDIRS;
        bi.lpfn = BrowseCallbackProc;
        bi.lParam = reinterpret_cast<int>(this);

        LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
        if( pidl )
        {
            //...
            CoTaskMemFree(pidl);
        }
    }


Gambit
Re: Positioning the SHBrowseForFolder window
#4210
Author: Dennis Jones
Date: Tue, 07 Mar 2006 03:59
142 lines
5491 bytes
"Remy Lebeau (TeamB)" <no.spam@no.spam.com> wrote in message
news:440cd987$1@newsgroups.borland.com...

Remy,

Thank you for your reply.  However...  ;-)


> The callback function receives the BFFM_INITIALIZED notification when the
> dialog has been fully initialized.  You can then query the dialog's
current
> size and then re-position it as needed.

Yes, but my point is that the size that is returned for the window is NOT
correct.  Please read on...


> > Thus, if I try to execute my centering code from anywhere within the
> > BrowseCallbackProc function, it is always too early because the
> > window's size has not been updated yet.
>
> Works fine when I use it.

Yes, of course it does, because you failed to set the BIF_NEWDIALOGSTYLE
flag, without which you cannot resize the browse window!  If I couldn't
resize the browse window, I wouldn't be having any trouble either!  The
whole point of this discussion was to find out why it won't restore to its
previous size *before* I need to center it.  So, try setting the
BIF_NEWDIALOGSTYLE flag in your code first (note: you will need to use
CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ) for that flag to work),
then do the following:

Resize the browse window (any arbitrarily large size will do -- stretch it
horizontally, for example), then close the window, then re-open it.  It will
not re-open centered on the desired form.  It will always get positioned as
if the browse window were opened at its (apparently) default size at the
time that it gets centered.  Then after it gets resized, it appears offset
from the desired form, after which the BrowseCallbackProc function will not
be called again, thereby preventing any opportunity to re-position the
window using its restored size.

Your sample code is very similar to mine, with the exception that I don't
pass the form pointer in bi.lParam because I need it for the initial
directory (thus, the form's handle comes from elsewhere) and, of course, the
*necessary* BIF_NEWDIALOGSTYLE flag.   Here is my code:

(And since I know how you think, Remy, I'm gonna head you off at the pass --
forget about the call to CenterWindowTo() -- it is not at fault.  You will
find that your centering code will not work as expected either.)

int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM /*lParam*/,
LPARAM lpData)
{
  LPITEMIDLIST  pidl = NULL;

  SHPathToPIDL( (char *)lpData, &pidl );

  switch (uMsg) {
    case BFFM_INITIALIZED:
      ::SendMessage(hwnd, BFFM_SETSELECTION, false, (LPARAM)pidl);

      CenterWindowTo( hwnd, TFileMonitorDialog::hBrowseOwner );
      break;
  }

  // Global pointer to the shell's IMalloc interface.
  LPMALLOC g_pMalloc;

  // Get the shell's allocator.
  if (!SUCCEEDED(SHGetMalloc(&g_pMalloc)))
    return false;

  g_pMalloc->Free(pidl);

  return 0;
}

bool BrowseForFolder(HWND hWnd, char *buf, char *prompt)
{
  // Global pointer to the shell's IMalloc interface.
  LPMALLOC g_pMalloc = NULL;

  // Get the shell's allocator.
  if (!SUCCEEDED(SHGetMalloc(&g_pMalloc)))
    return false;

  LPITEMIDLIST pidlMyComputer = NULL;  // PIDL for MyComputer folder

  // Get the PIDL for the Desktop folder.
  if (!SUCCEEDED(SHGetSpecialFolderLocation(hWnd, CSIDL_DESKTOP,
&pidlMyComputer)))
    return false;

  HRESULT hResult = CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );  //
required for BIF_NEWDIALOGSTYLE
  if ( hResult != S_OK && hResult != S_FALSE )
  {
    // Free the PIDL returned by SHGetSpecialFolderLocation
    g_pMalloc->Free(pidlMyComputer);
    pidlMyComputer = NULL;

    return false;
  }

  BROWSEINFO bi;
  memset( &bi, 0, sizeof(bi) );

  // Fill in the BROWSEINFO structure.
  bi.hwndOwner = hWnd;
  bi.pidlRoot = pidlMyComputer;
  bi.pszDisplayName = buf;
  bi.lpszTitle = prompt;
  bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_RETURNFSANCESTORS |
BIF_NEWDIALOGSTYLE;
  bi.lpfn = BrowseCallbackProc;
  bi.lParam = (LPARAM)buf;

  // Browse for a folder and return its PIDL.
  LPITEMIDLIST pidlBrowse = SHBrowseForFolder(&bi);

  CoUninitialize();

  bool result = (pidlBrowse != 0);
  if (result)
  {
    if (!SHGetPathFromIDList(pidlBrowse, buf))
      result = false;

    // Free the PIDL returned by SHBrowseForFolder.
    g_pMalloc->Free(pidlBrowse);
    pidlBrowse = NULL;
  }

  // Free the PIDL returned by SHGetSpecialFolderLocation
  g_pMalloc->Free(pidlMyComputer);
  pidlMyComputer = NULL;

  return result;
}

- Dennis
Re: Positioning the SHBrowseForFolder window
#4213
Author: JD
Date: Tue, 07 Mar 2006 06:05
29 lines
1738 bytes
"Dennis Jones" <nospam@nospam.com> wrote:
>
> [...] my point is that the size that is returned for the
> window is NOT correct.

Confirmed.

>> > Thus, if I try to execute my centering code from anywhere within the
>> > BrowseCallbackProc function, it is always too early because the
>> > window's size has not been updated yet.

Confirmed.

> [...] BIF_NEWDIALOGSTYLE flag [...] note: you will need to
> use CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ) for
> that flag to work

I do *not* need to.

> [...] Then after it gets resized, it appears offset from the
> desired form, after which the BrowseCallbackProc function
> will not be called again, thereby preventing any opportunity
> to re-position the window using its restored size.

I use AllocateHWnd and then use PostMessage when I detect
BFFM_INITIALIZED and it works for me.

~ JD
Re: Positioning the SHBrowseForFolder window
#4212
Author: Remy Lebeau (TeamB)
Date: Tue, 07 Mar 2006 07:03
159 lines
5729 bytes
"Dennis Jones" <nospam@nospam.com> wrote in message
news:440d053b$1@newsgroups.borland.com...

> Yes, of course it does, because you failed to set the
> BIF_NEWDIALOGSTYLE flag, without which you
> cannot resize the browse window!

You never said anything earlier about doing that.  Next time you want
someone to reproduce your problem, always provide specific details about
what you are actually doing in the first place.

> If I couldn't resize the browse window, I wouldn't be having any trouble
> either!  The whole point of this discussion was to find out why it won't
> restore to its previous size *before* I need to center it.  So, try
> setting the BIF_NEWDIALOGSTYLE flag in your code first

Using window hooks and subclassing, you can catch when the window is
becoming visible and make it centered before it is shown onscreen.

The following code works for me (yes, I realize that this is a bit complex
for the task, but it works nontheless):


    #ifdef STRICT
    #define MYDLGWNDPROC WNDPROC
    #else
    #define MYDLGWNDPROC FARPROC
    #endif

    MYDLGWNDPROC OldDlgProc = NULL;
    HWND hFolderDlg = NULL;
    HHOOK hHook = NULL;

    LRESULT CALLBACK DlgWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM
lParam)
    {
        if( uMsg == WM_SHOWWINDOW )
        {
            RECT r;
            ::GetWindowRect(hwnd, &r);

            int iWidth = (r.right-r.left);
            int iHeight = (r.bottom-r.top);

            ::MoveWindow(
                hwnd,
                Form1->Left + (Form1->Width/2)-(iWidth/2),
                Form1->Top + (Form1->Height/2)-(iHeight/2),
                iWidth,
                iHeight,
                FALSE);
        }

        return ::CallWindowProc(OldDlgProc, hwnd, uMsg, wParam, lParam);
    }

    LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
        if( nCode == HCBT_CREATEWND )
        {
            HWND hDlg = (HWND) wParam;
            CBT_CREATEWND *lpcw = (CBT_CREATEWND*) lParam;

            if( lpcw->lpcs->hwndParent == Form1->Handle )
            {
                hFolderDlg = hDlg;
                OldDlgProc = (DLGWNDPROC) ::SetWindowLong(hDlg, GWL_WNDPROC,
(LONG) DlgWndProc);
            }
        }

        else if( nCode == HCBT_DESTROYWND )
        {
            if( (hFolderDlg) && (hFolderDlg == (HWND) wParam) )
            {
                ::SetWindowLong(hFolderDlg, GWL_WNDPROC, (LONG) OldDlgProc);
                hFolderDlg = NULL;
            }
        }

        return ::CallNextHookEx(hHook, nCode, wParam, lParam);
    }

    int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM /*lParam*/,
LPARAM lpData)
    {
        if( uMsg == BFFM_INITIALIZED )
        {
            LPITEMIDLIST pidl = NULL;
            SHPathToPIDL((char*) lpData, &pidl);
            if( pidl )
            {
                ::SendMessage(hwnd, BFFM_SETSELECTION, FALSE, (LPARAM)
pidl);
                ::CoTaskMemFree(pidl);
            }
        }

        return 0;
    }

    bool BrowseForFolder(HWND hWnd, char *buf, char *prompt)
    {
        bool result = false;

        LPITEMIDLIST pidlDrives = NULL;
        if( SUCCEEDED(::SHGetSpecialFolderLocation(hWnd, CSIDL_DRIVES,
&pidlDrives)) )
        {
            if( SUCCEEDED(::CoInitialize(NULL)) )
            {
                hHook = ::SetWindowsHookEx(WH_CBT, (HOOKPROC) CBTProc, NULL,
::GetCurrentThreadId());
                if( hHook )
                {
                    BROWSEINFO bi = {0};
                    bi.hwndOwner = hWnd;
                    bi.pidlRoot = pidlDrives;
                    bi.pszDisplayName = buf;
                    bi.lpszTitle = prompt;
                    bi.ulFlags = BIF_RETURNONLYFSDIRS |
BIF_RETURNFSANCESTORS | BIF_NEWDIALOGSTYLE;
                    bi.lpfn = BrowseCallbackProc;
                    bi.lParam = (LPARAM) buf;

                    LPITEMIDLIST pidlBrowse = ::SHBrowseForFolder(&bi);
                    if( pidlBrowse )
                    {
                        result = ::SHGetPathFromIDList(pidlBrowse, buf);
                        ::CoTaskMemFree(pidlBrowse);
                    }

                    ::UnhookWindowsHookEx(hHook);
                    hHook = NULL;
                }

                ::CoUninitialize();
            }

            ::CoTaskMemFree(pidlDrives);
        }

        return result;
    }

    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
        char buf[MAX_PATH+1];
        strcpy(buf, "C:\\Windows");

        if( BrowseForFolder(Handle, buf, "Testing") )
            ShowMessage(buf);
        else
            ShowMessage("none");
    }


Gambit
Re: Positioning the SHBrowseForFolder window
#4215
Author: Dennis Jones
Date: Tue, 07 Mar 2006 15:59
25 lines
1903 bytes
"Remy Lebeau (TeamB)" <no.spam@no.spam.com> wrote in message
news:440d2feb$1@newsgroups.borland.com...
>
> You never said anything earlier about doing that.  Next time you want
> someone to reproduce your problem, always provide specific details about
> what you are actually doing in the first place.


No, I guess I wasn't explicit enough.


> Using window hooks and subclassing, you can catch when the window is
> becoming visible and make it centered before it is shown onscreen.
>
> The following code works for me (yes, I realize that this is a bit complex
> for the task, but it works nontheless):

<code snipped>

Yes, a little complex for the task, but it does demonstrate a useful
technique.  So thanks for that.  However, I think I'll try JD's simpler
suggestion first and see how that goes.

- Dennis
Re: Positioning the SHBrowseForFolder window
#4218
Author: Dennis Jones
Date: Tue, 07 Mar 2006 16:32
33 lines
1978 bytes
"JD" <nospam@nospam.com> wrote in message
news:440d30ca@newsgroups.borland.com...
>
> "Dennis Jones" <nospam@nospam.com> wrote:
> >
> > [...] my point is that the size that is returned for the
> > window is NOT correct.
>
> Confirmed.
>
> >> > Thus, if I try to execute my centering code from anywhere within the
> >> > BrowseCallbackProc function, it is always too early because the
> >> > window's size has not been updated yet.
>
> Confirmed.
>
> > [...] BIF_NEWDIALOGSTYLE flag [...] note: you will need to
> > use CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ) for
> > that flag to work
>
> I do *not* need to.

Hmm.  I don't seem to need it now.  I wonder if it was necessary on Windows
NT 4 or 2000  (I stuck with NT 4 until just about 2 years ago).


> I use AllocateHWnd and then use PostMessage when I detect
> BFFM_INITIALIZED and it works for me.

Thank you, JD, it is a very nice solution.  Works perfectly.

- Dennis
Re: Positioning the SHBrowseForFolder window
#4217
Author: Dennis Jones
Date: Tue, 07 Mar 2006 16:35
40 lines
2498 bytes
"JD" <nospam@nospam.com> wrote in message
news:440d30ca@newsgroups.borland.com...
>
> "Dennis Jones" <nospam@nospam.com> wrote:
> >
> > [...] my point is that the size that is returned for the
> > window is NOT correct.
>
> Confirmed.
>
> >> > Thus, if I try to execute my centering code from anywhere within the
> >> > BrowseCallbackProc function, it is always too early because the
> >> > window's size has not been updated yet.
>
> Confirmed.
>
> > [...] BIF_NEWDIALOGSTYLE flag [...] note: you will need to
> > use CoInitializeEx( NULL, COINIT_APARTMENTTHREADED ) for
> > that flag to work
>
> I do *not* need to.

From MSDN:

BIF_NEWDIALOGSTYLE
Version 5.0. Use the new user interface. Setting this flag provides the user
with a larger dialog box that can be resized. The dialog box has several new
capabilities including: drag-and-drop capability within the dialog box,
reordering, shortcut menus, new folders, delete, and other shortcut menu
commands. To use this flag, you must call OleInitialize or CoInitialize
before calling SHBrowseForFolder.

I know very little about these SHxxx functions.  But I have noticed that
sometimes CoInitialize is needed, and other times it isn't (as if it has
already been called by some other code I don't have control over).  I
suspect if it hasn't been called already by some library you are using, that
you will have to call it yourself.

- Dennis
Re: Positioning the SHBrowseForFolder window
#4221
Author: JD
Date: Wed, 08 Mar 2006 10:33
18 lines
1569 bytes
"Dennis Jones" <nospam@nospam.com> wrote:
>
>From MSDN:
>
> [...] To use this flag, you must call OleInitialize or
> CoInitialize before calling SHBrowseForFolder.
>
> [...] I suspect if it hasn't been called already by some
> library you are using, that you will have to call it
> yourself.

Andrue said exactly the same thing (did you google?) but I
couldn't get it to fail. I know ... just because it didn't
break doesn't mean that it ain't broke. Besides, MS has
always had reliable docs ... right?

~ JD
Re: Positioning the SHBrowseForFolder window
#4227
Author: Dennis Jones
Date: Wed, 08 Mar 2006 16:32
38 lines
2604 bytes
"JD" <nospam@nospam.com> wrote in message
news:440ec104$1@newsgroups.borland.com...
>
> "Dennis Jones" <nospam@nospam.com> wrote:
> >
> >From MSDN:
> >
> > [...] To use this flag, you must call OleInitialize or
> > CoInitialize before calling SHBrowseForFolder.
> >
> > [...] I suspect if it hasn't been called already by some
> > library you are using, that you will have to call it
> > yourself.
>
> Andrue said exactly the same thing (did you google?)

Huh?

> but I
> couldn't get it to fail. I know ... just because it didn't
> break doesn't mean that it ain't broke. Besides, MS has
> always had reliable docs ... right?

I don't remember the details (it's been a couple years since I wrote that
code), but I do remember that sometimes it was needed and other times it
wasn't.  So, to be safe, I included the CoInitialize call because I couldn't
be certain when it wasn't required, and it didn't seem to hurt anything if
it was called redundantly.  IIRC, whether it was needed was dependent on
whether or not some other API or library call(s) had been made beforehand
(or maybe if some certain DLL had been loaded by another application in the
system), but I have no recollection of what might have initialized things to
the point where I didn't need it.  Also, when I wrote that code, I was still
using NT 4, so it could also have been an artifact of using that OS; whereas
it might never be needed in XP (which I presume most people use nowadays) --
I just don't know.

- Dennis
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads