如何在C#里调用COM的函数并取得返回值

qian_xu 2008-04-16 03:01:33
本人对COM一窍不通。客户提供了用COM写的dll,包括x.dll和xLib.dll(x代表dll的名称,以Lib结束的这个dll是不是相当于Type library?)。 现在需要调用其中的一个函数,把xLib.dll加到Solution下的References里后,可以在object browser里看到函数签名,但该函数的返回值为void,而com的函数返回值都是hresult类型,。我怎么才能取得这个COM函数的返回值?
如能赐教,不胜感激!
...全文
895 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
gomoku 2008-04-18
  • 打赏
  • 举报
回复
如果你非要这样做,那么手工写一个封装。用[PreserveSig]来声明不要把HRESULT吃掉。
你可以参考以下代码,记得要换掉Interface和coclass的GUID。


using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

[ComImport]
[Guid("A4E23A7E-C932-4A31-8455-2F137015DCB2")]
public interface INakeTTClass
{
[DispId(1)]
int Add(int a, int b, out int c);

[DispId(2)]
int GetAge();
}

[ComImport]
[Guid("73269D6A-0BB4-46B8-8062-7C8DCDEE81D3")]
public class NakeTTClass : INakeTTClass
{
[DispId(1)]
[PreserveSig] //<---------------
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public extern int Add(int a, int b, out int c);

[DispId(2)]
[PreserveSig] //<---------------
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
public extern int GetAge();
}

class Program
{
static void Main()
{
NakeTTClass nake = new NakeTTClass();
int sum;
int hresult = nake.Add(3, 4, out sum);
// hresult == S_OK == 0
// sum == 7

hresult = nake.GetAge();
// hresult = 123
}
}



原型:


//interface IMyTT : IDispatch{
// [id(1), helpstring("method Add")] HRESULT Add([in] int a, [in] int b, [out,retval] int* c);
// [id(2), helpstring("method GetAge")] HRESULT GetAge(void);
//};

STDMETHODIMP CMyTT::Add(int a, int b, int* c)
{
*c = a + b;
return S_OK;
}

STDMETHODIMP CMyTT::GetAge(void)
{
return (HRESULT)123;
}

qian_xu 2008-04-18
  • 打赏
  • 举报
回复
我在这里把我要说的整理一下,前面可能有点乱了。

我想要在C#里调用COM的函数,该函数没有[out, retval]类型的参数,因此把COM dll加到C#工程的引用里后,在Object Browser里面看到的是类似void FunctionName(Parameter, Paremeter)这样的函数签名。COM函数原型的HRESULT一般是用来返回错误值的,像S_OK表示成功执行,如果HRESULT的最高位是1,表示出错,封装者看到这样的值一般会抛出异常来通知调用者。现在的问题是:如果HRESULT的值不是S_OK,也不是错误值,即最高位是0(后面几位不全是0),怎么样才能在C#里得到这个HRESULT值?
如果只是在C#里new一个COM对象,然后调用该成员函数,这种方式是没法得到HRESULT值的;用try-catch?因为该HRESULT值不是错误值,封装者不会抛出异常,也不行。


yagebu1983 2008-04-18
  • 打赏
  • 举报
回复
学习!!
gomoku 2008-04-18
  • 打赏
  • 举报
回复
int Add(int val1, int val2);
那不就是返回值了么?

Add原型的HRESULT一般是用来返回错误价值的,像S_OK表示成功执行,像E_FAIL等表示出错.
封装者看到E_XXXXX可以用抛出异常的方式来通知调用者.

没有出错当然你就没catch到了.


qian_xu 2008-04-17
  • 打赏
  • 举报
回复
我的确添加了引用,但是在.net里只能看到无返回值的函数签名,因为在把Type Library转换成程序集的元数据的过程中,HRESULT值被隐藏了(按照msdn的说法,只有最高位标记为1的HRESULT才会抛出异常。如果最高位是0呢?)。我想要取得这个HRESULT值,这是我发帖的目的。


上面的代码只是我用来测试的,因此Add函数有返回值。
Add的IDL原型是:[id(1)] HRESULT Add([in] LONG val1, [in] LONG val2, [out, retval] LONG* result);
加到c#工程的引用里后,在Object Browser里看到的函数签名是: int Add(int val1, int val2);

gomoku 2008-04-17
  • 打赏
  • 举报
回复
[Quote=引用楼主 qian_xu 的帖子:]
把xLib.dll加到Solution下的References里后,可以在object browser里看到函数签名,
[/Quote]

你不是已经添加引用了吗,为什么还要用InvokeMember?
添加引用的命名空间你就可以直接调用函数了。

Add的IDL原型是什么?
如果想InvokeMember的话,或许试试这样:

int sum = (int)ComObjType.InvokeMember(
"Add",
BindingFlags.InvokeMethod,
null,
ComObj,
new object[]{3,4}
);
qian_xu 2008-04-17
  • 打赏
  • 举报
回复
贴下代码:
            
Type ComObjType; // invoke COM object by Type.
object ComObj;
string ReturnValue;

// ComObjType = Type.GetTypeFromProgID("Wrox.ProCSharp.COMInterop.Server.COMDemo");
Guid gid = new Guid("78E88924-824D-46E3-87FD-111AC27D7B7A");
ComObjType = Type.GetTypeFromCLSID(gid);
ComObj = Activator.CreateInstance(ComObjType);
object[] ParamArray = new object[3];
ParamArray[0] = 4;
ParamArray[1] = 5;
ParamArray[2] = 0;

//用参数的索引属性来指出哪些参数是一个返回的参数
//对于那些是[in]或ByRef的参数可以不用指定
ParameterModifier[] ParamMods = new ParameterModifier[1];
ParamMods[0] = new ParameterModifier(3); // 初始化为接口参数的个数
ParamMods[0][2] = true; // 设置第三个参数为返回参数
// how to get Hresult.
ReturnValue = (string)ComObjType.InvokeMember("Add",
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
ComObj,
ParamArray,
ParamMods,
null,
null);



函数Add是COM类COMDemo(GUID为78E88924-824D-46E3-87FD-111AC27D7B7A)实现的某个接口里的函数。现在执行到该段代码的最后一句时抛出异常:
Unknown name. (Exception from HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME))

请问该如何才能找到这个Add函数?(如果在代码开头创建代码的时候使用包含Add的接口的GUID,则会在CreateInstance时抛出异常说找不到COM对象,该GUID未注册。)
qian_xu 2008-04-16
  • 打赏
  • 举报
回复
自己顶
qian_xu 2008-04-16
  • 打赏
  • 举报
回复
to 楼上:
这样行不通,编译都通不过。
kingmax54212008 2008-04-16
  • 打赏
  • 举报
回复
加到引用里面以后。
在Class view里面,你可以看到COM类和方法。
针对性的调用就行了。
ComCLS.class c1 = new ComCLS.class();
int hr = c1.f1(p1,p2...) ;

试试看
qian_xu 2008-04-16
  • 打赏
  • 举报
回复
看了msdn,明白vs2005把com的dll加到References里的时候,会自动把Type Library里的COM Types转换成程序集(Assembly)里的Metadata。因为我要调用的函数没有[in,out]类型的参数,所以转换后的函数返回类型为Void。
但仍旧不清楚如何取得HRESULT,像1楼说的,封装者可以把HRESULT返回的错误用异常(Exception)抛出。可是如果封装者没有这样做呢,那该怎么办?
qian_xu 2008-04-16
  • 打赏
  • 举报
回复
我错误地认为所有COM的函数返回值都是HRESULT类型的。
这个COM是C++写的,但该函数的返回值类型在Object Browser里看到的是void,是不是表示该COM已经被用Runtime Callable Wrapper等某些技术封装过了?
gxj760998 2008-04-16
  • 打赏
  • 举报
回复
该函数的返回值为void,而com的函数返回值都是hresult类型
这句没有看懂!
不过如果你能正常加载到项目Ref里面,这个应该是没有多少问题的。
问下,你的这个COM是。NET的么?
qian_xu 2008-04-16
  • 打赏
  • 举报
回复
问过客户,我要调用的函数的返回值的确是HRESULT类型的。
qian_xu 2008-04-16
  • 打赏
  • 举报
回复
已经注册过了
whoami333 2008-04-16
  • 打赏
  • 举报
回复
com组件需要先注册(安装)后才能正常引用的吧。
qian_xu 2008-04-16
  • 打赏
  • 举报
回复
我试过try,catch但是没有抓到COM异常(System.Runtime.InteropServices.COMException)
gomoku 2008-04-16
  • 打赏
  • 举报
回复
1,com的函数返回值不一定都是hresult类型
2,封装者可以把HRESULT返回的错误用用异常(Exception)抛出.
3,你可以try{}catch{}

110,533

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧