Visual c++ 是否定义了COM接口中传递的结构打包?
我正在使用一个第三方COM服务器,它有自己的自定义接口,可以设置和获取结构作为它的一些属性。碰巧,我正在为客户端使用C++。我在下面的IDL文件中发布了一些具有代表性的代码,其中更改了名称并删除了GUID 是定义了结构的打包,还是我的客户机代码碰巧使用了COM服务器构建时使用的打包设置?在默认的C++编译器打包设置中更改的项目是否可能出错?是否有pragma pack设置可用于确保客户端编译器打包设置正确 在IDL或MIDL生成的头文件中,我看不到任何打包pragma或语句。如果客户端使用C#或VB,会发生什么?如果通过IDispatch机制调用,是否更清楚地指定了包装行为Visual c++ 是否定义了COM接口中传递的结构打包?,visual-c++,com,idl,Visual C++,Com,Idl,我正在使用一个第三方COM服务器,它有自己的自定义接口,可以设置和获取结构作为它的一些属性。碰巧,我正在为客户端使用C++。我在下面的IDL文件中发布了一些具有代表性的代码,其中更改了名称并删除了GUID 是定义了结构的打包,还是我的客户机代码碰巧使用了COM服务器构建时使用的打包设置?在默认的C++编译器打包设置中更改的项目是否可能出错?是否有pragma pack设置可用于确保客户端编译器打包设置正确 在IDL或MIDL生成的头文件中,我看不到任何打包pragma或语句。如果客户端使用C#或
struct MyStruct
{
int a, b;
};
[
object,
uuid( /* removed */ ),
dual,
nonextensible,
pointer_default(unique)
]
interface IVideoOutputSettings : IDispatch{
[propget, id(1), HRESULT MyProperty([out, retval] struct MyStruct* pVal);
[propput, id(1), HRESULT MyProperty([in] struct MyStruct newVal);
/* other methods */
};
根据此处的MIDL命令行开关参考,默认包装是沿着8字节边界: 如果更改包值,代码的其他部分更有可能首先中断,因为IDL文件通常是提前预编译的,而且很少有人会故意更改提供给MIDL的命令行开关(但有人会摆弄C-scope
#pragma pack
而忘记恢复默认状态的情况并不罕见)
如果有很好的理由更改设置,可以使用pragma pack
语句显式设置打包
幸运的是,没有任何一方更改了任何会干扰默认打包的设置。它会出错吗?是的,如果有人特意更改默认打包
当使用IDL文件时,详细信息通常被编译成类型库(.tlb),并且假设在使用相同的typelib时,服务器和客户端的平台是相同的。/Zp
开关的脚注中建议了这一点,因为某些值在某些非x86或16位目标上会失败。也可能存在32位64位转换情况,可能会导致期望值中断。不幸的是我不知道是否还有更多的情况出现,但默认的情况下,最小的麻烦工作
C#和VB没有任何内在行为来处理.tlb中的信息;相反,像tlbimp这样的工具通常用于将COM定义转换为可从.NET使用的定义。我无法验证C#/VB.NET与COM客户端和服务器之间的所有期望是否成功;但是,我可以使用特定的pragma设置来验证是否成功如果引用一个从在该设置下编译的IDL创建的.TLB,那么8个将工作。虽然我不建议违反默认的PrimaPACK,但如果您希望使用一个工作示例作为参考,这里有一些步骤要执行。我创建了一个C++ ATL项目和一个C项目来检查。
这里是C++的侧指令。< /P>
SomeFoo
的ATL简单对象。同样,没有更改默认值。这将创建一个名为CSomeFoo
的类,并将其添加到项目中isomofoo
,并添加了一个名为FooIt
的方法,该方法将struct BarStruct
作为名为theBar的[in]参数import "oaidl.idl";
import "ocidl.idl";
[uuid(D2240D8B-EB97-4ACD-AC96-21F2EAFFE100)]
struct BarStruct
{
byte a;
int b;
byte c;
byte d;
};
[
object,
uuid(E6C3E82D-4376-41CD-A0DF-CB9371C0C467),
dual,
nonextensible,
pointer_default(unique)
]
interface ISomeFoo : IDispatch{
[id(1)] HRESULT FooIt([in] struct BarStruct theBar);
};
[
uuid(F15B6312-7C46-4DDC-8D04-9DEA358BD94B),
version(1.0),
]
library SampleATLProjectLib
{
struct BarStruct;
importlib("stdole2.tlb");
[
uuid(930BC9D6-28DF-4851-9703-AFCD1F23CCEF)
]
coclass SomeFoo
{
[default] interface ISomeFoo;
};
};
在CSomeFoo
类中,下面是FooIt()
的实现
接下来,在C侧:
Foo
的属性,并通过将其设置为false禁用嵌入互操作类型SampleATL.FooStuff
,将[STAThread]
属性添加到Main()
(COM单元模型必须与进程内消费相匹配),并添加了一些代码来调用COM组件using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SampleATL.FooStuff;
namespace SampleATLProjectConsumer
{
class Program
{
[STAThread]
static void Main(string[] args)
{
BarStruct s;
s.a = 1;
s.b = 127;
s.c = 255;
s.d = 128;
ISomeFoo handler = new SomeFooClass();
handler.FooIt(s);
}
}
}
最后,它运行,我得到一个模式弹出窗口,显示以下字符串:
Size: 12, Values: 1 127 255 128
为了确保可以更改pragma pack(因为4/8字节打包是最常用的对齐方式),我按照以下步骤将其更改为1:
Size: 12, Values: 1 127 255 128
Size: 12, Values: 1 1551957760 129 3
#pragma pack(push, 1)
/* [uuid] */ struct DECLSPEC_UUID("D2240D8B-EB97-4ACD-AC96-21F2EAFFE100") BarStruct
{
byte a;
int b;
byte c;
byte d;
} ;
#pragma pack(pop)
Size: 7, Values: 1 127 255 128