Ostatnie trzy tygodnie mego codziennego życia to: O #$%()#$%. Uwierzcie mi, jak ktoś mówi Outlook już mam dreszcze. I prędko ich się nie pozbędę. Dużo się wydarzyło a wiem, że to jeszcze nie koniec. Przygoda dopiero się zaczęła i to sławami których nigdy nie zapomnę: dwóch użytkowników ma problem z rozszerzeniem, nic im nie działa i wszystko się spowalnia. Od tych słów zaczęła się jazda bez trzymanki.

Dzisiaj to tylko podsumowanie kilku tygodni i WIELU GODZIN zmarnowanych na szukanie przyczyn nie działania czegoś co dla 99% ludzi działa. Zaczęło się od jednego problemu, przeszło na inne. Tutaj grupuje przepisy na rozwiązanie problemów lub przyczyny powstanie problemu.

Problemy wydajnościowy #01: Rozszerzenie powoduje, iż Outlook przestaje działać wydajnie

Wywal plik OST lub zrób skan PST. Zrób to od razu. Upewnij się, że to nie jest to. Jeżeli problem się rozwiązał, przybij sobie piątkę i napij się czegoś dobrego – zaoszczędziłeś sobie właśnie tygodnie pracy!

Jak się może objawiać problem z OST? Na przykład KAŻDA LINIJKA KODU rozszerzenia BĘDZIE DZIAŁAĆ 1s.

Jeżeli problem się nie rozwiązał to zaczynamy się bawić.

Problemy wydajnościowy #02: Rozszerzenie powoduje, iż Outlook przestaje działać wydajnie

Wywal wszystkie ALE TO WSZYSTKIE rozszerzenia (nie licząc Microsoftowych domyślnych) i następnie zobacz czy dalej problem istnieje. Jeżeli nie, problem jest na kompatybilności z jakimś pluginem. Włączaj pojedynczo pluginy i zobacz który powoduje problem.

Jak już wykryjesz problem, napisz do producenta – zobacz, czy da się to rozwiązać. Ale ogólnie smutna historia i lepiej dać info, że dane pluginy powodują konflikt.

Dalej nie rozwiązane?

Problemy wydajnościowy #03: Rozszerzenie powoduje, iż Outlook przestaje działać wydajnie

Wykorzystaj VBA w Outlooku (ALT+F11) i zobacz, czy problem istnieje tylko z Twoim kodem czy też kodem Outlooka. Jeżeli te same zdarzenia będą zajmowały tak samo długo jak u Ciebie to problem leży po stronie sytemu. Good luck.

Przykładowy kod:

Dim WithEvents itm As Outlook.MailItem 

Sub CheckPerf() 
    Debug.Print "CONNECTING START: " + CStr(Now) 
    Set itm = Application.ActiveExplorer().Selection.Item(1) 
    Debug.Print "CONNECTING END: " + CStr(Now) 
End Sub

Jeżeli zdarzenia działają jedynie u Ciebie wolno to… jak zrobiłeś dwa poprzednie korki czeka się zabawa z krokami następnymi.

Kod wielowątkowy w Outlook

Masz kod wielowątkowy? Wykorzystujesz w nim Outlook Object Model? Jak tak to jest to strzał w kolano. Outlook wykonuje wszystkie zapytania do Outlook Object Model w tym samym wątku. Próba dostania się do tego z innego wątku powoduje, że nie ma gwarancji jak Outlook się zachowa – Outlook wymusi dostęp do wątku UI ucinając lub przerywając działanie kodu wykonywanego, nie jest to rozsądne „czekanie na wolną chwilę”. Może to być problem z wydajnością, może to być uwalenie jakiegoś obiektu, do którego jeszcze przed chwilą miałeś dostęp itp.

Najlepiej nie mieć wątków, a jak się ma to bez Outlook Object Model.

Edytujesz treść maila w Inline Response?

Jest to nowość od 2013, ogólnie w tym samym oknie co czytamy wiadomości można na nią odpowiedzieć lub zrobić jej forward. Co śmieszne jest to, że New dalej jest w nowym oknie… nie ważne.

Jeżeli korzystasz z Inline Response (to opiszę w innym artykule) to grzechem śmiertelnym jest odwołanie się do obiektu Inspectora. To jest o tyle dziwne, że jak masz okno New czy standardowy edytor w nowym oknie to do edytora odwołujesz się przez obiekt Inspector. A przy Inline Response idziesz przez obiekt Explorer. Obiekt Mail zaś ma własność GetInspector. GetInspector zwraca obiekt Inspector ale jeżeli go nie ma (nie istnieje) to go stworzy. Jak go stworzy to następuje cały proces zdarzeń: InspectorNew, InspectorActive itp. a to znaczy, że jak mamy jakiś kod podpięty pod te zdarzenia to… różne dziwne rzeczy mogą się zdażyć.

Co ciekawe wywołanie ActiveInspector() zwraca null. więc wina jest tylko i wyłącznie we własności GetInspector.

Wywołanie GetInspector kiedy jest się w Inline Response spowoduje, że outlook nie gwarantuje tego jak się zachowa, możesz:

  • Stracić cały draft
  • Część draft
  • Część może być nieczytelna – tak jakby tekst jest, możesz go zaznaczyć, skopiować, ale nie przeczytasz go
  • Być potraktowany jako edycja otrzymanego maila a nie odpowiedź na ten mail
  • Inne w zależności od podpiętych zdarzeń

Mnie się wszystkie rzeczy przydarzały dość losowo. Z tym problemem już się dzieliłem na FB:

Mój dzisiejszy sukces po 4h debuggowania Outlooka 🙂 A jaki był Twój sukces dzisiaj? ;)

Opublikowany przez Jakub Gutkowski na 12 grudnia 2017

Otrzymujesz winmail.dat?

Jest to wina formatu RTF – winmail.dat to zapis tego formatu. Problem polega na tym, że i załączniki są do niego wrzucane. Przeważnie nic się z tym nie da zrobić. Są aplikacji które wam pomogą rozszyfrować ten plik, ale lepiej poprosić Exchange i Outlook by konwertowały mail do HTML przy wysyłaniu. SERIO!

Otrzymujesz winmail.dat przy zaszyfrowanych mailach?

Coś poszło nie tak. Dokładniej mówiąc ty jako twórca rozszerzenia nieświadomie dotknąłeś czegoś czego nie powinieneś tykać. Na przykład NIE NALEŻY USTAWIAĆ UserProperty. To gwarantuje, że klient inny niż Outlook sobie z tym nie poradzi. Lepiej Ustawić Names Property poprzez PropertyAccessor.

Wycieki pamięci

To jest słabe. Z jednej strony MS Mówi: NIE RÓBCIE JUŻ ReleaseComObject z drugiej jak tego się nie robi to są wycieki i to duże. A więc RÓBCIE ale rozsądnie! :)

Jeżeli odwołujecie się do rzeczy po kropce typu Outlook.ActiveInspector().CurrentItem.Attachments[i]. To macie wyciek gwarantowany. Tak jak w ObjectiveC, nie zaleca się tego robić w ten sposób. Najlepiej przypisać każdy obiekt osobno do zmiennej i zwalniać jak trzeba.

Zawsze, ale to zawsze: for over foreach nawet jak robicie RleaseComObject. W przeciwnym wypadku, wyciek gwarantowany.

Wyjątki RCW

Jeżeli otrzymujecie wyjątki:

COM object that has been separated from its underlying RCW cannot be used.

To jest kilka rzeczy do sprawdzenia:

  • Wielowątkowość – jak macie, ubijcie ją
  • Pozbywanie się obiektów przekazanych jako parametr danej funkcji
  • Pozbywanie się czegoś co jest wykorzystywane w większym kontekście (na przykład obiekt explorer może być wykorzystywany od ExplorerActive aż po ExplorerClose – to jest wciąż ten sam obiekt
  • Pozbywanie się obiektu, na którym się zrobiło as – w sensie rzutowanie czegoś jak CurrentItem na mail.

Najlepiej jest mieć jedną metodą (Extension Method) robiącą to co trzeba. Wtedy też możemy dużo lepiej to analizować. Ale tak, ogólnie znalezienie problemu to jest szukanie igły w stogu siania. U mnie była to wielowątkowość.

Widok Outlook Today

TO JEST… po prostu zamknijcie metodę w Try/Catch i nie logujcie problemu. Jak próbujcie się podłączyć do zdarzenia ExplorerActive to przy Outlook Today metoda będzie wam wywalała błędy:

The Explorer has been closed and cannot be used for further operations. Review your code and restart Outlook

Spokojna głowa. Tak będzie i tyle. NIE PRÓBÓJCIE JEDNAK jakoś wykryć, czy to jest Outlook View. Ja próbowałem i zacząłem dostawać błędy typu:

Your system needs more memory or system resources. Close some windows and try again

I to też losowo u użytkowników, kiedy próbowałem się dobrać do HTMLDocument danego widoku w Outlooku. Serio. Outlook to ZŁO.

Czyli rozwiązanie z tweeta:

NIE DZIAŁA! :)))

Inline Response i zdarzenia Reply/Forward/ReplyAll

Zdarzenia podpięte pod obiekt Mail – Reply/Forward/ReplyAll są ZAWSZE wykonywane przed zdarzeniem Inline Response. Przy czym te zdarzenia NIE WIEDZĄ, że będzie to Inline Response i wy też nie macie jak tego się dowiedzieć w kodzie. Próba przechwycenia treści maila w tym momencie zwraca informację o mailu oryginalnym – tak jakby to była treść naszej wiadomości (bez tych nagłówków reply, subject itp. co zawsze macie przy odpowiedzi na maile).

Jedynym rozwiązaniem problemu Kury i Jajka jest odpinanie się od zdarzeń wszędzie, gdzie się tylko da i podpinanie tylko przy InspectorActive.

Opisy błędów przez użytkowników

Zapomnijcie. Zadzwońcie się i zobaczcie o co chodzi użytkownikowi. Serio! Nie ma innej opcji. Może wam się trafią super użytkownicy wiedzący wszystko, ale wątpię ;) Jak zobaczycie co on robi i co to jest wynikiem to będziecie wiedzieć jak działać dalej.

Jak diagnozować?

Serio, żadne testy nawet integracyjne czy systemowe was na to nie przygotują. Jak coś przestanie działać najlepiej dorwać kompa który ma problem i linijka po linijce testować co jest nie tak. Tak dowiecie się, że GetInspector powoduje problemy, czy też Mail.Body może zająć nawet 5 sekund.

Podsumowanie

Jak mówię, to dopiero rozpoczęcie przygody. Żadnych z tych rzeczy też nie miałbym szans przetestować, wszystkie wychodzą w praniu u konkretnych użytkowników. To też nie jest tak, że każdy problem nastąpi zawsze! oj nie, testowaliśmy – na przykład problem z GetInspector następował jedynie przy szybkim przejściu pomiędzy elementami, jak się normalnie klikało myszką… nie do wykrycia :) RCW? ha, to niby się dało przetestować, ale akurat u nas nie było winą nasze zwalnianie, ale zwalnianie obiektu przez rozszerzenie, z którego korzystamy – jedna metoda umożliwiła nam szybkie i łatwe przetestowanie tego na komputerach, na których problem występował. Tak znów piszę na komputerach, na których problem występował, bo nie wszędzie on występował! :)