Nie, nie jest to rant na Office ;) serio po prostu ostatnio miałem w tym projekt i opisuje wszystkie swoje problemy na które natrafiłem począwszy od złego oszacowania po inne bolesne rzeczy, na które natrafiłem. Jednak większość z nich była taka, bo… została tak zaprogramowana – jak to mówią by design i feature nie bug. Można tak w nieskończoność. Jednak jest jedna sprawa, która… jest bugiem i jak mamy wspierać outlook od 2007 to na niego na pewno natrafimy. Zaś chyba dopiero od 2013 w któreś wersji to podobno zostało naprawione. Nie wiem, bo strona supportu MS nie działa jak się próbuje ten problem wyszukać! ;)

Ogólnie outlook możemy odpalić na wiele sposobów – poprzez aplikację, linki, sent to z kontekstowego menu na pliku czy też z innej aplikacji office.  Normalnie rzecz biorąc można by było założyć, iż to niema znaczenia jak to odpalamy. Aplikacja to aplikacja i powinna działać zawsze tak samo.

Ale niestety tak nie jest. W Outlooku możemy wyróżnić dwa typy działania: modalny jak i normalny. Te dwa typy mogą być mieszane w ten sposób, że mając otwarty Outlook możemy też otworzyć modalne okno outlooka.

W gwoli wyjaśnienia:

  • Normalne okno – otwieramy Outlook normalnie poprzez link Outlook czy jakkolwiek inaczej jest to zorganizowane, ale otwarty jest cały outlook;
  • Modalnie – jedynie JEDNO okno jest otwierane, czy to będzie okno wysłania maila czy stworzenia eventu nie ma znaczenia. Outlook tak jakby nie jest ładowany, ale jest ładowane tylko to okno. Tryb modalny to na przykład sent to w menu kontekstowym na pliku;
  • Tryb mieszany – to taki kiedy mamy normalnie otwarty outlook i do tego odpalimy go modalnie. Aha… ciekawe nie?

Podczas tworzenia nowego maila – czy też ogólnie w trakcie operacji, która otwiera nowe okno outlooka – są wykonywane odpowiednie event. Każde nowe okno ma własnego Inspectora – byt reprezentujący dane okno. Problem polega zaś na tym, że w zależności w jakim typie działania jesteśmy te zdarzenia się różnią.

Przy trybie modalnym wszystkie zdarzenia związane z Inspector nie są wykonywane. Nie zostaje stworzone zdarzenie o tym, że inspektor został stworzony, nie zostają podpięte zdarzenia pod inspektor więc też nie wiemy, kiedy jest on aktywny. Przy opcji modalnej NIE WIEMY, że jesteśmy w edycji maila czy czegoś innego.

Note: Inspector to taki byt, który służy jako kontekst wielu rzeczy w outlooku w tym i maila. Za pomocą własności CurrentItem możemy się odwołać do aktualnej rzeczy, której tyczy inspector. A taki inspector może się tyczyć maila, spotkania czy też kontaktu czy zadania. Ale jest jeden Inspector per jeden task, mail itp. Dzięki czemu możemy w łatwy sposób rozróżniać czy ten mail jest tym czy innym mailem.

Oczywiście ten problem tyczy nas jedynie wtedy, kiedy interesują nas zdarzenie związane z inspectorem. Na przykład chcemy dodać treść do maila albo usunąć załącznik albo włączyć szyfrowanie maila. Różne przypadki chodzą po ludziach :)

Ale jest na to hack… który jest albo nie jest ładny. Jednym znanym mi sposobem zorientowania się w jakiej sytuacji my jesteśmy – czy mail został stworzony przez nową wiadomość czy przez menu kontekstowe sent to – to wykorzystanie timera w .NET.

Jego zadaniem będzie działanie cały czas i sprawdzanie czy nasz wewnętrzny licznik inspektorów jest zgodny z aktualną liczbą dostępnych inspektorów.

By to osiągnąć musimy stworzyć naszą metodę, która jest odpowiedzialna za zliczanie tworzonych inspectorów – czyli obsługujemy zdarzenie NewInspector a w nim zwiększamy licznik aktualnie stworzonych inspectorów.

private void OnOutlookNewInspector(object sender, object inspector, string folderName)
{
    _count_insepctors++;
    // other code
}

Następnie w kodzie inicjalizacji naszego rozszerzenia tworzymy Timer który co określony czas wywoła metodę sprawdzającą jak otworzyliśmy outlooka:

private Timer _timer = new Timer();
private volatile bool _requestStop = false;
private int _count_insepctors = 0;

private void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
    // just in case this is office dev...
    if (OutlookApp == null)
    {
        return;
    }
    Outlook.Inspectors insepctors  = null;
    try
    {
        // get all inspectors in outloook
        insepctors = OutlookApp.Inspectors;

        // get count - mostly for debugging purpose
        int count = insepctors.Count;

        // if count diff our count we are in ctx Sent To | Mail reciepient
        if (count != _count_insepctors)
        {
            // for each inspector, itinialize code
            foreach (Outlook.Inspector inspector in insepctors)
            {
                // do our initialization code and if needed stop timer
                // we detected a problem, so we do not need to check every
                // 100 milliseconds. one sec should be fine, even maybe
                // stoping it completely? dep. on req.
                _timer.Interval = 1000;
                inspector.Release();
            }

            _count_insepctors = count;
        }
        // we're ok, NewIspector handler works
        else if(count >= 1)
        {
            // haha, you wish!
            //Stop();
        }

        if (!_requestStop)
        {
            _timer.Start();
        }
    }
    catch (Exception ex)
    {
    }
    finally
    {
        insepctors.Release();
    }
}

private void Stop()
{
    _requestStop = true;
    _timer.Stop();
}

private void Start()
{
    _requestStop = false;
    _timer.Start();
}

No i oczywiście inicjalizacja kodu:

_timer.Interval = 100;
_timer.Elapsed += OnTimerElapsed;
_timer.AutoReset = false;
_timer.Start();

Trzy uwagi do kodu:

  • Trzeba pamiętać o ubiciu timer przy wychodzeniu z aplikacji;
  • Warunek stopu…. Wspomniałem o trybie mieszanym…. Niestety, w trybie mieszanym mamy taki sam problem jak przy modal. Zdarzenia nie są wykonywane. Więc nie możemy kompletnie zastopować timer;
  • Ja po obsłużeniu przypisuję count do liczydełka. To jest dobre wyjście jak chcemy tylko raz wykonać nasz kod w oknie dialogowym, w danym oknie dialogowym, jak jednak potrzebujemy więcej razy to to warto zmienić warunek i dodać inną obsługę zmniejszania liczby inspectorów.

Kod nie jest ładny, kod nie jest fajny, kod jest hackiem na buga. Ale jak widać, nawet proste rzeczy nie chcą działać od tak od siebie. Mam nadzieję, że ten post Ci pomoże z tym problemem, albo inaczej, mam nadzieję, że nigdy nie będziesz miał tego problemu :)