标签: com

是否可以通过路径而不是GUID在托管项目中引用COM DLL?

我有一个托管(实际上是asp.net)项目,它引用了一个COM DLL.现在,.csproj中的引用如下所示:

<COMReference Include="thenameinquestion">
  <Guid>{someguidhere}</Guid>
  <VersionMajor>1</VersionMajor>
  <VersionMinor>0</VersionMinor>
  <Lcid>0</Lcid>
  <WrapperTool>tlbimp</WrapperTool>
</COMReference>
Run Code Online (Sandbox Code Playgroud)

这是有效的,但它有一个令人遗憾的结果,DLL需要在构建机器上注册,这意味着(除其他外)在同一构建机器上构建使用不同版本的DLL的项目的多个版本是不方便的.

MSDN显示ResolveComReference任务看起来它做的正确,但我的google-search-fu还不够好,无法提供其使用的实际示例.有可能做我想要的吗?我是在正确的轨道上吗?

.net com msbuild

10
推荐指数
2
解决办法
2796
查看次数

在Visual C#2008 Express Edition中设置32位x86构建目标?

我正在构建一个加载32位COM dll的C#应用​​程序.编译的应用程序在32位Windows上正常运行,但在64位Windows上运行barfs,因为它无法加载32位COM.有没有办法在VC#2008 Express Edition中设置32位构建目标?

或者,有没有办法强制编译到AnyCPU构建目标的.NET应用程序在64位Windows上以32位模式运行?

c# com 32-bit visual-studio-2008

10
推荐指数
2
解决办法
3万
查看次数

C# - 将Nullable <T>暴露给COM的好方法

我们正在努力将程序集暴露给COM.

除此之外,我们频率使用可空值,例如long?,DateTime?等.这些是泛型类型,不能暴露给COM.

什么是COM的这些数据类型的良好替代品?

我们尝试过以下方法:

//Original CustomerID property in class
public long? CustomerID
{
   get;
   set;
}

//Explicit COM interface
long IComInterface.CustomerID
{
  get { return CustomerID.GetValueOrDefault(); }
  set { CustomerID = value; }
}
Run Code Online (Sandbox Code Playgroud)

问题是,我们需要一种通过COM来回传递"null"的方法.使用-1或0之类的数字将不起作用,因为它们也是有效值.

我们不得不使用nullables b/c这些最初来自我们的数据库模式.

.net c# com nullable

10
推荐指数
1
解决办法
1282
查看次数

CoWaitForMultipleHandles API的行为与记录不符

这是由我正在研究的另一个问题引发的.阅读可能太长了,所以请耐心等待.

显然,在MSDN CoWaitForMultipleHandles没有记录的行为.

下面的代码(基于原始问题)是一个控制台应用程序,它启动一个带有测试Win32窗口的STA线程并尝试发布并抽取一些消息.它做了三个不同的测试CoWaitForMultipleHandles,都没有 COWAIT_WAITALL标志.

测试#1旨在验证这一点:

COWAIT_INPUTAVAILABLE如果设置,如果队列输入存在,则对CoWaitForMultipleHandles的调用将返回S_OK,即使已使用对另一个函数(如PeekMessage)的调用看到(但未删除)输入.

这不会发生,CoWaitForMultipleHandles阻塞并且在发出等待句柄之前不会返回.我不认为任何未决的消息应被视为输入(与同MWMO_INPUTAVAILABLEMsgWaitForMultipleObjectsEx,它按预期工作).

测试#2旨在验证这一点:

COWAIT_DISPATCH_WINDOW_MESSAGES允许从ASTA或STA中的CoWaitForMultipleHandles分派窗口消息.ASTA中的默认值是没有调度的窗口消息,STA中的默认值只是一小部分特殊的消息调度.该值在MTA中没有意义,将被忽略.

这也不起作用.当CoWaitForMultipleHandles仅使用COWAIT_DISPATCH_WINDOW_MESSAGES标志调用时,它会立即返回错误CO_E_NOT_SUPPORTED(0x80004021).如果它是一个组合COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS,则呼叫阻止但不抽取任何消息.

测试#3演示了我可以CoWaitForMultipleHandles使用调用线程的Windows消息队列的唯一方法.它是一个组合COWAIT_DISPATCH_WINDOW_MESSAGES | COWAIT_DISPATCH_CALLS | COWAIT_INPUTAVAILABLE.这确实是泵送和发送消息,尽管显然它是一种无证件的行为.

测试代码(可立即运行的控制台应用程序):

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleTestApp
{
    static class Program
    {
        // Main 
        static …
Run Code Online (Sandbox Code Playgroud)

.net c# windows com winapi

10
推荐指数
1
解决办法
5106
查看次数

接口版本控制

在我工作的地方,我们刚刚发布了一个使用dll的功能,该功能严重依赖于接口.dll和所有客户端应用程序都是用Delphi编写的.无需注册.这个DLL不是一个合适的com服务器.唯一的限制是dll和客户端应用程序都可以访问包含接口的单元.这允许我们将复杂数据传递给使用此dll的应用程序,而不需要求助于记录指针,数组或极其繁重的函数签名,也不需要bpl或完全兼容的COM服务器引入的包袱.

看起来它解决了我们遇到的一个没有缺点的重大问题.不幸的是有一个缺点.接口发布后对接口的任何更改都需要随后重新编译该接口的任何使用者.这对于属于同一发布周期的项目来说很好,但是我们的一些项目有不同的发布计划.

我对此进行了一些研究,看起来通常的做法是引入一个继承自之前发布的接口的新接口,而不是修改原始接口.

type
  IOriginalInterface = interface
  ['{8B598EC1-AD92-4144-A1BE-9062C5EA0748}']
    procedure DoSomething;
  end;

  INewInterface = interface(IOriginalInterface)
  ['{DD9D9DE0-0F87-4BC5-803C-74C8AB0F3E39}']
    procedure DoSomethingElse;
  end;
Run Code Online (Sandbox Code Playgroud)

这可确保针对原始接口编译的旧可执行文件继续有效.

我注意到使用RAD Studio打开工具api时,只要引入了新的接口,就会重命名接口.因此,不是为最新的界面提供新名称,而是最新的界面获取原始界面的名称,并重命名原始界面.

type
  IOldInterface = interface
  ['{8B598EC1-AD92-4144-A1BE-9062C5EA0748}']
    procedure DoSomething;
  end;

  IOriginalInterface = interface(IOldInterface)
  ['{DD9D9DE0-0F87-4BC5-803C-74C8AB0F3E39}']
    procedure DoSomethingElse;
  end;
Run Code Online (Sandbox Code Playgroud)

这显然适用于RAD Studio团队以及第三方扩展供应商.这可确保任何重新编译的客户端都将使用最新的接口,而无需更改任何代码.我认为这是有效的,因为名称是无关紧要的,并且在编译代码之后剩下的只是接口定义,使用GUID解析.

说了这么多,这是我们现在面临的界面版本问题的一个很好的解决方案吗?还有其他问题需要注意吗?

delphi com dll interface version

10
推荐指数
1
解决办法
251
查看次数

Delphi和COM:TLB和维护问题

在我工作的公司中,我们使用C#开发所有GUI,但应用程序内核主要是在Delphi 5中开发的(由于历史原因),在COM +中使用了很多组件.与这个非常具体的应用相关,我有两个问题:

  • Delphi和/或COM中经验丰富的人,您是否有任何工作场所可以使用错误的TLB接口?一些错误是:IDE在大型TLB的编辑过程中崩溃,丢失方法ID,TLB损坏等.在这里,我们还没有找到任何好的解决方案.实际上我们尝试升级做新的2007版本.但是新的IDE TLB接口具有我们之前发现的相同错误.

  • 你如何控制TLB版本?TLB文件采用二进制格式,冲突解决方案很难做到.我们尝试将接口描述导出到IDL并提交到CVS,但我们没有找到任何使用Delphi从IDL生成TLB的好方法.另外,Microsoft提供的MIDL工具没有正确解析我们从delphi导出的IDL文件.

delphi com typelib com-hell

9
推荐指数
3
解决办法
5402
查看次数

如何在ActiveX方法调用期间防止WPF事件处理程序的重入?

我们在WPF和STA应用程序中调用ActiveX组件上的方法.此调用是通过以下方式执行的:

res = ocx.GetType().InvokeMember(methodName, flags, null, ocx, args);
Run Code Online (Sandbox Code Playgroud)

...其中ocx是使用System.Windows.Forms.AxHost.GetOcx()方法检索的ActiveX对象.

此调用是在WPF事件处理程序中执行的,例如"鼠标单击".

现在问题.如果我们双击'鼠标点击'事件将触发,运行InvokeMember().但是,此调用期间,我们看到重新输入了"鼠标单击"事件.所以在同一个线程中,我们在调用堆栈上看到两次事件处理程序.这是非常意外的,我们正在努力防止这种情况发生.我们怎样才能防止这种情况发生?

我们能想到的唯一原因,为什么它偏偏就是:

  • COM对象是在另一个STA中创建的,因此我们正在执行需要编组的跨STA调用
  • 跨线程STA调用使用Windows消息向COM组件发送RPC请求
  • 跨线程STA调用使用Windows消息泵来接收RPC应答
  • 在等待期间,会出现另一种类型的事件(如"鼠标点击"),并在处理RPC应答之前处理此事件.
  • 这个RPC答案得到处理

我们试图解决问题的方法:

  • 在所有事件处理程序中使用lock().这不起作用,因为lock()将锁定一个线程,在这种情况下,它是重新进入事件处理程序的相同线程.
  • 使用自定义锁定,如'bool locked = false; if(!locked){locked = true; 的InvokeMethod(); ...; locked = false; }".这部分工作:它会丢弃事件而不是将它们排队等待,并且需要对我们所有的事件处理程序进行大量更改,这样做并不好.
  • 使用Dispatcher.DisableProcessing来停止(其他)消息的处理.这没有帮助:它会抛出异常,因为无论如何都要处理消息.
  • 在新线程中创建第二个调度程序,并通过Dispatcher.Invoke()运行ocx.InvokeMehod()以使其由另一个线程处理.这给出了'一个事件无法调用任何订阅者(来自HRESULT的异常:0x80040201)'(是的,我们还订阅了ActiveX对象的COM事件).
  • 使用Dispatcher.PushFrame()来停止发生事件处理.这也失败了.

一个可能有效,但不知道如何实现这一点的疯狂想法将创建一个新的消息泵作为WPF消息泵,可配置为临时只处理RPC调用.这与http://jmorrill.hjtcentral.com/Home/tabid/428/EntryId/430/WPF-MediaKit-Updates.aspx一致,但仍与此情况有所不同.

所以问题归结为我们如何能够像我们预期的那样同步进行ActiveX调用而不是异步?

更新

为了更清楚地说明所涉及的机制不仅仅是关于鼠标事件,而是"在执行旧事件时处理新事件"这一更普遍的问题,我将给出另一个堆栈跟踪示例:

上下文:我们有一个WPF网格,我们得到一个鼠标点击(Grid_MouseDown),我们有一个ActiveX对象,我们在其上执行方法'CloseShelf'.打开架子需要时间,所以我们订阅了事件'EventShelfClosed',它在EventShelfClosed的事件处理程序中将调用'ListShelf'来知道剩下哪些架子.

这就是托管堆栈跟踪的样子(Hans要求一个非托管堆栈跟踪,但我不知道如何获得一个):

MyAxWrapper.dll!MyAxWrapper.MyAxWrapper.InvokeMember(string methodName, System.Reflection.BindingFlags flags, object[] args, int refArgIdx) Line 53 C#
MyAxWrapper.dll!MyAxWrapper.LoggingMyAxWrapper.InvokeMember(string methodName, System.Reflection.BindingFlags flags, object[] args, int refArgIdx) Line 151 + 0x14 bytes    C#
MyAxWrapper.dll!MyAxWrapper.MyAxWrapper.InvokeMethod(string methodName, object[] args) …
Run Code Online (Sandbox Code Playgroud)

.net c# com wpf activex

9
推荐指数
1
解决办法
3719
查看次数

通过VC++中的COM实例化C#对象的类未注册错误

在VC++项目中,我试图创建一个包含在C#项目中的C#类的实例(通过COM).

事实

  • C#和C++项目都是使用.NET 4.0编译的
  • C#.dll正在使用regasm/codebase"CSharpProjectName.dll" " 进行注册,并且Windows命令提示符报告,"类型已成功注册."
  • 在c ++项目中,我尝试在C#项目中创建一个类的实例,但是我得到的HRESULT为0x80040154 - 类未注册

以下是我尝试从C#.dll创建.NET对象实例的示例.我试图实例化的具体类叫做Employee,为了简单地提出我的问题,实现了IPerson接口:

    CSharpProjectName::IPersonPtr pPersonPtr;
    HRESULT hr = pPersonPtr.CreateInstance(CSharpProjectName::CLSID_Employee);
Run Code Online (Sandbox Code Playgroud)

即使我使用"regasm/codebase"注册c#.dll并确认注册表中存在密钥,为什么我会收到"未注册类"错误?

任何帮助将不胜感激.谢谢!

.net c# com regasm visual-c++

9
推荐指数
1
解决办法
2万
查看次数

无法使用WiX注册DLL

我正在尝试使用WiX注册DLL.现在我知道并理解我应该使用heat并获取它来为我提取注册表信息,但热似乎失败,并显示以下错误消息:heat.exe : warning HEAT5150 : Could not harvest data from a file that was expected to be a SelfReg DLL: C:\Users\seb\Desktop\Development\addin.dll. If this file does not support SelfReg you can ignore this warning. Otherwise, this error detail may be helpful to diagnose the failure: Unable to load file: C:\Users\seb\Desktop\Development\addin.dll, error: 193

我环顾四周,发现这篇文章解释了有关注册DLL的更多细节,但我想尽可能避免自注册路由.我还看到了下面手动注册DLL的提及,但是我不太了解注册表或COM对象,知道我甚至可以获得CLSID,ProgId以及已经提到的任何其他信息.

如果有人能指出我正确的方向或向我解释我需要做什么来运行DLL通过heat它将不胜感激.

com dll wix wix3.5

9
推荐指数
2
解决办法
5960
查看次数

VBA中自定义COM类中的IntelliSense

有没有办法在VBA中自己构建的COM类中获取IntelliSense?

例如,在下面的示例中,每当我按下点(或ctrl + space for shortcut)时,我想显示"Number": 在此输入图像描述

我想,如果以某种方式解决了这个问题,我还会得到一些关于对象公共函数的信息:

在此输入图像描述

那么,有什么建议?

建议1:

在此输入图像描述

c# com intellisense vba visual-studio

9
推荐指数
1
解决办法
854
查看次数