0

I'm developing a custom address dialog for Outlook 2010, following the sample of Helmut Oberdan published here

http://www.codeproject.com/Articles/21288/Customize-the-built-in-Outlook-Select-Names-dialog

I've migrated the project to VS2015 with framework 4.5 but I'm in trouble with the findwindow function

IntPtr hBuiltInDialog = WinApiProvider.FindWindow("#32770", "");

on some computer (mine) works well and on some other (customer) it doesn't. It seems that the function finds another 32770 window that is not the Outlook's one, I've tryed to enumerate all the 32770 windows but when the InspectorWrapper_Deactivate function fires up, my 32770 window is not present in the list. A strange behavior occurs, if I put a messagebox in the deactivate function it pops up twice and after the second time the right window gets catched and my custom dialog opens.

Here is the InspectorWrapper_Deactivate function

void InspectorWrapper_Deactivate()
{
    _showOwnDialogOnActivate = false;

    // If there is an invisible ghost Window out there - close it
    if (_hWndInvisibleWindow != IntPtr.Zero) WinApiProvider.SendMessage(_hWndInvisibleWindow, WinApiProvider.WM_SYSCOMMAND, WinApiProvider.SC_CLOSE, 0);

    IntPtr hBuiltInDialog = WinApiProvider.FindWindow("#32770", "");
    if (hBuiltInDialog != IntPtr.Zero)
    {
        // ok, found one
        // let's see what childwindows are there
        List<IntPtr> childWindows = WinApiProvider.EnumChildWindows(hBuiltInDialog);
        // Let's get a list of captions for the child windows
        List<string> childWindowNames = WinApiProvider.GetWindowNames(childWindows);

        //MessageBox.Show("Contact");

        // now check some criteria to identify the build in dialog..
        int languageId = Inspector.Application.LanguageSettings.get_LanguageID(Microsoft.Office.Core.MsoAppLanguageID.msoLanguageIDUI);
        switch (languageId)
        {
            case 1031:
                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                // !!! This part is only valid for German Outlook 2007 Version !!!
                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                if (!childWindowNames.Contains("Nur N&ame")) return;
                if (!childWindowNames.Contains("&Mehr Spalten")) return;
                if (!childWindowNames.Contains("A&dressbuch")) return;
                // you can even check more criteria
                break;

            case 1033:
                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                // !!! This part is only valid for english Outlook 2007 Version !!!
                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                if (!childWindowNames.Contains("&Name only")) return;
                if (!childWindowNames.Contains("Mo&re columns")) return;
                if (!childWindowNames.Contains("A&ddress Book")) return;
                break;

            case 1040:
                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                // !!! This part is only valid for italian Outlook 2007 Version !!!
                // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
                if (!childWindowNames.Contains("Solo n&ome")) return;
                if (!childWindowNames.Contains("Altre &colonne")) return;
                if (!childWindowNames.Contains("R&ubrica")) return;
                break;
            // TODO: place your language here....

            default:
                return;
        }

        // OK - we have the built in Recipient Dialog
        // Create a new invisible window
        _hWndInvisibleWindow = WinApiProvider.CreateWindowEx(0, "Static", "BriaSOFT", 0, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); 

        // use this window as new Parent for the original Dialog
        WinApiProvider.SetParent(hBuiltInDialog, _hWndInvisibleWindow);

        WinApiProvider.SendMessage(hBuiltInDialog, WinApiProvider.WM_SYSCOMMAND, WinApiProvider.SC_CLOSE, 0);
        // When our INspector becomes active again, we should show our own Dialog
        _showOwnDialogOnActivate = true;
    }
}

any suggestion will be really appreciate. Thanks Flavio

Following Dmitry suggestion but still not able to find the 32770 window, because it has empty window name (even if it exists)

    string windowName;
    IntPtr outlookHandle = (IntPtr)0;
    IOleWindow window = Inspector as IOleWindow;
    if (window != null)
    {
        window.GetWindow(out outlookHandle);
        List<IntPtr> subWindows = WinApiProvider.EnumChildWindows(outlookHandle);
        foreach (IntPtr hand in subWindows)
        {
            StringBuilder ClassName = new StringBuilder(256);
            int nRet = WinApiProvider.GetClassName(hand, ClassName, ClassName.Capacity);
            if (nRet != 0)
            {
                if (ClassName.ToString().Contains("#32770"))
                {
                    windowName = WinApiProvider.GetWindowName(hand);
                    if (windowName.Contains("Seleziona nomi"))
                    {
                        hBuiltInDialog = hand;
                        break;
                    }
                }
            }
        }
    }

  • That's pretty typical Codeproject.com code, it only ever works correctly by sheer accident. You'll have to throw it away. Use the System.Windows.Automation namespace, there is a bit of a learning curve but the investment is well worth it and will pay back handsomely in the future. – Hans Passant Aug 04 '16 at 13:09

2 Answers2

0

Instead of using FindWindow, you will need to find the Outlook's window that hosts that control (cast Inspector for IOleWindow and cal lIOelWindow.GetWindow), then look for a child of that window (EnumChildWindows) with the given class name / caption / etc. If there multiple windows with that class name, you might need to use a unique combination of the parent / child / peer windows to make sure you have the right one.

Dmitry Streblechenko
  • 62,942
  • 4
  • 53
  • 78
  • Hi Dmitry thanks a lot for reply, I've followed your instructions but the loop through the list doesn't find the right window, the title seems to be empty, I'm going to test the get window name function – Flavio Bianchi Aug 04 '16 at 12:53
  • Play with Spy++ first to see the right window hierarchy. – Dmitry Streblechenko Aug 04 '16 at 16:10
  • Using Spy++ I see that the window I'm searching for is under desktop root, The problem seems to be that when the deactivate function of the inspector is called, the window is still not present in the list – Flavio Bianchi Aug 04 '16 at 16:23
  • In my environment, 32770 is a child of the AfxWndW window. – Dmitry Streblechenko Aug 04 '16 at 16:29
  • In customer's enviroment is child only of 32769 (Desktop), I've tryed to print the list of all the windows but in the list I can't find the 32770 I'm looking for. – Flavio Bianchi Aug 04 '16 at 16:37
  • Open the Outlook inspector, start Spy++, go to "Search | Find Window", drag the find icon on top of the "To" button in Outlook's inspector, click Ok. You should now see the hierarchy. – Dmitry Streblechenko Aug 04 '16 at 16:59
  • I've tested spy++ on the "To..." button of the new message form and you are right, here the hierarchy starts from AfxWndW and goes on to the other components. The window I need to replace is the one that opens with the address list, and if I look for its hierarchy seems to be under desktop, this one is the window I'm not able to catch... – Flavio Bianchi Aug 04 '16 at 17:08
0

After days of test and tryes the solution (suggested by Microsoft) has been to insert a separate thread, started from the inspector and auto terminated when the window is found.

Inside the inspector_deactivate

    if (workerThread.ThreadState == ThreadState.Unstarted)
    {
        // Start the worker thread.
        workerThread.Start();

        while (!workerThread.IsAlive) ;

        // Put the main thread to sleep for 1 millisecond to
        // allow the worker thread to do some work:
        Thread.Sleep(1);
    }
    else if (!workerThread.IsAlive)
    {
        workerObject = new Worker(Inspector);
        workerThread = new Thread(workerObject.DoWork);
        workerThread.Start();
    }

and inside the thread

   public Worker(Outlook.Inspector insp)
    {
        Inspector = insp;
        _shouldStop = false;
    }

    public void DoWork()
    {
        while (!_shouldStop)
        {
            SubstituteWindow();
        }
    }