2

I have followed the documentation provided for the Python comtypes module (http://pythonhosted.org/comtypes/server.html) and built a simple COM server.

It seems to be working correctly. I can see that the typelib and interface are registered (using Oleviewer) and from a Python script I can call the (single) method that adds two integers.

However, I cannot make it work with Excel (Office 2007, running on Win7). The following simple function does not work (execution stops when MyMethod is called)

Function test(a, b)
    Dim x As New MyTypeLib.MyObject
    ab = x.MyMethod(a, b)
    Debug.Print "d" & CStr(ab)
    test = ab
End Function

Is there any way to debug this?

More comments

There is indeed light at the end of the tunnel. The suggestion to use VBScript has been helpful. The following short script does work (providing independent confirmation that the COM server works).

Dim x,ab
Set x = CreateObject("MyTypeLib.MyObject")
ab = x.MyMethod(1, 2)
MsgBox CStr(ab)

However, VBScript is calling the server using late binding and I was trying to get the early binding to work.

So, I changed my Excel VBA function to use late binding and expected things to work, but they do not.

Here is the modified VBA:

Function test(a, b)
    Dim x As Object
    Set x = CreateObject("MyTypeLib.MyObject")
    gadd = x.MyMethod(1, 5)
End Function

So my server can be used with late binding from Python and VBScript, but not Excel!

Using Process Monitor I can see that in both late and early binding cases Excel tries unsuccessfully to load msvcr90.dll, even though this DLL is installed on the system.

Here is a screenshot from Process Monitor, showing where Office Excel 2007 on 64-bit Windows 7 Home Edition starts to try to load msvcr90.dll

enter image description here

xmojmr
  • 8,073
  • 5
  • 31
  • 54
Blair
  • 135
  • 1
  • 9
  • Does it work from standalone `VBScript`? (see [MSDN: VBSCript > CreateObject](http://msdn.microsoft.com/en-us/library/dcw63t7z%28v=vs.84%29.aspx)) – xmojmr Dec 02 '14 at 10:23
  • I don't know. I don't have VBScript (doesn't that require an ASP server?) – Blair Dec 02 '14 at 22:28
  • VBScript interpreters (see [Wikipedia: VBScript](http://en.wikipedia.org/wiki/VBScript)) `cscript` and `wscript` are included in every version of Windows. You put almost the same (but type-less) code as you use in Excel into a text file with `.vbs` extension and when you run it then you actually test another COM client application. Stack Overflow has [tags](http://stackoverflow.com/tags/vbscript/info) with answers to some questions you may have. Testing against multiple clients is typically the way to approach this kind of problem. Do you build `in-process` or `out-of-process` COM server? – xmojmr Dec 03 '14 at 06:39
  • Thank you. This has indeed been very helpful. I have been able to use the CreateObject function to talk to my server :-). Which confirmed (the Python test) that it works with late binding. – Blair Dec 03 '14 at 21:26
  • COM is not an easy to grasp technology when it comes to the COM-server-side. You can/should post the solution as [self-answer](http://stackoverflow.com/help/self-answer) to help future `Python` programmers ;) – xmojmr Dec 04 '14 at 06:15
  • I thought I had the answer, but I am finding that sometimes, and on some machines, I can get things to work, but I do not yet have a reliable solution with early binding! – Blair Dec 06 '14 at 03:02
  • "_On some machines it works on some machines it does not, how to debug it_" is too broad for clear on-topic answer. Compare all relevant files (COM client libraries, your COM server libraries), compare all relevant [COM registry keys](http://msdn.microsoft.com/en-us/library/windows/desktop/ms678477(v=vs.85).aspx) make sure the client's reference correct "latest" version of the type library and also the `out-of-process` COM server is more tolerant to various DLL versioning issues as it is a standalone application.. – xmojmr Dec 06 '14 at 07:28
  • 1
    I'm sorry, formulating a clear question is getting to be a challenge. I have two machines in quite different locations. It may be helpful to identify the two machines I am dealing with. Both run 64-bit Win7, but one (call it 'A') uses a 'Home' edition and the other (call it 'B') runs Win7 professional. On machine A I have Office 2007 and on B Office 2013. I have installed exactly the same Python versions to create the COM server on each machine and all project files are synchronised via a cloud drive. – Blair Dec 07 '14 at 01:43
  • 1
    On machine B, things started to work, but then I found on A that the problem persists. I have been checking that the registry keys refer to the latest type library. I think that's OK. Using Process Monitor on machine A, I can see that msvcr90.dll is not found (a whole set of different folders from the PATH are searched then Excel quietly gives up). The DLL is installed on the machine (I ran vcredist_x86.exe to be sure). Using Process Monitor again, but with the VBScript, msvcr90.dll is not used. – Blair Dec 07 '14 at 02:05
  • It seems (Google search) that Excel 2007 uses `msvcr80.dll`. If your `in-process` COM server expects `msvcr90.dll` then there's sort of conflict. Two different versions of the "same" library can not co-exist reliably in the same process (due to shared static variables etc.). If this is the case then moving your COM server to the `out-of-process` model will be the only way to guarantee the compatibility (there would be `LocalServer32` registry key and not `InprocServer32` registry key present in your server's registration) – xmojmr Dec 07 '14 at 06:39
  • Back with machine B this morning. A quick check indicates that Excel 2013 is using msvcr90.dll (when all user addins, COM etc are removed). So that suggests that my COM server is not working on machine A because it does not correctly identify where to find msvcr90.dll. I should add that simply putting a version of this DLL in the PATH of A does not fix the problem. That causes a runtime error. – Blair Dec 07 '14 at 20:48
  • 1
    I have tried a little with making and registering a local server. It seems promising (but I must wait to try on machine A). It would be good to have a solution, but I am afraid that this adds overhead to each method call compared to the in-process server (and my intended application will be sensitive to performance overheads). I remember seeing (at some point of this saga) mscvr89.dll and mscvr90.dll loaded and working side by side. So, I would like to try to resolve the problem, not just avoid it. – Blair Dec 08 '14 at 01:37

1 Answers1

2

When I posted this question, I needed to know a bit more about how to debug COM servers (well a lot more actually!). It turns out that there were several problems that stopped my server from working. I think finding out about the tools is probably as much of interest as the actual problems, so this answer will try to cover both.

Initially, it was helpful to use VBScript to provide an independent verification that the server was registered and working (the simple code is shown above). VBScript uses late binding and I wanted early binding, but it was nevertheless helpful because I then realised that Excel did not work with the late binding VBA code either (on machine A)!

Something odd happened too with VBScript. When I first wrote scripts they ran by default (clicking on the file in Windows Explorer), but then for no apparent reason they stopped working. I found that I needed to specify the full path to the 32-bit version (C:\WINDOWS\SysWOW64\cscript.exe) for them to work.

Also worth noting is that by using cscript.exe, instead of wscript.exe, debugging messages embedded in my Python modules appear on the Windows CMD window.

The next breakthrough came by using the Dependency Walker program depends.exe. This is often mentioned in posts like mine where people are trying to figure out why DLLs are not working.

Dependency Walker is available from it's own website, but that version does not work properly with Windows 7 (see Profiling x86 executable with Dependency Walker hangs on Windows 7 x64) so I found that I needed to install WDK 8.1 (from here: http://msdn.microsoft.com/en-US/windows/hardware/gg454513).

DW is very powerful and a bit overwhelming at first. However, by reading its logging output it became clear that Excel 2007 uses msvcr80.dll and that the server's attempt to load msvcr90.dll was failing (even though the DLL was properly installed on the machine). This was not a problem on machine B because Excel 2013 uses msvcr90.dll.

Now, in these tests I was creating the server using Python comtypes interactively (there is no server DLL involved). But there is also the possibility of creating a DLL by using the py2exe tool. I was trying to do this too, the DLL failed to register using regsvr32.

DW helped debug this problem too. Happily, in the logging output I noticed a stack trace of a Python exception. It was the GetModule() in the example. It turns out that this call is needed only once, to create some Python classes that are then cached on the machine. Removing this command fixed the problem.

Also, although it may not have caused a problem, the official current version of py2exe is 0.6.9 but this caused some files that are no longer available to be bundled (specifically zlib.pyd). The unofficial version 0.6.10 (available here: http://www.lfd.uci.edu/~gohlke/pythonlibs/#py2exe) solved that issue.

So I now have the possibility of compiling a COM server DLL, in which I can include the required msvcr90.dll, and, yes, it works on both machines :-)

In summary then. Windows 7, with its mixture of 32-bit and 64-bit libraries is a nightmare. VBScript helps, by providing an independent environment for testing a server. Dependency Walker really is amazing, but takes ages to learn to use (other useful tools in the WDK include oleviewer, process-monitor and process-explorer).

Community
  • 1
  • 1
Blair
  • 135
  • 1
  • 9