首页 > 解决方案 > 将 Excel 互操作库与 python3 ctypes 一起使用

问题描述

我正在尝试使用 Python3 为 Microsoft.Office.Interop.Excel.dll 编写一个包装库。由于安全策略,我坚持使用核心 python 库,它具有用于调用 DLL 和共享库中的函数的ctypes库。换句话说,我无法使用 win32com (pywin32),这可能是最直接的解决方案(我过去已经这样做了)。

我已经为 C# 编写了一个类似的库。在 C# 中,我将使用以下 Excel.cs 程序来创建工作簿:

using System;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop.Excel;
namespace ExcelSpace  {
    public class ExcelMainClass {
        [STAThread]
        public static void Main() {
            // Create a application instance
            Application excelApp = new Application();
            // Add a new workbook
            Workbook workbook = excelApp.Workbooks.Add();
            // Save the workbook into current directory
            workbook.SaveAs(System.IO.Directory.GetCurrentDirectory() + "\\MyNewWB.xlsx");
            // Close the workbook and application
            workbook.Close(0); workbook = null; excelApp.Quit();
            // Cleanup
            Marshal.ReleaseComObject(excelApp); excelApp = null; GC.Collect(); GC.WaitForPendingFinalizers();
        }
    }
}

我已将 Excel 互操作 DLL 从“C:\Windows\assembly\GAC_MSIL\Microsoft.Office.Interop.Excel\14.0.0.0__71e9bce111e9429c”复制到本地目录,以便能够更轻松地引用它。然后我会用 csc.exe 编译程序:

C:\Windows\Microsoft.Net\Framework\v4.0.30319\csc.exe /r:Microsoft.Office.Interop.Excel.dll /out:Excel.exe Excel.cs

现在我想用 python3 和 ctypes 做类似的程序。基于来自 Python 的 Access COM 方法How to use IFileOperation from ctypes我已经弄清楚,我必须从我的注册表中获取 CLSID。然后我定义了类似于 comtypes GUID.py的 GUID 类

这是我到目前为止编写的 Excel.py 程序:

#HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Classes\CLSID\
#Assembly == Microsoft.Office.Interop.Excel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71E9BCE111E9429C
#Microsoft Excel Application - Microsoft.Office.Interop.Excel.GlobalClass == {00020812-0000-0000-C000-000000000046}
#Microsoft.Office.Interop.Excel.OLEObjectClass == {00020818-0000-0000-C000-000000000046}
#Microsoft.Office.Interop.Excel.WorkbookClass == {00020819-0000-0000-C000-000000000046}
#Microsoft Excel Worksheet == {00020830-0000-0000-C000-000000000046}

import ctypes
from ctypes import *
import sys

##
# https://github.com/enthought/comtypes/blob/master/comtypes/GUID.py
##
BYTE = c_byte; WORD = c_ushort; DWORD = c_ulong
_ole32 = oledll.ole32
_StringFromCLSID = _ole32.StringFromCLSID; _CoTaskMemFree = windll.ole32.CoTaskMemFree; _ProgIDFromCLSID = _ole32.ProgIDFromCLSID;
_CLSIDFromString = _ole32.CLSIDFromString; _CLSIDFromProgID = _ole32.CLSIDFromProgID; _CoCreateGuid = _ole32.CoCreateGuid
class GUID(Structure):
    _fields_ = [("Data1", DWORD),
                ("Data2", WORD),
                ("Data3", WORD),
                ("Data4", BYTE * 8)]
    def __init__(self, name=None):
        if name is not None:
            _CLSIDFromString(str(name), byref(self))
    def __repr__(self):
        return u'GUID("%s")' % str(self)
    def __unicode__(self):
        p = c_wchar_p()
        _StringFromCLSID(byref(self), byref(p))
        result = p.value
        _CoTaskMemFree(p)
        return result
    __str__ = __unicode__
    def __cmp__(self, other):
        if isinstance(other, GUID):
            return cmp(bytes(self), bytes(other))
        return -1
GUID_null = GUID()
##

##
# https://stackoverflow.com/questions/48986244/access-com-methods-from-python
# and
# https://stackoverflow.com/questions/62065891/how-to-use-ifileoperation-from-ctypes
##
CoInitialize = _ole32.CoInitialize
CoUninitialize = _ole32.CoUninitialize
CoCreateInstance = _ole32.CoCreateInstance

rc = CoInitialize(None)
clsid = GUID("{00020812-0000-0000-C000-000000000046}")
drv = c_void_p(None)
rc = CoCreateInstance(byref(clsid), 0, 1, byref(clsid), byref(drv))
''' Pointers manipulation. Short form: function = cast( c_void_p( cast(drv, POINTER(c_void_p))[0] ), POINTER(c_void_p)) '''
VTable = cast(drv, POINTER(c_void_p))
wk = c_void_p(VTable[0])
function = cast(wk, POINTER(c_void_p))

#Unload DLL and reset COM environment
rc = Release(drv)
rc = CoUninitialize()

CoCreateInstance 函数给了我以下错误:

    rc = CoCreateInstance(byref(clsid), 0, 1, byref(clsid), byref(drv))
    File "_ctypes/callproc.c", line 948, in GetResult
    OSError: [WinError -2147221164] Class not registered

我不知道为什么会收到此错误消息。基于我已链接到此问题的其他堆栈溢出问题,我也不了解如何创建 Excel 应用程序实例。我应该在某个时候使用ctypes 加载共享库函数来加载 DLL 吗?

可能有一些方法可以用 C# 编写静态函数,然后更容易地从 python 调用它们,但我认为这是肮脏的解决方案,我想坚持使用纯 python 代码。

标签: pythonctypesexcel-interop

解决方案


你真的安装了 Excel 吗?您不必对 GUID 执行此操作。你可以说:

import win32com.client
excel = win32com.client.gencache.EnsureDispatch('Excel.Application')
excel.Workbooks.Add()
... and so on ...

推荐阅读