[英]System.InvalidCastException trying to build a COM client and a COM server in C#
我正在嘗試構建一個 COM 客戶端,它必須在 C# 中實例化一個進程內 COM 服務器。 我做了兩個 C# 項目:一個用於客戶端,一個用於服務器。 兩個項目都使用 x86 目標平台,后者設置了“注冊 COM 互操作”選項。 我在 64 位 Windows 7 上使用 Visual Studio 2013 並使用 .net 4 進行編譯。
這是服務器代碼:
using System.Runtime.InteropServices;
namespace ComRibbonApplicationMenuServer
{
[ComVisible(true)]
[Guid("CCF43AAC-0822-4C36-90FD-2AFF7B94E71D")]
public interface IComRibbonApplicationMenuServer
{
[DispId(1)]
int OpenWindow();
}
[ComVisible(true)]
[Guid("38B1DE85-BC15-48E1-AFAF-4A7EA506256B")]
[ClassInterface(ClassInterfaceType.None)]
public class ComRibbonApplicationMenuServerClass : IComRibbonApplicationMenuServer
{
public ComRibbonApplicationMenuServerClass()
{
// Needed for COM
}
public int OpenWindow()
{
return 33;
}
}
}
這是客戶端:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace ComRibbonApplicationMenuClient
{
[Guid("CCF43AAC-0822-4C36-90FD-2AFF7B94E71D")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
interface IComRibbonApplicationMenuServer
{
void OpenWindow();
}
[ComImport, Guid("38B1DE85-BC15-48E1-AFAF-4A7EA506256B")]
class ComRibbonApplicationMenuServerClass
{
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
ComRibbonApplicationMenuServerClass comServerClass = new ComRibbonApplicationMenuServerClass();
IComRibbonApplicationMenuServer comServer = (IComRibbonApplicationMenuServer)comServerClass;
comServer.OpenWindow();
}
}
}
結構new ComRibbonApplicationMenuServerClass();
拋出InvalidCastException HResult=-2147467262
。
我能做什么?
編輯:
感謝漢斯的回答。
如果我理解正確,正如我所懷疑的那樣,不允許在沒有一些黑客攻擊的情況下為 dotnet COM 服務器創建 dotnet COM 客戶端。
正如您已經猜到的那樣,我正在嘗試做的是測試一個 dotnet 程序,我發現該程序在作為獨立應用程序執行時可以正常工作,但是當由另一個應用程序通過 COM 執行時會崩潰(僅在 Windows XP 中)。
為了重現這個問題,我需要構建一個簡單的測試程序,它必須啟動服務器並調用一個方法來執行一些可能導致堆棧溢出異常的指令。 這兩個程序都使用 GUI,這可能是問題的一部分。
為了簡單起見,我首先嘗試為 COM 客戶端制作 winform 程序,但是,閱讀您的回答后,我想我必須制作一個 MFC 應用程序來測試 COM 場景。 你怎么認為?
拋出一個 InvalidCastException HResult=-2147467262。
你得到一個更詳細的異常信息,它會告訴你,中投失敗,原因是E_NOINTERFACE錯誤返回。 這通常是,相當難以診斷,除了在這種情況下,實在是沒有界面。 您在客戶端中使用的聲明與服務器中使用的聲明嚴重不匹配:
客戶端中的 ComRibbonApplicationMenuServerClass 聲明沒有實現任何接口。 由於您隨意省略了 [ClassInterface(ClassInterfaceType.None)],.NET 將自動生成一個。 它有一個永遠不會匹配服務器的隨機 [Guid],從而產生 E_NOINTERFACE 錯誤。
你任意給了客戶端接口聲明 [InterfaceType(ComInterfaceType.InterfaceIsDual)] 屬性。 服務器忽略它,因此使用默認值 ComInterfaceType.InterfaceIsIDispatch。 這樣的接口只能稱為后期綁定,客戶端必須使用IDispatch。 這意味着如果您在第一項中修復了問題,它仍然會失敗,因為服務器實際上並未實現該接口。 在 C# 中,您必須使用dynamic關鍵字才能使用這樣的服務器。
顯然,客戶端使用與服務器完全相同的聲明是絕對必要的,通常通過在服務器上使用Tlbexp.exe
來生成類型庫來確保。 你進入這個pickle的原因有點可想而知,IDE將拒絕讓你添加對類型庫的引用,它可以看到服務器是在.NET中實現的,並告訴你添加一個普通的.NET引用。 這是相當不錯的建議,但它不是在所有意義的使用COM當正常.NET方式在一個優越得多的方式已經運作。
您可以通過在 Tlbexp.exe 生成的互操作庫上使用反編譯器來欺騙機器,並將它們復制/粘貼到客戶端,從而確保您有完全匹配。 或者通過在客戶端應用程序中使用帶有動態關鍵字的后期綁定,無論如何都需要,因為服務器使用ComInterfaceType.InterfaceIsIDispatch 。 也不那么痛苦,因為您不必在更改服務器時重復執行復制/粘貼步驟。
但是您確實需要記住,您在執行此操作時實際上並未使用 COM,CLR 本身足夠智能,可以看到 .NET 代碼與 .NET 代碼對話,並且將跳過創建 RCW 和 CCW。 因此,如果您這樣做是為了測試,請記住,您實際上並沒有按照在真實客戶端應用程序中使用的方式測試服務器。 您不妨通過實際添加 .NET 引用來測試它。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.