我很难找到如何访问C++应用程序提供的COM接口,并从C# .NET应用程序中使用它。
我尝试从我的C#应用程序中访问COM对象(它是由正在运行的进程提供的),如下所示:
object obj = Marshal.GetActiveObject("MyLibrary.Application");
MyLibrary.IMainApp app = (MyLibrary.IMainApp)obj;obj的值是非空的,但是当我试图转换它时,我得到了System.InvalidCastException。
我尝试实现一个自定义包装器,并将其链接到TlbImp.exe生成的DLL中。两者都产生了同样的例外。
似乎有很多关于互操作的文档,但这对我来说没有意义--主要是因为我不懂COM--(我被困于维护别人开发的代码),我正一头扎进C#、.NET和WPF的可怕世界。
这是我在C++端的资料.
IDL文件
实际上,扩展是.odl。我不知道这是不是另一回事。请注意,我已经用GUID-1、GUID-2等占位符替换了GUID.
import "oaidl.idl";
[ uuid(GUID-1), helpstring("My Type Library"), version(1.1) ]
library MyLibrary
{
importlib("stdole32.tlb");
// Primary dispatch interface for CMainApp
[ uuid(GUID-2) ]
dispinterface IMainApp
{
properties:
[id(1)] BOOL Mode;
methods:
[id(2)] void Quit();
[id(3)] void LogEventMessage(BSTR szMessageType, BSTR szMessage);
[id(4)] BOOL GetNoteDetails(BSTR szQualifiedName, BSTR* lpbstrOperator, DATE* lpdtDate);
};
// Class information for CMainApp
[ uuid(GUID-3) ]
coclass Application
{
[default] dispinterface IMainApp;
};
};示例用法
当另一个C++应用程序想要调用一个函数(例如Quit)时,它们会这样做:
CLSID clsid;
HRESULT hr;
LPUNKNOWN lpUnk;
LPDISPATCH lpDisp;
BOOL bReturn = FALSE;
// Get Class ID
if (CLSIDFromProgID( OLESTR("MyLibrary.Application"), &clsid ) == S_OK )
{
// Get Active Object from ROT
if ( ( hr = GetActiveObject( clsid, NULL, &lpUnk ) ) == S_OK )
{
hr = lpUnk->QueryInterface( IID_IDispatch, (LPVOID*)&lpDisp );
lpUnk->Release();
if ( hr == S_OK )
{
CMainApp oMainApp;
oMainApp.AttachDispatch( lpDisp );
TRY
{
oMainApp.Quit();
bReturn = TRUE;
}
CATCH( CException, e )
{
bReturn = FALSE;
}
END_CATCH
}
}
}CMainApp类似乎是由IDL编译器自动生成的。该文件被编译到希望使用它的项目中,基本上可以执行大量的InvokeHelper调用。我很高兴能接受这正是它的工作方式,我希望C#不是那么复杂。
试图包装在C#中
我遵循了关于C#中COM互操作的MSDN指南。它暗示我可以使用TlbImp.exe,但它发出了足够多的警告,让我望而却步。另外,我不想再分发另一个DLL。所以我采取了另一种方法来编写一个包装器。
我就是这么写的:
using System.Runtime.InteropServices;
namespace MyLibrary
{
// Declare MainApp COM coclass
[ComImport, Guid("GUID-3")]
class MainApp
{
}
[Guid("GUID-2"), InterfaceType(ComInterfaceType.InterfaceIsDual)]
interface IMainApp
{
public void Quit();
public void LogEventMessage(
[In, MarshalAs(UnmanagedType.BStr)] string szMessageType,
[In, MarshalAs(UnmanagedType.BStr)] string szMessage );
public bool GetNoteDetails(
[In, MarshalAs(UnmanagedType.BStr)] string szQualifiedName,
[Out, MarshalAs(UnmanagedType.BStr)] out string lpbstrOperator );
out DateTime lpdtDate );
}
}有人能帮我明白这一点吗?也许可以指出我犯了一个明显的愚蠢的错误?
发布于 2013-05-01 09:38:32
您的示例C++代码正在通过IDispatch使用后期绑定。这是正确的方法,您的COM服务器不支持早期绑定。从IDL中可以看出,它使用的是分配接口而不是接口。
C#中的等效方法是使用C# dynamic关键字。如下所示:
dynamic obj = Marshal.GetActiveObject("MyLibrary.Application");
obj.Quit();发布于 2022-11-27 14:19:24
谢谢。我可以通过将ExcelWorkBook声明为动态来控制Excel中的每一项。
static void Main(string[] args)
{
Excel.Application oXL;
Excel._Workbook AWB;
Excel._Worksheet ASH;
dynamic ExcelWorkbook;
try
{
ExcelWorkbook = Marshal.GetActiveObject("Excel.Application");
AWB = ExcelWorkbook.ActiveWorkBook;
String naam = AWB.Name;
ASH = AWB.ActiveSheet;
oXL = ExcelWorkbook.Application();
object theValue = oXL.get_Range("A1", "A1").Value2;
ASH.Cells[1, 1] = "First Name";
ASH.Cells[1, 2] = "second Name";
ASH.Cells[1, 3] = "Third Name";
ASH.get_Range("A1", "D1").Font.Bold = true;
}
catch { }
String tekst="test";
}https://stackoverflow.com/questions/16312837
复制相似问题