【问题标题】:ActiveX DLL called from thread从线程调用的 ActiveX DLL
【发布时间】:2012-06-07 21:47:45
【问题描述】:

我有一个 ActiveX (COM) DLL,它可以进行 Windows 系统调用(例如 ReadFile() 和 WriteFile())。我的 GUI(在 Python 或 C# 中)在主 GUI 线程中创建 DLL 的实例。但是,为了从线程调用它,必须在每个线程中创建一个新的 DLL 实例,无论使用 C# 还是 Python。 (作为旁注,可以从 C# 中的线程调用原始实例,但这会阻塞主线程;在 Python 中这样做会使 GUI 崩溃。)有没有办法避免在线程中创建 DLL 的新实例?

需要使用原始 DLL 实例的原因:DLL 允许连接到 HID 微控制器。 DLL 提供了一个选项,只允许微控制器使用一个独占句柄。如果 GUI 设计者选择此选项(在某些情况下这是必需的),第二个 DLL 实例将无法按预期工作,因为只有一个 DLL 实例可以建立连接。

【问题讨论】:

  • 在线程中创建新实例有什么问题?只是为了避免它?
  • 好问题;我更新了原始问题,因为它是一个重要的细节。
  • ActiveX 只能由 GUI 线程调用,也可以由 STA 调用。 See this post 您需要将后台线程的调用编组到 GUI 线程,以便使用 ActiveX 对象。
  • 我相信我可以而且实际上可以在主线程以外的线程中创建 ActiveX 对象:“但是,为了从线程中调用它, DLL 必须在每个线程中创建,无论使用 C# 还是 Python”。但是,我的目标是不这样做。
  • 您能否解释一下“但是,为了从线程调用它,必须在每个线程中创建一个新的 DLL 实例”,更详细地包括为什么会这样? ?

标签: c# python multithreading dll activex


【解决方案1】:

我没有使用过 Phyton,但对于 C#,我建议创建一个帮助类,其中包含 ActiveX 作为静态公共属性。让主线程创建 ActiveX,然后所有线程根据需要从那里访问它。

【讨论】:

  • 没有帮助,因为 DLL 实例仍在主线程中。
【解决方案2】:

当你制作一个 ActiveX/COM 组件时,你可以为你的组件指定线程模型,例如它可以是“分门别类”。根据您的选择,ActiveX/COM 负责序列化请求。

如果您多次“打开”和 ActiveX/COM 组件(取决于线程模型?)实际上只会创建一个实例。

我假设您使用 win32com.client.Dispatch(".") 来“打开”您的 ActiveX/COM 组件。

另外,不要忘记 pythoncom.CoInitialize() 和 CoUninitialize() 对调用。

Google 了解这些人的实际行为。

如果您无法更改给定的 ActiveX/COM 组件并且其线程模型不可接受,则可以将所有“出站”调用包装在一个具有监视器“接口”的专用 Python 线程中。

以下是我曾经遇到类似情况时编写的代码的大纲:

class Driver(threading.Thread):
    quit = False  # graceful exit
    con = None
    request = None
    response = None

    def __init__(self, **kw):
        super(Driver, self).__init__(**kw)
        self.setDaemon(True)  # optional, helps termination
        self.con = threading.Condition()
        self.request = None
        self.response = None

    def run(self):
        pythoncom.CoInitialize()
        handle = win32com.client.Dispatch("SomeActiveX.SomeInterface")
        try:
            with self.con:
                while not self.quit:
                    while not self.request: self.con.wait()                    # wait for work
                    method, args = self.request
                    try: self.response = getattr(handle, method)(*args), None  # buffer result
                    except Exception, e: self.response = None, e               # buffer exception
                    self.con.notifyAll()                                       # result ready
        finally:
            pythoncom.CoUninitialize()

    def call(method, *args):
        with self.con:
            while self.request: self.con.wait()       # driver is busy
            self.request = method, args
            self.con.notifyAll()                      # driver can start
            while not self.response: self.con.wait()  # wait for driver
            rv, ex = self.response
            self.request = self.response = None       # free driver
            self.con.notifyAll()                      # other clients can continue
            if ex: raise ex
            else: return rv

【讨论】:

  • 这是个好主意,我正在测试它。但是,在 DLL 的一个函数中调用了 RegisterDeviceNotification(),它在调用时会挂起我的 GUI。如果从 GUI 线程调用,那么它工作正常。我不确定为什么从另一个线程调用它会导致任何问题,因为它正在接收表单的句柄,就像直接从 GUI 线程调用时一样。
    另外,我有权修改 DLL。它是 STA; MTA 没有选择。您有什么建议可以使 DLL 按预期运行?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-06
  • 2014-06-01
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
相关资源
最近更新 更多