VSTO is voor mannen met borsthaar

Echt waar.

Visual Studio Tools for Office oftewel VSTO oftewel visto mag
er dan nog zo gelikt uitzien met al die fraaie gold plating in de vorm
van Ribbons of Task Panes; bij de basis is het nog steeds die goeie, oude
ole-automation.

Natuurlijk heeft VSTO een aantal sterke punten die het heel interessant maken
om voor bedrijfsapplicaties te gebruiken. Die visuele zaken in Office 2007
zorgen voor een lage instap en versnelde productiviteit. Iedereen die Office
kent, kan sneller overweg met een OBA (Office Business Application) dan met een maatwerk applicatie waarvoor een beginnende medewerker dagen tot weken op cursus moet. Voor een medewerker die net iets meer dan het minimumloon mag kosten, ga je geen training van honderden euro’s per dag organiseren.

There are some ??? google can not answer. Uithangbord voor de kerk Church of God

Goedkoop is de sleutel tot VSTO. Wat we dus verwachten is dat OBA applicaties
snel en eenvoudig te bouwen zijn, en je zou verwachten dat dit met
ontwikkelomgevingen zoals Visual Studio 2008 ook wel zo zou zijn. Maar dan
VSTO…

Als je op het web op zoek gaat naar de achilleshiel van VSTO, dan wordt
meestal de installatie van zo’n applicatie op Vista genoemd. Het is inderdaad
lastig om bv. een succesvolle installatie voor een Excel add-in te produceren,
maar het is niet ondoenlijk. Voor VSTO is een installatie blijkbaar een klusje
voor de administrator dus wil Vista in Elevated Mode draaien. Hiervoor moet je
in verhouding alleen maar ‘droog achter de oren’ zijn.

Maar je hebt pas echt borsthaar nodig als je tegen de DOM (Document Object
Model) aan wilt programmeren. Dit is iets wat we rond het millennium achter ons lieten toen we webapplicaties in managed code gingen bouwen.  En dit is dus het gebied waarbij je Excel.WorkBook, Excel.WorkSheet, Excel.Range etc. met
bedrijfslogica probeert te combineren om zo aardige functionaliteit aan de
gebruiker te kunnen aanbieden.

Een voorbeeld:

Sinds enige tijd zijn wij bezig om in Excel 2007 een prachtige OBA te bouwen.
Dit gaat voorspoedig maar toen besloten we om te achterhalen in welke Excel.Name de actief geselecteerde cel staat.

Een Excel.Name (what in the name…) is een alternatieve benaming voor een
cel of een aantal aaneengesloten cellen (X rijen maal Y kolommen groot). Zo kan
je een cel met daarin 6,0 gewoon LaagBtw noemen en later daarmee doorrekenen (=LaagBtw*BrutoArtikelPrijs).

Om dus te controleren of de cel in een name staat kom je uit bij Excel.Range.
Een Range is een willekeurig aantal cellen, zoals een Excel.Name en de actieve
selectie van de gebruiker. Excel biedt de mogelijkheid om overlap bij twee
ranges te registreren en dat gaan we dus gebruiken.

Laten we de MSDN er eens op naslaan:

expression.Intersect(Arg1,
Arg2,
Arg3,
Arg4,
Arg5,
Arg6,
Arg7,
Arg8,
Arg9,
Arg10,
Arg11,
Arg12,
Arg13,
Arg14,
Arg15,
Arg16,
Arg17,
Arg18,
Arg19,
Arg20,
Arg21,
Arg22,
Arg23,
Arg24,
Arg25,
Arg26,
Arg27,
Arg28,
Arg29,
Arg30)

Dit ziet er lelijk uit maar we gaan gewoon stug door (in .Net 4.0 wordt het
mogelijk om 28 van de 30 parameters weg te laten, iets wat al wel bij VB.Net
werkt):

Range activeRange = Globals.ThisAddIn.Application.ActiveCell;
Range nameRange = ExtractRange("aaa");
Range intersection = Globals.ThisAddIn.Application.Intersect(
  activeRange, nameRange,Type.Missing, Type.Missing, Type.Missing,
  Type.Missing, Type.Missing, Type.Missing, Type.Missing,
  Type.Missing, Type.Missing, Type.Missing, Type.Missing,
  Type.Missing, Type.Missing, Type.Missing, Type.Missing,
  Type.Missing, Type.Missing, Type.Missing, Type.Missing,
  Type.Missing, Type.Missing, Type.Missing, Type.Missing,
  Type.Missing, Type.Missing, Type.Missing, Type.Missing,
  Type.Missing);

if (intersection != null)
{
  MessageBox.Show("Overlap!");
}
else
{
  MessageBox.Show("Helaas...");
}

Hierbij gebruiken we een handige methode om een Excel.Name op naam te zoeken en naar een Excel.Range om te zetten:

private static Range ExtractRange(string rangeName)
{
  Name name = null;
  try
  {
    // haal de name op
    name = (Name) Globals.ThisAddIn.Application.
               Names.Item(rangeName, Type.Missing, Type.Missing);
  }
  catch (Exception)
  {
    return null;
  }

  if (name == null) { return null; } // escape
  // haal de range op
  Range range = name.RefersToRange;
  return range;
}

Na het compileren kan dit getest worden in Excel. We benoemen op het
worksheet van een workbook een aantal cellen met de Name “aaa” en
selecteren een andere cel er iets naast of juist er binnen.

En inderdaad, het aftesten van de overlap werkt fraai.

Maar toen deze code omgesmeed werd naar echte code kregen we op een gegeven moment de volgende foutmelding:

Schermafdruk van een COM foutmelding bij VSTO

Geen verdere uitleg, geen hint en inderdaad, Google heeft niet overal een
antwoord op (-2146827284 geeft geen hit). Ook de MSDN en de schaarse boeken over dit onderwerp bleven in gebreke.

Uiteindelijk zijn we eruit gekomen door weer naar de basis te gaan, dus
bovenstaande code uit een test applicatie. En wat bleek? Om een intersection te
mogen uitvoeren, moeten de twee ranges op dezelfde Excel.WorkSheet geplaatst
zijn. Gelukkig kan van een Excel.Range de Excel.WorkSheet opgevraagd worden
waarop deze is geplaatst dus komen we tot de volgende code:

/// <summary>
/// Extracts the intersection.
/// </summary>
/// <param name="range1">The range1.</param>
/// <param name="range2">The range2.</param>
/// <returns></returns>
public static Excel.Range ExtractIntersection(Range range1, Range range2)
{
  if (range1.Worksheet != range2.Worksheet)
  {
    return null; // not placed on the same WorkSheet
  }

  Range intersection = (Range) Globals.ThisAddIn.Application.Intersect(
    range1, range2, Type.Missing, Type.Missing, Type.Missing,
    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
    Type.Missing, Type.Missing, Type.Missing, Type.Missing,
    Type.Missing);
  if (intersection != null)
  {
    return intersection;
  }
  else
  {
    return null;
  }
}

En als je bedenkt dat VSTO dus schaars gedocumenteerd is met helpteksten bij
de methodes en dat alles run-time via Transparant Proxies werkt waardoor debuggen moeizaam is, dan mag je ook wel wat haar op je rug hebben.

StarWars Wookie met haar

Microsoft: als OBA jullie redding moet zijn voor SaS (Software and Services; Office op de client, diensten vanuit het netwerk/internet/cloud), zorg dan dat VSTO zich echt managed gedraagt. Of is dit de ware reden waarom C# 4.0 hernieuwde aandacht voor COM Interop toont?