views:

88

answers:

0

Please keep in mind that is is my first post.

Summary

In a program, I need to be able to retrieve the FileName associated with a FileDialog by intercepting Windows messages without access to the dialog object creating the dialog.

Background

I have an unusual problem concerning dialogs in .NET 3.0 with C#. Currently, I am working on an application extension that is capable of chronicling all user actions associated with the main application.

For instance, if a user clicks on a button, the application extension will retrieve the button object, the action performed, and add it to a chronicle. The user can then use the application extension to automate this action much like a real-time playback.

The Issue And My Approach

For the most part this works as long as the control interacted with by the user is owned by the application. However, this causes problems when the user interacts with dialogs, especially with Open(Save)FileDialogs since they are not owned -let alone known- by the current application.

Through research, I found that Dialogs interact with the applications that created them by sending unmanaged messages, the most significant being WM_NOTIFY which is sent when the dialog needs to update another window and contains the dialog's selected file name. Keeping this in mind, I created a Hook and Callback to intercept Windows messages:

private DialogProc _dialogCallback; 
private IntPtr _dialogHookID;
...
...
private delegate IntPtr DialogProc(int hdlg, IntPtr wParam, IntPtr lParam);

private IntPtr SetDialogHook(DialogProc proc)
{
    return SetWindowsHookEx(HookType.WH_MSGFILTER, proc, IntPtr.Zero, GetCurrentThreadId());
}

Here is the documentation: SetWindowsHookEx

Of course I didn't want to intercept all messages. Luckily, I can assign the HookTpye to be WH_MSGFILTER. According to MSDN:

The WH_MSGFILTER and WH_SYSMSGFILTER hooks enable you to monitor messages about to be processed by a menu, scroll bar, message box, or dialog box... [Hooks Overview]

This Hook seems to do it's job. Upon the creation of a dialog (any dialog) we find ourselves stepping into the callback:

private IntPtr DialogHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode == 0)
            {
                MSG msg = (MSG)Marshal.PtrToStructure(lParam, typeof(MSG));

                switch (msg.MESSAGE)
                {
                    case WM_NOTIFY:
                        OFNOTIFY ofNotify = (OFNOTIFY)Marshal.PtrToStructure(msg.lParam, typeof(OFNOTIFY));
                        OPENFILENAME ofn = (OPENFILENAME)Marshal.PtrToStructure(ofNotify.OPENFILENAME, typeof(OPENFILENAME));
                        fileName = ofn.lpstrFile;
                        break;
                    case WM_USER:
                        break;
                }
            }
            return CallNextHookEx(_dialogHookID, nCode, wParam, lParam);
        }

As you can see, we get a pointer to a MSG structure by looking at the lParam pointer. The MSG struct is defined as:

[StructLayout(LayoutKind.Sequential)]
        private struct MSG
        {
            public IntPtr HWND;
            public uint MESSAGE;
            public IntPtr wParam;
            public IntPtr lParam;
            public uint time;
            public POINT pt;
        }

MSG.MESSAGE contains the message sent from the dialog to the main application and the code is setup to handle this message. Yet, the correct message is never received. Instead of the dialog sending WM_NOTIFY, it sends WM_USER which is user defined, and thus useless since I do not know how it is defined.

WM_USER does contain a pointer that can be cast to a OFNOTIFY, but this is wrong and it does not contain the information I need anyways.

New Approach

Any ideas about how to refactor my code, or any possible solutions would be greatly appreciated. I have tried a few other approaches such as getting the control from a handle to no avail and am out of things to try.