WRT the problem of the abstraction bar being set wrong in the Office object model, here are two good “bad” examples, and a good “good” example of where the abstraction bar could be moved to.

Currently, to create a table in Word you have to write this code:

Dim myApp As New Word.Application
myApp.Visible = True
Dim myDoc As Word.Document
Set myDoc = myApp.Documents.Add()
Dim myRange As Word.Range
Set myRange = myDoc.Range(Start:=0, End:=0)
myDoc.Tables.AddRange:=myRange, NumRows:=3, NumColumns:=4

Then you have to write a bunch of tedious code to populate the table by walking a dataset.  Ironically, the way we tell customers to create tables from data is not to create a table but to create an empty range and then get a dataset and convert it to a string and add some heading text with tabs embedded and, well you get the picture--far from the programmer’s conceptual model of creating a table.  For more see http://support.microsoft.com/default.aspx?scid=kb;EN-US;q261999

Excel isn’t much better when it comes to the abstraction level.  For example, imagine a programmer wants to write code that runs against an Excel Workbook containing an expense report that detects when a cell containing the employee’s e-mail address is edited.  When the e-mail address is edited, the programmer wants to automatically lookup the employee’s employee ID and put it into another cell in the worksheet.  The code a developer would write using the generic API of Excel might look like this.

Public Sub Workbook_OnChange(changedRange As Range)
  If (changedRange.Address = "$A$4") Then
    Document.Range("$B$1").Value = GetIDFromEMailAlias(changedRange.Value) 
  End If
End Sub

What you’d really like to do is code in terms of the domain of the problem.  For example, the code above written in the language of the domain of the problem being solved rather than the domain of Excel object model trivia—would look like this:

Public Sub EmployeeEmail_OnChange()
  Employee.ID = GetIDFromEMailAlias(Employee.Email)
End Sub