python - 将 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 代码。
解决方案
你真的安装了 Excel 吗?您不必对 GUID 执行此操作。你可以说:
import win32com.client
excel = win32com.client.gencache.EnsureDispatch('Excel.Application')
excel.Workbooks.Add()
... and so on ...
推荐阅读
- python - PyTorch 从检查点加载 GradScaler
- javascript - NextJs & Strapi 使用 getStaticPaths 错误
- javascript - 如何获得与承诺相关的返回值
- python - 当 OpenCV 在 venv python 3.10 中时,Pycharm“找不到参考”
- pine-script - 我如何计算会话的总累计牛/熊蜡烛?
- windows - 如何创建热键以在 Windows 中打开文件
- python-3.x - 完全关闭 django rest framework url
- docker - 无法从 macOS M1 本地主机访问容器中运行的 Flask 应用程序
- mysql - 为什么在 concat_ws 中更改 order by 并限制结果?
- python - 如何根据备用列中的相应值将一列中的特定值添加到列表中