【问题标题】:lpvoid to interface reference invalid cast exceptionlpvoid 到接口引用无效强制转换异常
【发布时间】:2017-12-16 11:42:51
【问题描述】:

我在将类转换为 LPVOID 并将其重新转换为接口类时遇到问题。这是简化的代码:

public interface class IEventRaiser
{
   void fireAppDisconnect()
   // some other methods
}

interface class ISpecificEventRaiser : IEventRaiser
{
   // some specific methods
}

public ref class ManagedItem
{
   ManagedItem()
   {
      eventRaiser = gcnew EventRaiser();
      LPVOID lP = reinterpret_cast<LPVOID>(GCHandle::ToIntPtr(GCHandle::Alloc(eventRaiser)).ToPointer();
      item = new UnmanagedItem(lP);
   }
   // some implementation
   ref class EventRaiser : public ISpecificEventRaiser
   {
      virtual void fireAppDisconnect();
      // other methods
   };

   EventRaiser^ eventRaiser;
   UnmanagedItem* item;
};

public class UnmanagedItem
{
   UnmanagedItem(LPVOID eventRaiser)
   {
      IEventRaiser^ r;
      IntPtr pointer(eventRaiser);
      handle = GCHandle::FromIntPtr(pointer);
      r = safe_cast<IEventRaiser^>(handle.Target); // InvalidCastException : Unable to cast object of type 'EventRaiser' to type 'IEventRaiser'.
   }
};

将 EventRaiser^ 转换为 IEventRaiser^ 应该没有问题,因为我之前尝试过。在尝试 LPVOID 对话之前,它运行良好。但是当我将它转换为 LPVOID 并将其重新转换为 IEventRaiser 时,它会抛出 InvalidCastException。如何正确通过 LPVOID 进行铸件?

【问题讨论】:

  • 亲爱的@HansPassant,它将一直存在到应用程序的生命周期中,我将在析构函数上调用handle.free()。我试图将引用包装为 IEventRaiser,但它引发了相同的异常。喜欢以下; ISpecificEventRaiser^ sEventRaiser = (ISpecificEventRaiser^)eventRaiser; lp = reinterpret_cast(GCHandle::ToIntPtr(GCHandle::Alloc(safe_cast(sEventRaiser ))).ToPointer();
  • 将这段代码 sn-p 修改成可以编译的形状是一项艰巨的工作。当我尝试时,无论哪种方式都可以正常工作,演员表不是问题。可能的错误是您对接口类型有多个定义。当您在 .h 文件中声明它并将它包含在属于两个不同项目的源文件中时,往往会发生这种情况。 .NET 中的类型标识不仅由命名空间和标识符名称定义,它还包括它所在的程序集的全名。使用程序集引用代替,因此只有一个接口定义。
  • 想一想,你之前已经遇到过这个问题了。您在previous question 中犯了完全相同的错误。
  • 亲爱的@HansPassant,我在最后一个问题后删除了 .h 文件。所以,接口类不应该被定义两次。但是,是的。看来这是一个组装问题。我打印了 IEventRaiser 句柄的组件名称,它是继承的组件名称!但我无法理解这怎么可能。在继承的项目中,基础项目只有一个头文件,并且仅用于本地类的头文件。并且该头文件不包含任何托管类型头文件。所以,接口类不应该被原生包含。

标签: casting c++-cli clr


【解决方案1】:

有一个指向我的非托管类的 LPVOID 指针的原因是摆脱重复的标头导入。所以,虽然我注意到包含的头文件,但 IEventRaiser 的头文件以某种方式被导入到继承的项目中。因此,正如 Hans Passant 在 cmets 上指出的那样,由于程序集名称不同,它会导致转换问题。我为解决这个问题所做的是将#ifdef 语句添加到基础项目的 IEventRaiser 头文件包括。像下面这样:

#ifdef BASE_DLL 
#include "IEventRaiser.h"
#endif

然后,我将“BASE_DLL”添加到预处理器定义中。它保证事件引发程序标头将被包含一次。另一种选择是使用前向声明来不将“IEventRaiser.h”头文件添加到非托管文件头中。它也经过了尝试和工作。

【讨论】:

    猜你喜欢
    • 2012-02-20
    • 1970-01-01
    • 2012-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-28
    • 2015-01-27
    • 2012-09-20
    相关资源
    最近更新 更多