例如,winsock libs在所有版本的visual studio中都能很好地工作.但是我在为所有版本提供一致的二进制文件时遇到了麻烦.使用VS 2005编译的dll在链接到2008年编写的应用程序时将无法工作.我将2k5和2k8升级到SP1,但结果没有太大变化.它的工作原理还可以.但是当它们将它包含在C#应用程序中时,C#应用程序会出现访问冲突错误,但使用经典的C++应用程序时,它可以正常工作.
当我提供dll时,我应该知道一个策略吗?
首先,除了传递DLL边界的普通旧数据之外,不要传递任何其他内容.结构很好.课不是.其次,确保所有权不被转移 - 即在dll边界上传递的任何结构都不会在dll之外被释放.因此,如果您导出X*GetX()函数,则会有相应的FreeX(X*)类型函数,确保分配的相同运行时负责取消分配.
下一步:获取DLL以链接到静态运行时.将来自多个第三方的dls组合在一起,每个第三方链接并期望不同的运行时,可能与应用程序预期的运行时不同,这是一种痛苦,可能迫使安装程序软件安装7.0,7.1,8.0和9.0的运行时 - 其中几个存在于不同的服务包中,这可能会也可能不会引起问题.善待 - 静态链接你的dll项目.
- 编辑:您无法使用此方法直接导出c ++类.在模块之间共享类定义意味着您必须具有同类运行时环境,因为不同的编译器或编译器版本将以不同方式生成修饰名称.
您可以通过将类导出为COM样式接口来绕过此限制...也就是说,虽然您无法以独立于运行时的方式导出类,但您可以导出一个"接口",您可以通过声明来轻松实现一个只包含纯虚函数的类......
struct IExportedMethods { virtual long __stdcall AMethod(void)=0; }; // with the win32 macros: interface IExportedMethods { STDMETHOD_(long,AMethod)(THIS)PURE; };
在您的类定义中,您从此接口继承:
class CMyObject: public IExportedMethods { ...
你可以通过制作C工厂方法来导出这样的接口:
extern "C" __declspec(dllexport) IExportedClass* WINAPI CreateMyExportedObject(){ return new CMyObject; }
这是一种非常轻量级的导出编译器版本和运行时独立类版本的方法.请注意,您仍然无法删除其中一个.您必须包含释放函数作为dll或接口的成员.作为界面的成员,它可能如下所示:
interface IExportedMethods { STDMETHOD_(void) Release(THIS) PURE; }; class CMyObject : public IExportedMethods { STDMETHODIMP_(void) Release(){ delete this; } };
您可以采用这个想法并继续使用它 - 从IUnknown继承您的接口,实现引用计数的AddRef和Release方法,以及查询vIn接口或其他功能的QueryInterface的能力.最后,使用DllCreateClassObject作为创建对象并获得必要COM注册的方法.所有这些都是可选的,但是,您可以轻松地通过C函数访问简单的接口定义.
我不同意Chris Becke的观点,同时看到了他的方法的优点.
缺点是您无法创建实用程序对象库,因为您被禁止跨库共享它们.
如何在VS版本中制作一致的dll二进制文件?
您的选择取决于编译器的不同之处.一方面,同一编译器的不同版本可以以相同的方式处理数据对齐,因此,您可以在DLL中公开结构和类.另一方面,您可能不信任其他库编译器或编译选项.
在Windows Win32 API中,他们通过"处理程序"处理问题.你这样做:
1 - 永远不要暴露结构.仅显示指针(即void*指针)
2 - 此结构数据的访问是通过将指针作为第一个参数
3的函数 - 此结构的指针分配/释放数据是通过函数
这样,您可以避免在结构更改时重新编译所有内容.
这样做的C++方法是PImpl.见http://en.wikipedia.org/wiki/Opaque_pointer
它与上面的void*概念具有相同的行为,但是使用PImpl,您可以使用RAII,封装和强类型安全的利润.这需要兼容的装饰(相同的编译器),但不是相同的运行时或版本(如果版本之间的装饰是相同的).
希望将来自不同编译器/编译器版本的DLL混合在一起,或者是灾难的处方(正如您在问题中所解释的那样),或者是繁琐的,因为您已经放弃了大部分(如果不是全部)代码的C++解决方案以回退到基本的C编码, 或两者.
我的解决方案是:
1 - 确保所有模块都使用相同的编译器/版本进行编译.期.
2 - 确保所有模块都被编译为与同一运行时
3 动态链接 - 确保对所有第三方模块进行"封装",无法控制(无法使用编译器进行编译),正如所解释的那样Chris Becke 如何在VS版本中制作一致的dll二进制文件?.
请注意,要求应用程序的所有模块都针对相同的编译器和相同版本的编译器进行编译,这并不奇怪也不离谱.
不要让任何人告诉你混合编译器是一件好事.它不是.对于大多数人来说,混合编译器的自由度可以通过从建筑物的顶部跳跃而享受同样的自由:你可以自由地这样做,但通常,你只是不想那样做.
我的解决方案使您能够:
1 - 导出类,因此,制作真正的,非阉割的C++库(例如,您应该使用Visual C++的__declspec(dllexport))
2 - transfert分配所有权(在使用分配时未经您的同意而发生) /或解除分配内联代码,或STL)
3 - 不要因为每个模块都有自己的运行时版本(即内存分配和C或C++ API使用的一些全局数据)这一事实而烦恼
请注意,这意味着您不应该将模块的调试版本与其他模块的发行版本混合使用.您的应用程序完全处于调试状态或完全处于发布状态.