gpt4 book ai didi

python - ctypes:在 COM 服务器中返回指针时的内存管理

转载 作者:太空宇宙 更新时间:2023-11-04 03:52:34 25 4
gpt4 key购买 nike

自从从 Win XP 过渡到 Server 2008 后,我遇到了一些奇怪的问题。我试图解决这些问题,但是,当返回指向结构的指针时,我仍然不确定内存管理如何通过 COM 工作。

假设我需要返回 POINTER(MyStruct) 类型的东西在用 Python 编写的 COM 服务器的功能中。在函数中,我创建了对象:

struct = MyStruct()
struct.field = 4

然后我回来
return POINTER(MyStruct)(struct)

我必须保留对 struct 的 python 引用吗?避免在编码发生之前释放服务器上的内存?如果我真的这样做了,COM 客户端就会崩溃。如果我不这样做,有时这些结构中包含的数据在客户端接收后会损坏。

我想我在这里做错了,但我无法通过阅读 ctypes 弄清楚是什么和 comtypes文档。

EDIT1:
我刚刚找到 this似乎相关的帖子,因为结构的内容也被覆盖。答案也表明了预期,即“意外”释放了内存。但是,答案并没有解释如何解决这个问题。

正如我之前解释的,如果我保留引用
self.struct = struct

客户端崩溃。

EDIT2:
我正在根据 eryksun 的要求设置 COM 接口(interface)定义和 python 方法签名。在我的问题中,我稍微简化了问题,以便更容易获得概述。实际的方法返回一个指向结构数组的指针:
IOPCItemMgt::ValidateItems
HRESULT ValidateItems(
[in] DWORD dwCount,
[in, size_is(dwCount)] OPCITEMDEF * pItemArray,
[in] BOOL bBlobUpdate,
[out, size_is(,dwCount)] OPCITEMRESULT ** ppValidationResults,
[out, size_is(,dwCount)] HRESULT ** ppErrors
);

关于指针 **上的指针,接口(interface)规范说:

You will note the syntax size_is(,dwCount) in the IDL used in combination with pointers to pointers. This indicates that the returned item is a pointer to an actual array of the indicated type, rather than a pointer to an array of pointers to items of the indicated type.



这是python方法:
def ValidateItems(self, count, p_item_array, update_blob):

假设有一个名为 OpcDa.tagOPCITEMRESULT() 的 ctypes 结构。 .

我通过调用创建这些结构的数组
validation_results = (OpcDa.tagOPCITEMRESULT * count)()
errors = (HRESULT * count)()

在设置所有数组元素的字段后,我返回如下指针:
return POINTER(OpcDa.tagOPCITEMRESULT)(add_results), POINTER(HRESULT)(errors)

EDIT3:
我想总结一下对这篇文章的评论以及到目前为止我发现的内容:

正如 eryksun 所建议的,简化的 return 语句至少会导致相同的行为和问题,但更具可读性:
return add_results, errors

与此同时,我做了一些实验。我按照 eryksun 的建议尝试了低级实现。
def ValidateItems(self, this, count, p_item_array, update_blob, p_validation_results, p_errors):
(...)
p_validation_results[0].contents = (OpcDa.tagOPCITEMRESULT*count)()
p_errors[0].contents = (HRESULT*count)()
(...)
for index (..)
val_result = OpcDa.tagOPCITEMRESULT()
p_validation_results[0][index] = val_result
p_validation_results[0][index].hServer = server_item_handle

在我填充数组元素的循环中,我用一个新元素覆盖了内容,只是因为我很绝望。有趣的是,使用这段代码,我能够看到服务器上的内存损坏,而之前的代码只显示客户端的损坏。
  • index=0 , hServer被赋予它的值(value)。当我检查值时,它很好。
  • index=1 , 但在 [0][1].hServer 赋值之前,[0][0].hServer 的值还可以。
  • index=1 , 但在赋值 [0][1].hServer = val_result 之后,[0][0].hServer 的值已以与前面提到的相同方式损坏。
  • index=2之后 作业 [0][2].hServer = val_result[0][1].hServer 的值很好

  • 这意味着 hServer在为第二个元素分配新值后,只有第一个数组元素会被部分覆盖。

    我假设第一个循环的 val_result 的内存以某种方式被释放和覆盖,尽管我认为赋值 some_pointer[0] = new_value实际上将内容复制为 this post建议。

    但是现在,它变得更加奇怪了。当我想起 val_result在 python 列表中,例如
    self.items.append(val_result)

    服务器端的损坏是 走了 .但是,我得到 COMError再次在客户端。

    问题是,这个神秘的 COMError 不是由服务器中的(可捕获的)错误引起的。一切似乎都很好。所以一定是由 COM 编码的内部引起的。

    有什么建议如何继续进行或对 COM 内部发生的事情有更多的了解吗?

    最佳答案

    哇,当我从微软论坛得到一个链接到 an older thread 的答案时,我几乎要辞职了这为我指明了正确的方向。实际上,线程启动器通过使用 SAFEARRAY 解决了它的问题。 ,但如果我没记错的话,你不能简单地返回 SAFEARRAY当请求指向数组的指针时。您必须更改我无法更改的界面。至少它对我不起作用。

    然而,他的代码片段中有一行让我想到:

    *ppServerId = (long*) CoTaskMemAlloc((*pSize) * sizeof(long));

    CoTaskMemAlloc 的显式调用似乎是方法 CoTaskMemFree的对应物这实际上在客户端崩溃。所以我在想,当第 3 方 C++ 软件中的释放例程(应该对很多客户正常工作)崩溃时,可能是分配错误或丢失。

    因此,我在 comtypes 来源中搜索对 CoTaskMemAlloc 的调用。但可以找到除一个测试用例之外的任何测试用例。

    因此,我通过明确地为通过指针而不是按值从 COM 方法返回的任何内容分配所有内存来使其工作。这包括字符串( c_wchar_p )、结构、数组和结构内的字符串。

    所以我写了这三个辅助方法(实际上可以简单一点):
    def make_com_array(item_type, item_count):
    array_mem = windll.ole32.CoTaskMemAlloc(sizeof(item_type) * item_count)
    return cast(array_mem, POINTER(item_type))

    def make_com_string(text, typ=c_wchar_p):
    text = unicode(text)
    size = (len(text) + 1) * sizeof(c_wchar)
    mem = windll.ole32.CoTaskMemAlloc(size)
    ptr = cast(mem, typ)
    memmove(mem, text, size)
    return ptr

    def make_com_object(object_type):
    size = sizeof(object_type)
    mem = windll.ole32.CoTaskMemAlloc(size)
    ptr = cast(mem, POINTER(object_type))
    return ptr

    然后,我在必须返回任何指针的地方使用它。

    所以 ValidateItems方法现在大致如下所示:
    def ValidateItems(self, count, p_item_array, update_blob):

    validation_results = make_com_array(OpcDa.tagOPCITEMRESULT, count)
    errors = make_com_array(HRESULT, count)

    (...)
    for index (...):
    validation_results[index].hServer = server_item_handle

    return add_results, errors

    关于python - ctypes:在 COM 服务器中返回指针时的内存管理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20546303/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com