我是一名刚毕业的计算机科学学士.就像今天的大多数学校一样,他们不再教学生C或推进C++(只有C++的入门课程......有教学指针).课程中规定的标准编程语言是C#(.NET堆栈).
就在最近,我被聘为初级软件开发人员.95%的代码库都是用C++编写的,我们的产品使用的是COM/DCOM.另外5%是在.NET中.我目前的职责是维护一个用.NET(ASP.NET)编写的项目,我不需要学习C++和其他技术YET.但我想尽快学习COM,以便我可以帮助其他项目.
所以我正在寻求这个社区的建议,我将如何学习COM.我目前的问题如下:
谢谢!:)
PS:我应该将其标记为社区维基吗?
Excel 2007,Windows Vista,VB.NET,带有.NET 3.5 sp2的Visual Studio 2008,MSI安装程序包.
我有一个用VB.NET编写的Excel UDF.它作为COM服务器公开,因为您无法直接在.NET语言中创建Excel UDF.安装是一个真正的痛苦,因为没有任何安装设置似乎正确; 它们都没有为您提供一个安装包,它将COM服务器放在客户端计算机上,注册了服务器,注册了类型库,并且组件在Excel 2007的自动化服务器列表中可见.
以下是类型库的安装设置,它们的缺陷在编译时和安装时显而易见:
vsdrfComSelfReg
vsdrfDoNotregister
vsdrfCOM
正确的设置应该是vsdrfCOM,如解释在这里:
问:任何人都可以告诉vsdrfCOM在Visual Studio的安装项目中的含义吗?当我在安装项目中添加文件的属性中检查属性"注册"时,它可用.
答:这意味着Visual Studio将在构建时提取COM注册数据并将其放入MSI文件(主要是MSI文件的注册表表,但也包括类表).因此,当您安装它时,您的代码不需要自行注册,因为文件被复制到磁盘并创建了注册表项.它还将通过向MSI的TypeLib表添加条目来创建类型库注册.
许多困难似乎都是针对Vista的.特别是,使用REGCAP实用程序从.TLB文件生成.REG文件在Vista中不起作用.如果不是这样,也许这个建议会很有用.相反,它生成空的.REG文件.
我已经尝试了StackOverflow帖子中的所有建议.该帖子对技术问题有很好的描述:
"引用"对话框中的条目来自HKCR\TypeLib注册表项,而不是来自HKCR\CLSID.如果您的程序集没有显示在"引用"对话框中,但已编译的DLL仍然可以使用您的COM程序集,则表示已为程序集正确注册了类和接口,但类型库本身没有.
任何人都知道如何使安装注册组件和类型库?我无法访问Windows XP计算机.
详细说明为什么这很糟糕
任何编译的代码都不需要.TLB来调用它.我没有像你一样尝试部署Excel自动化加载项,但我的猜测是UDF应该加载并运行得很好.
它在Excel中不太像.
据我所知,用户必须从命令行运行regasm.exe才能使Excel UDF/COM服务器可用.如何告诉人们从命令行运行regasm以安装Excel的加载项?
编辑2009-10-04
Mike的评论和指示非常棒.我不知道的关键是安装程序有一个内置的注册表编辑器,用于添加注册表项.哦,Microsoft安装程序没有调用带有属性ComRegisterFunctionAttribute的安装函数.我已经从他引用的来源获得了关于编写安装程序功能的指示.
根据这篇文章,如果我用"Both"或"Free"线程模型注册我的COM对象,该对象必须完全是线程安全的.具体而言,必须同步对全局共享变量的所有访问,并且还必须同步对成员变量的所有访问.这是很多努力.
现在我明白能够将我的对象注册为使用"Free"线程模型是有利的,并且可能值得付出使其完全线程安全的代价.但是为什么我要做所有相同的操作并使用"Both"线程模型注册我的对象呢?有什么好处?如何选择"两者"和"免费"?
当我在COM中开发时,我总是看到(void**)类型转换如下.
QueryInterface(/* [in] */ REFIID riid,/* [out] */ void** ppInterface)
Run Code Online (Sandbox Code Playgroud)
它的确切含义是什么?
恕我直言,它告诉编译器不要强制执行类型验证,因为在编译时客户端代码不知道ppInterface指向的类型.
谢谢~~~
我这样理解:
void*p表示 AnyType*p
void**pp 表示指向AnyType的指针*
如果void**pp表示"指向void*的指针",那么编译器在看到它时会做什么检查?
为什么QueryInterface()呼叫总是跟着Release()呼叫?例如,我看过MSDN的示例代码如下:
HRESULT hr = S_OK;
CDecoder *pObj = new CDecoder(&hr);
if (SUCCEEDED(hr))
{
*ppv = NULL;
hr = pObj->QueryInterface(riid, ppv);
}
pObj->Release();
return hr;
Run Code Online (Sandbox Code Playgroud)
有人可以解释Release()这里打电话的意图吗?
我正在处理大型多线程C#应用程序处理COM互操作串.其他开发人员和我有充分的机会不小心从MTA线程调用单线程公寓(STA) COM对象,并从他们未创建的STA线程.性能低迷,跨线程编组是主要的嫌疑.
是否有一个很好的方法来测试跨公寓编组?更好的是,是否有一种防御性编程技术来测试给定的COM对象属于这个线程的公寓?
我最接近的是一个关于可疑代码的防御声明:
Debug.Assert(Thread.CurrentThread.GetApartmentState() == ApartmentState.STA);
suspiciousComInterface.SomeMethod();
Run Code Online (Sandbox Code Playgroud)
虽然如果我们的BackgroundWorker线程正在调用STA对象,这将警告我们,我特别担心STA线程正在使用在另一个STA线程中创建的COM运行时可调用包装器(RCW)对象.
一个在线源认为,这是不可能的(http://www.pcreview.co.uk/forums/detecting-cross-apartment-com-calls-t2450589.html),在CLR掩盖了太多的COM代理的使对象在高级别可访问的对象.
我无法相信这是唯一的答案.谢谢!
这可能是一个菜鸟COM问题,但谷歌搜索引发的问题多于提供答案:
对于本地COM实例,使用"operator new"而不是CoCreateInstance是否安全?
我做了什么:
我 通过使用公共继承实现了IOperationsProgressDialog接口 http://msdn.microsoft.com/en-us/library/windows/desktop/bb775368(v=vs.85).aspx,从而也实现了IUnknown接口.
我通过"new RecyclerProgressCallback"创建了一个实例,并将其放入COM-Ptr进行终身管理."RecyclerProgressCallback"是我派生类的名称.
我在IFileOperation :: SetProgressDialog中使用此实例 http://msdn.microsoft.com/en-us/library/windows/desktop/bb775803(v=vs.85).aspx
简介:我的方法似乎"似乎"起作用,但我不相信它,围绕COM对象创建的过多令人不安的信息只依赖于可观察行为.
是否有任何微妙的风险,谬误或其他问题?谢谢!
我正在编写一个Excel RTD服务器实现,我被困在一个实现的coclass的样板上IDispatch.我无法访问ATL,但我使用的是ActiveQt,虽然我对如何在原始C或C++中执行此操作感兴趣.如何正确实现IDispatchCOM服务器中的方法?
一如既往,文档非常糟糕.到目前为止我所读的内容如下:
IDispatch方法调用委托给某些人ITypeInfo是更好的做法.它是否正确?ITypeInfo自己?LoadTypeLib()和系列(后面看ITypeLib::GetTypeInfo())?这种LoadTypeLib()方法似乎适合COM 客户端访问某些库的类型信息,而不适用于尝试内省自身的COM服务器.我对么?
这是一个非常常见的问题,我决定问这个问题,因为这个问题在今天可能会有不同的答案.希望这些答案有助于理解使用COM对象的正确方法.就个人而言,在对此主题发表不同意见后,我感到非常困惑.
在过去的5年中,我曾经使用COM对象,规则对我来说非常清楚:
你们中的一些人在阅读完最后一行后可能会感到沮丧,这就是我所知道的如何正确创建/发布Com对象,我希望能得到更清晰无误的答案.
以下是我在这个主题上找到的一些链接.他们中的一些人告诉他们需要调用ReleaseComObject而其中一些不需要.
"...在VSTO场景中,您通常不必使用ReleaseCOMObject......."
"...您应该使用此方法释放包含引用的基础COM对象......"
更新:
这个问题被标记为过于宽泛.根据要求,我将尝试简化并提出更简单的问题.