У меня возникли некоторые странные проблемы после перехода с Win XP на Server 2008. Я пытался исправить эти проблемы, однако я до сих пор не уверен, как работает управление памятью через COM при возврате указателей на структуры.
Допустим, мне нужно вернуть что-то типа POINTER(MyStruct)
в функцию COM-сервера, написанную на Python. Внутри функции я создаю объект:
struct = MyStruct()
struct.field = 4
тогда я вернусь
return POINTER(MyStruct)(struct)
Должен ли я сохранять ссылку python на struct
, чтобы избежать освобождения памяти на сервере до того, как произойдет сортировка? Если я действительно это сделаю, COM-клиент выйдет из строя. Если я этого не сделаю, иногда данные, содержащиеся в этих структурах, будут повреждены после приема на клиенте.
Я предполагаю, что я делаю что-то не так, но я не мог понять, что, прочитав ctypes и comtypes.
EDIT1: я только что нашел этот сообщение, которое, кажется, связано, так как содержимое структуры также перезаписывается. Ответ также предполагает то, что ожидалось, а именно то, что память освобождается «случайно». Однако в ответе не объясняется, как решить эту проблему.
Как я объяснял ранее, если я сохраняю ссылку, например
self.struct = struct
клиент вылетает.
EDIT2: я размещаю определение COM-интерфейса и сигнатуру метода python по запросу eryksun. В своем вопросе я немного упростил проблему, чтобы упростить обзор. Фактический метод возвращает указатель на массив структур:
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
);
Относительно указателя на указатель **
в спецификации интерфейса сказано:
Обратите внимание на синтаксис size_is(,dwCount) в IDL, используемый в сочетании с указателями на указатели. Это указывает на то, что возвращаемый элемент является указателем на фактический массив указанного типа, а не указателем на массив указателей на элементы указанного типа.
И это метод python:
def ValidateItems(self, count, p_item_array, update_blob):
Предположим, что существует структура ctypes с именем OpcDa.tagOPCITEMRESULT()
.
Я создаю массив этих структур, вызывая
validation_results = (OpcDa.tagOPCITEMRESULT * count)()
errors = (HRESULT * count)()
и после установки полей всех элементов массива возвращаю указатели вот так:
return POINTER(OpcDa.tagOPCITEMRESULT)(add_results), POINTER(HRESULT)(errors)
EDIT3: я хочу суммировать комментарии к этому сообщению и то, что я узнал на данный момент:
Как предложил eryksun, упрощенный оператор возврата, по крайней мере, приводит к тому же поведению и проблемам, но более читаем:
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
В цикле, где я заполняю элементы массива, я перезаписываю содержимое новым элементом просто потому, что я был в отчаянии. Интересно, что с помощью этого кода я смог увидеть повреждение памяти уже на сервере, тогда как предыдущий код выявляет повреждение только на стороне клиента.
- When
index=0
,hServer
gets assigned its value. When I check the value, it's fine. - When
index=1
, but before the assignment of[0][1].hServer
, the value of[0][0].hServer
is still fine. - When
index=1
, but after the assignment[0][1].hServer = val_result
, the value of[0][0].hServer
has been corrupted in the same way as mentioned before. - When
index=2
and after the assignment[0][2].hServer = val_result
, the value of[0][1].hServer
is fine
Это означает, что hServer
только первого элемента массива частично перезаписывается после того, как второму элементу будет присвоено новое значение.
Я предполагаю, что память для val_result первого цикла каким-то образом освобождается и перезаписывается, хотя я думал, что назначение some_pointer[0] = new_value
фактически копирует содержимое как этот пост а> предполагает.
Но теперь это становится еще более странным. Когда я помню val_result
в списке python, например, например.
self.items.append(val_result)
повреждение на стороне сервера исчезло. Но я снова получаю COMError
на клиенте.
Проблема в том, что эта загадочная ошибка COMError не вызвана (уловимой) ошибкой на сервере. Кажется, все работает нормально. Это должно быть вызвано внутренностями смешивания COM.
Любые предложения, как действовать или получить больше информации о том, что происходит внутри COM?
POINTER(MyStruct)(struct)
содержит ссылку наstruct
в атрибуте_objects
указателя. Ошибка в связанном вопросе заключается в том, что объектbytes
используется в качестве указателя без сохранения ссылки. Он должен использоватьDISK_GEOMETRY_EX.from_buffer_copy
. - person Eryk Sun   schedule 13.12.2013struct
? Освобождается ли память после возврата метода python? Другими словами, нужно ли сохранять ссылку на объект или достаточно вернуть указатель? - person jaw   schedule 14.12.2013return 42
. В противном случае вы получите ошибку типа. - person jaw   schedule 16.12.2013return add_results, errors
работает с точки зрения того, что не выдает ошибку типа. Однако он ведет себя аналогичноPOINTER(tagOPCITEMRESULT)(add_results), POINTER(HRESULT)(errors)
, поскольку данные, которые получает клиент, также частично перезаписываются. Мы также попробовали реализацию с низким рычагом. Тип выходных аргументовLP_LP_tagOPCITEMRESULT
. Когда мы разыменовываем второй уровеньp_validation_results[0][0]
, мы получаем ошибку указателя NULL на стороне сервера. Использованиеp_validation_results[index] = POINTER(tagOPCITEMRESULT)(tagOPCITEMRESULT())
приводит к ошибке на стороне клиента. - person jaw   schedule 16.12.2013validation_results = (OpcDa.tagOPCITEMRESULT*count)()
, затем заполнил поляvalidation_results[index].hServer = server_item_handle
и установил массив, как вы предложилиp_validation_results[0] = validation_results
. К сожалению, это приводит к той же проблеме.hServer
первого элемента массива частично перезаписывается. - person jaw   schedule 16.12.2013return validation_results, errors
уже делает для вас автоматически. - person Eryk Sun   schedule 16.12.2013p_validation_results.contents = cast(validation_results, POINTER(OpcDa.tagOPCITEMRESULT))
приводит к ошибке доступа к нулевому указателю на стороне клиента, поэтому это явно не эквивалентно использованию оператора нижнего индекса. - person jaw   schedule 16.12.2013p_validation_results[0] = _midlSAFEARRAY(OpcDa.tagOPCITEMRESULT).from_param(validation_results)
мы получаем ошибку типаCannot create SAFEARRAY type VT_RECORD without IRecordInfo.
. У вас есть опыт или идеи по этому поводу? - person jaw   schedule 16.12.2013