iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
📅

Common Pitfalls When Reading Outlook Calendars via COM (.NET 8 / WPF)

に公開

Why I started doing this

Honestly, with just task management, I often miss "what I have today."

I have tasks in TODAY.
But, there are meetings on my schedule.
And I get sucked into meetings, and my tasks die.

I thought, "Why not display my schedule on the same screen?"

So, I added Outlook Calendar reading to tsk.


What I built (Goal for this time)

  • c1: Display today's schedule (NOW / TODAY / TOMORROW)
  • c2: Display weekly schedule
  • As a bonus, I made it possible to ON/OFF c1/c2 from /config

I implemented this using Outlook COM (Interop).
I know "Graph API is the correct way," but I wanted to keep it local-only this time.
That's the official reason, but the real reason is that due to company PC restrictions, I had to keep it local.


How it works (Overview)

What I did is simple.

  • Create Outlook.Application
  • Get the calendar folder with Namespace("MAPI")
  • Filter Items and read AppointmentItem

However, even though it's simple, there are many pitfalls.
This is where the real story begins.


Pitfall 1: Microsoft.Office not found (Reference hell)

The first thing that happens.

  • Microsoft.Office does not exist
  • office, Version=15.0.0.0 not found at runtime

✅ The solution is to "Add COM Reference" + adjust "Embed Interop Types / Copy Local."

❌ Relying solely on NuGet's interop usually fails due to environmental differences.

(This is highly environment-dependent, so it was the right decision to make the feature toggleable via /config for general users.)


Pitfall 2: Logs appear but nothing shows on screen (Count == 0)

"It seems to be retrieved, but 0 items displayed."
This took the most time.

Conclusion: The date filter is off.

Common mistake

if (appt.Start >= from && appt.Start < to)
{
    // add
}

This is usually correct.
However, there are rounding or precision issues with date/times on the Outlook side, so exact matching or boundaries are suspicious.

  • = doesn't match
  • It fails if you specify down to the second
  • Ultimately, you have no choice but to take it as a "range"

This is the same for Restrict / Find, and there's talk that "since equivalence matching doesn't work, use a range."

👉 First, how you log is important.
If I logged inside the if, the log wouldn't appear unless the condition was met.
So, initially, I did a full-item log to verify the actual data.


Pitfall 3: Picking up the wrong calendar when I should be picking up my personal one

I thought I retrieved it with GetDefaultFolder(olFolderCalendar), but:

  • Only old appointments appear
  • It says there are no appointments

👉 This is an Outlook Profile/Store issue.

Outlook has multiple Stores such as:

  • Mailbox
  • Archive
  • pst
  • Shared

If the default is different from what you expect, the calendar will naturally be different.

✅ Solution: Enumerate Stores and grab the calendar from the correct Store.

(The correct way varies depending on the environment, so I focused the implementation on "adopt what is retrieved" + verify with logs.)


Pitfall 4: c1/c2 is slow (takes about 10 seconds)

The cause is almost always this.

  • Launching Outlook every time
  • Enumerating all Items and then filtering with C#

It is clearly stated that Outlook's Items.Restrict is significantly faster when there are many items.

✅ Strategy adopted

  • Reduce the number of retrievals (for c1, fetch 2 days' worth at once and split them locally)
  • If further optimization is needed, use Restrict (though it ended up being "good enough as is" this time)

👉 "Improve the user experience first, then use Restrict if necessary" was the right balance.


Pitfall 5: Deadlines are all red/all white (UI state bug)

This wasn't Outlook; it was a bug on the tsk side.

  • When I set OverdueDays >= 0, "today" was treated as overdue, turning everything red
  • When I corrected it to > 0, everything became "not overdue"

The cause was:

  • I was copying OverdueDays into DisplayRow by value
  • I changed the design mid-way, and the value stopped being updated

✅ Solution adopted: DisplayRow derives its state by referencing the Task

public int OverdueDays => Task?.OverdueDays ?? -1;

"Don't maintain state, derive it."
This was the correct answer.


Summary (Lessons learned)

The biggest takeaway was this:

  • Outlook COM "looks simple but is full of landmines."
  • However, the types of landmines are generally predictable.

✅ References
✅ Stores
✅ Date/time filters
✅ Performance

If you master these, you can make it work.

As for how it changed my work, the number of times I have to open Outlook just to check "today's meetings" has dropped dramatically.
Just that alone is a big win.


Download

GitHub

https://github.com/moritan777/tsk-releases/releases/latest

Download tsk_v130.zip, extract it, and run tsk.exe.


Author: mitsukida
Contact: mmitsuki0806@gmail.com

Discussion