У нас была похожая проблема, некоторые структуры, когда передавались из C # в C ++ через COM, работали в 32-битном COM, но были повреждены в 64-битном.
Я потратил некоторое время на изучение этого потокабыло также полезно.
Похоже, что TlbImp.exe пытается упаковать каждую структуру как выровненные по 4 байта, и только если она не соответствует определенным критериям, упаковывает структуру как выровненную по 8 байтов.Это делается как для 32-битного, так и для 64-битного режима.
Но у него есть странные правила для его определения.Я обнаружил, что для x64, если структура имеет только 4-байтовые целые числа, перечисления и списки защиты, она упаковывается как выровненные по 4 байта, в противном случае она упаковывается как выровненные по 8 байтов.Для x86 структура упаковывается как выровненные по 8 байт, когда в ней присутствует 64-битная переменная, в противном случае она упаковывается как выровненная по 4 байта.Возможно, есть и другие варианты, но это только то, что мы нашли в нашем продукте.
C ++, с другой стороны, с его упаковкой по умолчанию (8 байт), упаковывает каждый тип, выровненный минимум на 8 байт илиразмер члена.
Таким образом, для 32-разрядного режима проблем не возникает, поскольку нет разницы между выравниванием в 4 и 8 байтов, если в структуре нет элементов размером 8 байтов.Но если они есть, TlbImp.exe упаковывает структуру как выровненную по 8 байтам, так что это также соответствует C ++.
Для 64-битного режима, однако, есть случай, когда структура имеет 8-байтовый член, но упакован как выровненный 4 байта.В нашем случае это когда есть SAFEARRAY.Если структура имеет только целые числа SAFEARRAY и 32- (или меньше?) Битов, она упаковывается в 4 байта, выровненных для платформы x64.Но, поскольку SAFEARRAY является 64-битным указателем на стороне C ++, он упакован там по-разному, заполняя области перед SAFEARRAY до 8-байтовых полей.Я не знаю, является ли это ошибкой или функцией.
Чтобы исправить эту проблему, мы вставили инструкции C ++ pragma pack перед такими 4-байтовыми выровненными структурами в 64-битной версии.Мы определили такие структуры, декомпилировав сборку с помощью ildasm.exe и выполнив поиск «.pack 4» (спасибо Ciaran за подсказку).
Такая структура в IDL выглядит следующим образом:
// some of our structures must be 4 bytes aligned because TlbImp packs them this way
cpp_quote("#pragma pack(push, 4)")
typedef [uuid("3F253C09-D7F5-3BE-9698-00CB49A7005C"),
helpstring("SOME structure"),
version(5.1)] struct SOMEINFO
{
long ItemsActive;
SAFEARRAY(BSTR) ItemIDs; // without pack 4 it would be 8 bytes aligned on x64
SAFEARRAY(long) ItemRuns;
SAFEARRAY(SOMEFunctions) ItemFuncList;
SAFEARRAY(VARIANT) ItemFactors;
long MIndex;
SAFEARRAY(VARIANT) DeltaItems;
} SOMEINFO;
cpp_quote("#pragma pack(pop)") // matches pack(push, 4) placed before the structure
В качестве альтернативы, мы могли бы просто добавить к этой структуре 64-битную фиктивную переменную или BSTR, чтобы принудительно вставить ее в .pack 8,но мы не хотели менять интерфейс.
Итак, мы в основном следуем TlbImp по его странной логике.Но в нашем коде есть только несколько таких структур, так что это хорошо для нас.Таким образом, и 32-, и 64-разрядные COM-устройства правильно передают все структуры.