1

I was working well with GetNameSpace('MAPI'), but I wrote an ActiveState code: import Outlook contacts using win32com and GetNameSpace is not longer useful.

Now the code has changed: Is useful GetNamespace('MAPI'). Why did this happen and how to return to GetNameSpace('MAPI')?

import win32com.client as client
outlook = client.Dispatch('Outlook.Application')
namespace = outlook.GetNameSpace('MAPI')
drafts = namespace.GetDefaultFolder(16)
contacts = namespace.GetDefaultFolder(10)
inbox = namespace.GetDefaultFolder(6)

Result:

AttributeError: '<win32com.gen_py.Microsoft Outlook 16.0 Object Library._Application
  instance at 0x2538077068688>' object has no attribute 'GetNameSpace'

I am working on VSCode.

Eugene Astafiev
  • 47,483
  • 3
  • 24
  • 45
  • The ActiveState recipe is over 19 years old, so the problem may simply be that Outlook no longer has that attribute and/or does things differently. – martineau Jan 22 '22 at 18:16

2 Answers2

2

Short answer: if you change this line:

outlook = client.Dispatch('Outlook.Application')

to:

outlook = client.dynamic.Dispatch('Outlook.Application')

your original code will work for GetNameSpace("MAPI") (with the upper case 'S')

Alternatively, change your code to the lower case 's' version of GetNamespace().

Long answer: The issue arises because of the way win32com creates the Outlook Application Object, or indeed COM objects in general. This explanation is not confined to Python, and can be seen in VBA and other languages (eg C++, C#).

You can connect or bind to COM objects and their methods and properties in two ways: early or late binding.

Early binding: here, all the information on what methods and properties an object supports are known to the calling code in advance. This information is in the Type Library supplied with the application that supplies the object. Sometimes it is in a separate .tlb file or can be in the application's executable file (which is the case for Excel). The Type Library has all the information about the classes, their methods, and any constants that might be defined. This is sometimes called a 'custom' interface. The functions called have to match the type library methods exactly, and the function names are case-sensitive.

Late binding: here, the calling application knows very little about the object upfront. The object exposes a generic function-calling interface, the Dispatch interface. The application can try to call any function on this interface, which may or may not be valid. At run-time, the Dispatch interface looks up the requested function by name and tries to match it to a numeric identifier, or ID. If a valid Id comes back, then the function is called with a list of parameters. A key point here is that the function name lookup is case-insensitive.

A loose analogy might be going to a restaurant to get something to eat.

  1. You could go to the door of the restaurant and look at the menu outside. You have already chosen this eaterie based on the cuisine it serves. This will tell you what food is available, and might be broken down into starters, main course and dessert. As a diner, you can be confident that before open the door, you will be able to sit down and order your chosen items. This is early binding: you have a lot of upfront information.

  2. Alternatively, you could open any door on the High Street, walk in, sit down and ask for food, and maybe specify 'steak' and 'chips'. You might not know in advance if this is a restaurant or a dentist. If you have chosen the right door, and steak & chips is on the menu, all well and good. If not, you will have a problem or 'error'. This is late binding, and you have to be prepared for the possibility things won't turn out as you might have hoped.

In general, early binding does all the hard work of matching functions and parameters once, and then each call is fast. It also allows the calling application to create wrapper classes that mirror the class model of the object being created. But not every object provides a type library and some calling applications don't have the wrapper-generating capability. Late binding is more generic and requires little upfront knowledge of the object, but is inherently slower to execute. In many applications, the user won't notice a difference in speed.

Finally, back to win32com. This is a Python wrapper for COM objects, and can operate in both early or late-binding modes. Most applications that supply COM objects (eg Outlook, Excel etc) support both modes: the so-called dual interface. win32com has two routes for binding: win32com.client.gencache for early binding, and win32com.dynamic for late binding.

win32com early binding involves the package reading the object's type library and gencache uses makepy to generate python code to wrap the custom interface. This code is stored in the python packages file system for later use (exactly where will depend on your configuration). The dynamic alternative just attempts late binding.

At the higher level, the win32com.client.Dispatch() function will choose a binding method for you. If the wrapper files exist for the custom interface, then that will be used for early binding. If they don't, then late binding is used.

The key point here is that if you have never (in your application or in other python applications) called outlook=win32com.client.gencache.EnsureDispatch('Outlook.Application') you will not have generated the cache of wrapper code for early binding to the Outlook Application object. Hence a call to win32com.client.Dispatch() will fall back to using late-binding.

In this scenario, with late binding, the Python code will work whether you call outlook.GetNameSpace(), outlook.GetNamespace(), or indeed outlook.GeTnAmEsPaCe(), because the function lookup on the late-bound Dispatch interface is case-insensitive.

If however, at some point (today, yesterday, a year ago) the gencache.EnsureDispatch('Outlook.Application') function has been called (and it is called in the example code to which the OP links) then the wrapper functions will have been generated, and win32com.client.Dispatch('Outlook.Application') will do the optimal thing and use early binding. The problem here is that the function names are now case-sensitive, and only outlook.GetNamespace() with a lower-case 's' will now work. Here is the MS documentation for GetNamespace() which shows the spelling.

This is not specific to Python / win32com. You can observe the same effect in VBA.

If you write this:

Dim ol As Object
Set ol = CreateObject("Outlook.Application")

Dim ns As Object
Set ns = ol.GeTnaMeSpAcE("MAPI")

it will work fine. This is because unless you have included the "Microsoft Outlook 16.0 Object Library" (using Tools | References) VBA is using case-insensitive late-binding.

But if you do include the Type Library Reference, and use the class types, you are now using early-binding:

Dim ol As Outlook.Application
Set ol = CreateObject("Outlook.Application")
    
Dim ns As Outlook.Namespace
Set ns = ol.GetNamespace("MAPI")

The VBA editor will likely enforce the correct case-sensitive spelling.

Although VBA is also confused (on my setup at least): as you type Dim ns as Outlook. ... VBA will suggest the NameSpace completion (uppercase 'S'), but once chosen, the IntelliSense will correct it to Dim ns as Outlook.Namespace with a lower case 's' !

DS_London
  • 3,644
  • 1
  • 7
  • 24
0

Nothing has been changed so far in the Outlook object model. The Session property and the GetNamespace method can be used interchangeably to obtain the NameSpace object for the current session. Both members serve the same purpose. For example, the following statements do the same function:

Set objNamespace = Application.GetNamespace("MAPI") 

Set objSession = Application.Session

Note, sometimes, when you have got several profiles configured in Outlook, you may need to use the Logon method of the GetNamespace method.

Eugene Astafiev
  • 47,483
  • 3
  • 24
  • 45