将Excel Interop库与python3 ctypes一起使用



我正在尝试为Microsoft编写一个包装器库。办公室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 Interop DLL从";C: \Windows\assembly\GAC_MSIL\Microsoft。办公室Interop。Excel\14.0.0.0__71e9cce111e9429c";以便能够更容易地引用它。然后我会用csc.exe编译程序:

C:WindowsMicrosoft.NetFrameworkv4.0.30319csc.exe /r:Microsoft.Office.Interop.Excel.dll /out:Excel.exe Excel.cs

现在我想用python3和ctypes做类似的程序。基于Python中的Access COM方法和ctypes中的如何使用IFileOperation,我已经了解到,我必须从寄存器中获取CLSID。然后我定义了类似于comtypes GUID.py 的GUID类

这是我迄今为止编写的Excel.py程序:

#HKEY_LOCAL_MACHINESOFTWAREWOW6432NodeClassesCLSID
#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代码。

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

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

最新更新