Taka prosta operacja, którą każdy potrafi wykonać z poziomu UI w outlooku już taką prostą operacją z poziomu kodu nie jest.  Albo dokładnie mówiąc jest, jeżeli wie się wszystko i pisało się Outlooka. To pewnie banalna sprawa. Jeżeli jednak tak jak ja, nie pisaliście nigdy nic pod outlook to pewnie dla was to będzie taka sama zagwozdka jak dla mnie. A dla mnie była dość dużą zagwozdką, bo przez brak jednej linijki zmarnowałem!!!! Cały dzień debuggując i zachodząc co jest nie tak.

Moje zadanie polegało na tym, by dodać zasadę, że jak przyjdzie email, którego nie chcemy to możemy na nim kliknąć i powiedzieć: przenieść mi to do Junk Email folder za każdym razem jak ten sam nadawca wyśle mi wiadomość. Czyli tak zwana reguła: przenieść mail od danej osoby do danego folderu.

Side note: Dlaczego niedodanie adresu do zablokowanych nadawców? BARDZO DOBRE PYTANIE. A krótka odpowiedź też jest BARDZO prosta:

Bo to k jest tak skomplikowane, że nawet nie warto się w do babrać.

Odpowiedź dłuższa, taka na którą programiści zasługują:

Dodanie do zablokowanych jest uzależnione od tego, gdzie i jak chcemy to dodać, mamy dwie opcje: lokalnie w oulooku, zdalnie w exchange. Jeżeli piszemy plugin pod exchange (nawet nie wiem co to znaczy, ale mając dostępne exchange API -jak co, nie wiem) to mamy metodę umożliwiającą nam dodanie osoby do listy zablokowanych. Wiem, że istnieje, bo widziałem dokumentację.

Ale jeżeli piszemy plugin pod Outlook to już tak fajnie nie jest. Z outlooka możemy dodać osobę do zablokowanych nadawców zarówno lokalnie (do lokalnej listy) jak i do listy exchangowej (jeżeli usługa jest włączona/dostępna z poziomu exchange). By to zrobić wystarczy zaprzyjaźnić się jedynie [MS-OXCSPAM]: Spam Confidence Level Protocol – aż tak dobrym programistą chyba nie jestem by się z tym zaprzyjaźniać. Znam tylko jeden produkt i jedną osobę, która się z tym zaprzyjaźniła i teraz trzepie na tym kasę: Outlook Redemption – na wszystkich forach właśnie te linki znajdziecie. Więc jak trzeba dodać do zablokowanych to z miejsca załóżcie $400 więcej budżetu.

Wiedząc co chcemy robić zabieramy się do roboty.

Będziemy tworzyć obiekty COMowe więc potrzebujemy trochę więcej rzeczy niż normalnie. By w ogóle rozpocząć to potrzebujemy dostęp do przestrzeni nazw (Outlook Name Space – MAPI jest jedynym dostępnym przynajmniej w Outlook 2007) by dobrać się do storage w którym są przechowywane takie rzeczy jak reguły. Na razie opiszę kod a potem pokarzę pełną wersję:

var session = _outlook.Session;
var store = session.DefaultStore;
var rules = store.GetRules();

Mając dostęp do reguł, możemy stworzyć nową regułę:

var rule = rules.Create(ruleName, OlRuleType.olRuleReceive);

I następnie już działać w oparciu o nią dodając różne warunki i akcje.

Dla przykładu warunek od kogo otrzymaliśmy wiadomość wygląda tak:

var ruleConditions = rule.Conditions;
var fromCondition = ruleConditions.From;
fromCondition.Recipients.Add(email);

// UWAGA zaraz o tym napiszę
fromCondition.Recipients.ResolveAll();
fromCondition.Enabled = true;

Akcja przenoszenia do folderu:

var ruleActions = rule.Actions;
var moveRuleAction = ruleActions.MoveToFolder;

moveRuleAction.Folder = destinationFolder;
moveRuleAction.Enabled = true;

Całośc naspnie trzeba zapisać:

rules.Save(true);

Niby proste? No ale. Jeżeli nie zrobicie ResolveAll() to wasza reguła będzie wyglądała tak samo jakbyście ją stworzyli z palca ALE WAM NIGDY NIE ZADZIAŁA. Nawet jeżeli dodajecie osobę która nie jest kontaktem tylko dodajecie ją po email to musicie zrobić ResolveAll(). Tego oczywiście nigdzie nie ma powiedziane, albo ja nie znalazłem. Kosztowało to mnie dzień………. dzień.

Jest jeszcze jedna sprawa, folder musi istnieć do którego się próbujemy dostać. Plusem jest to, że w naszym przypadku to są dwie linijki:

var rootFolder = store.GetRootFolder();
var destinationFolder = session.GetDefaultFolder(OlDefaultFolders.olFolderJunk);

Poniżej cały kod metody wraz z sprawdzianem czy nasza reguła już istnieje:

private readonly _Application _outlook;

public bool Block(string email)
{
    var ruleName = Msgs.BlockedEmailRuleName.FormatWith(email);

    NameSpace session = null;
    Store store = null;
    Rules rules = null;
    MAPIFolder destinationFolder = null;
    MAPIFolder rootFolder = null;
    Rule rule = null;
    RuleConditions ruleConditions = null;
    ToOrFromRuleCondition fromCondition = null;
    RuleActions ruleActions = null;
    MoveOrCopyRuleAction moveRuleAction = null;

    try
    {
        session = _outlook.Session;
        store = session.DefaultStore;
        rules = store.GetRules();

        var exists = rule_exists(ruleName, rules);
        if (exists)
        {
            return true;
        }

        rootFolder = store.GetRootFolder();
        destinationFolder = session.GetDefaultFolder(OlDefaultFolders.olFolderJunk);

        rule = rules.Create(ruleName, OlRuleType.olRuleReceive);

        ruleConditions = rule.Conditions;

        fromCondition = ruleConditions.From;
        fromCondition.Recipients.Add(email);
        fromCondition.Recipients.ResolveAll();
        fromCondition.Enabled = true;

        ruleActions = rule.Actions;
        moveRuleAction = ruleActions.MoveToFolder;
        moveRuleAction.Folder = destinationFolder;
        moveRuleAction.Enabled = true;

        rules.Save(true);
    }
    catch(System.Exception ex)
    {
        _log.Error(ex, "Block|");
        return false;
    }
    finally
    {
        moveRuleAction.Release();
        ruleActions.Release();
        fromCondition.Release();
        ruleConditions.Release();
        rule.Release();
        rootFolder.Release();
        destinationFolder.Release();
        rules.Release();
        store.Release();
        session.Release();
    }

    return true;
}

private bool rule_exists(string name, Rules rules)
{
    Rule rule = null;

    try
    {
        for (var i = 1; i <= rules.Count; i++)
        {
            rule = rules[i];

            if (rule.Name == name)
            {
                return true;
            }

            rule.Release();
        }
    }
    catch {}
    finally
    {
        rule.Release();
    }

    return false;
}

Mam nadzieję, że się komuś ten krótki fragment kodu przyda. Mi go brakowało :(

2 KOMENTARZE

  1. Konieczne jest “ręczne” zwalnianie tego wszystkiego co jest w sekcji finally metody Block? Co by się stało, gdybyś nie wywołał metody Release()?

    • Konieczne, outlook mógłby sie zawiesić jakbyś próbował go zamknąć bo ktoś trzyma “referencje” do niego. Tutaj ta referencja to count liczby obiektów trzymających niby referencje. Ten release zmniejsza count. Wazne jest by noe było za mało release ani za dużo release :)

Comments are closed.