有关COM参数传入的问题--帮朋友问一下,请多帮忙。

纪俊 2002-05-01 03:37:23
现在我们老板想做一个网络化的版本,希望客户访问我们的服务器,通过在浏
览器界面内输入一些资料即可得到返回的输出结果。这就是要求我们做一个基于
B/S的网络计算系统。
经过向一些高手咨询,初步认为用C#(ASP.Net)做一个Web Application,引
用该组件,开发这样的系统是可行的。
现在有一个问题:在COM接口函数上,用到了变体(Variant)类型的参数;但是
C#中取消了这种数据类型,那么怎样在C#中将参数传入接口呢?
C#中的装箱(Boxing)与拆箱(Unboxing)操作是否能派上用场?
请高手指点,如能给出示例代码,则更好!

50分不够再加,在50-500分之间就行。
...全文
74 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
纪俊 2002-05-02
  • 打赏
  • 举报
回复
多谢,兄弟
Yang_Wenli 2002-05-02
  • 打赏
  • 举报
回复
使用在前面的示例中定义的 MarshalObject 接口,下面的代码示例说明如何将各种类型的变量传递给 COM 服务器。

[C#]
MarshalObject mo = new MarshalObject();
mo.SetVariant(null); // Marshal as variant of type VT_EMPTY.
mo.SetVariant(System.DBNull.Value); // Marshal as variant of type VT_NULL.
mo.SetVariant((int)27); // Marshal as variant of type VT_I2.
mo.SetVariant((long)27); // Marshal as variant of type VT_I4.
mo.SetVariant((single)27.0); // Marshal as variant of type VT_R4.
mo.SetVariant((double)27.0); // Marshal as variant of type VT_R8.
不具有相应托管类型的 COM 类型可以使用诸如 ErrorWrapper、DispatchWrapper、UnknownWrapper 和 CurrencyWrapper 这样的包装类封送。下面的代码示例说明如何使用这些包装将各种类型的变量传递给 COM 服务器。

[C#]
using System.Runtime.InteropServices;
// Pass inew as a variant of type VT_UNKNOWN interface.
mo.SetVariant(new UnknownWrapper(inew));
// Pass inew as a variant of type VT_DISPATCH interface.
mo.SetVariant(new DispatchWrapper(inew));
// Pass a value as a variant of type VT_ERROR interface.
mo.SetVariant(new ErrorWrapper(0x80054002));
// Pass a value as a variant of type VT_CURRENCY interface.
mo.SetVariant(new CurrencyWrapper(new Decimal(5.25)));
包装类定义在 System.Runtime.InteropSevices 命名空间中。

将 IConvertible 接口封送到变量
上一节中列出的那些类型以外的类型可以通过实现 IConvertible 接口来控制封送它们的方式。如果对象实现 IConvertible 接口,则 COM Variant 类型将在运行时由从 IConvertible.GetTypeCode 方法返回的 TypeCode 枚举的值确定。

下表显示 TypeCode 枚举的可能值以及每个值的相应 COM Variant 类型。

TypeCode COM Variant 类型
TypeCode.Empty VT_EMPTY
TypeCode.Object VT_UNKNOWN
TypeCode.DBNull VT_NULL
TypeCode.Boolean VT_BOOL
TypeCode.Char VT_UI2
TypeCode.Sbyte VT_I1
TypeCode.Byte VT_UI1
TypeCode.Int16 VT_I2
TypeCode.UInt16 VT_UI2
TypeCode.Int32 VT_I4
TypeCode.UInt32 VT_UI4
TypeCode.Int64 VT_I8
TypeCode.UInt64 VT_UI8
TypeCode.Single VT_R4
TypeCode.Double VT_R8
TypeCode.Decimal VT_DECIMAL
TypeCode.DateTime VT_DATE
TypeCode.String VT_BSTR
不受支持。 VT_INT
不受支持。 VT_UINT
不受支持。 VT_ARRAY
不受支持。 VT_RECORD
不受支持。 VT_CY
不受支持。 VT_VARIANT

COM Variant 的值是通过调用 IConvertible.ToType 接口(其中,ToType 是对应于从 IConvertible.GetTypeCode 返回的类型的转换例程)确定的。例如,从 IConvertible.GetTypeCode 返回 TypeCode.Double 的对象被作为 VT_R8 类型的 COM Variant 封送。可以通过强制转换为 IConvertible 接口并调用 ToDouble 方法来获取变量的值(它存储在 COM Variant 的 dblVal 字段中)。

将变量封送到对象
将变量封送到对象时,被封送变量的类型(有时还有值)确定产生的对象的类型。下表标识每个变量类型以及在将变量从 COM 传递给 .NET 框架时封送拆收器所创建的相应对象类型。

COM Variant 类型 对象类型
VT_EMPTY 空对象引用。
VT_NULL System.DBNull
VT_DISPATCH System.__ComObject;如果 (pdispVal == null) 则为 Null
VT_UNKNOWN System.__ComObject;如果 (punkVal == null) 则为 Null
VT_ERROR System.UInt32
VT_BOOL System.Boolean
VT_I1 System.SByte
VT_UI1 System.Byte
VT_I2 System.Int16
VT_UI2 System.UInt16
VT_I4 System.Int32
VT_UI4 System.UInt32
VT_I8 System.Int64
VT_UI8 System.UInt64
VT_R4 System.Single
VT_R8 System.Double
VT_DECIMAL System.Decimal
VT_DATE System.DateTime
VT_BSTR System.String
VT_INT System.Int32
VT_UINT System.UInt32
VT_ARRAY |VT_* System.Array
VT_CY System.Decimal
VT_RECORD 不受支持。
VT_VARIANT 不受支持。

从 COM 传递给托管代码,然后再传回到 COM 的变量类型可能无法在调用的持续时间内保持为同样的变量类型。请考虑将 VT_DISPATCH 类型的变量从 COM 传递到 .NET 框架时会发生什么。在封送处理期间,变量被转换为 System.Object。如果接着将 Object 传回 COM,则它将被封送回 VT_UNKNOWN 类型的变量。不能保证将对象从托管代码封送到 COM 时所产生的变量将与最初用于产生该对象的变量为同一类型。

封送 ByRef 变量
虽然变量本身可以通过值或引用传递,但是 VT_BYREF 标志也可以与任何变量类型一起使用以指示变量的内容通过引用而不是通过值传递。按引用封送变量与封送设置了 VT_BYREF 标志的变量之间的差异可能是令人费解的。下面的插图阐明这些差异。

通过值和通过引用传递的变量



按值封送对象和变量的默认行为

将对象从托管代码传递给 COM 时,对象的内容将使用在将对象封送到变量中定义的规则复制到由封送拆收器创建的新变量中。对非托管端的变量所做的更改将不会在从调用返回时传播回原始对象。
将变量从 COM 传递给托管代码时,变量的内容将使用在将变量封送到对象中定义的规则复制到新创建的对象。对托管端的对象所做的更改将不会在从调用返回时传播回原始变量。
按引用封送对象和变量的默认行为

若要将更改传播回调用方,参数必须通过引用传递。例如,可以使用 C# 中的 ref(或 Visual Basic 托管代码中的 ByRef)关键字通过引用传递参数。在 COM 中,引用参数是使用指针(如 variant *)传递的。

将对象通过引用传递给 COM 时,封送拆收器创建一个新变量并在进行调用之前将对象引用的内容复制到变量中。变量被传递给用户可随意更改变量的内容的非托管函数。从调用返回时,对非托管端的变量所做的任何更改都将传播回原始对象。如果变量的类型与传递给调用的变量的类型不同,则更改将被传播回另一类型的对象。也就是说,传递到调用中的对象的类型可以不同于从调用返回的对象的类型。
将变量通过引用传递给托管代码时,封送拆收器创建一个新对象,并在进行调用之前将变量的内容复制到对象中。对该对象的引用被传递给用户可以随意更改该对象的托管函数。从调用返回时,对被引用的对象所做的任何更改都将传播回原始变量。如果对象的类型与传入调用中的对象的类型不同,则原始变量的类型将被更改而且值将被传播回变量中。同样,传入调用中的变量的类型可以不同于从调用返回的变量的类型。
封送设置了 VT_BYREF 标志的变量的默认行为

通过值传递给托管代码的变量可以设置 VT_BYREF 标志以指示该变量包含引用而不是值。在这种情况下,仍然将变量封送到对象,原因是变量正通过值传递。封送拆收器自动取消对变量内容的引用,并在进行调用前将其复制到新创建的对象中。然后,对象将被传递到托管函数中;但是,在从调用返回时,该对象不会被传播回原始变量中。对托管对象所做的更改将丢失。
警告 即使变量设置了 VT_BYREF 标志,也无法更改通过值传递的变量的值。
通过引用传递给托管代码的变量也可以设置 VT_BYREF 标志以指示该变量包含另一个引用。如果确实设置了该标志,变量将被封送到 ref 对象,这是因为该变量正通过引用传递。封送拆收器自动取消对变量内容的引用,并在进行调用前将其复制到新创建的对象中。从调用返回时,仅当对象与传入的对象是同一类型时,才将对象的值传播回原始变量中的引用。也就是说,传播不会更改设置了 VT_BYREF 标志的变量的类型。如果在调用期间更改了对象的类型,则在从调用返回时将发生 InvalidCastException。
下表汇总了变量和对象的传播规则。

从 到 传播回更改
Variant v Object o 从不
Object o Variant v 从不
Variant *pv Ref Object o 始终
Ref object o Variant *pv 始终
Variant v(VT_BYREF|VT_*) Object o 从不
Variant v(VT_BYREF|VT_) Ref Object o 仅当类型尚未更改时。
Yang_Wenli 2002-05-02
  • 打赏
  • 举报
回复
可以使用object,下面是MSDN的原文

可以将类型化为 System.Object 的参数和字段作为下列类型之一向非托管代码公开:

Variant,当对象为参数时。
接口,当对象是结构字段时。
只有 COM Interop 支持对对象类型的封送处理。默认行为是将对象封送到 COM Variant。这些规则只适用于 Object 类型,而不适用于从 Object 类派生的强类型对象。

本主题提供下列有关封送对象类型的附加信息:

封送处理选项
将对象封送到接口
将对象封送到变量
将变量封送到对象
封送 ByRef 变量
封送处理选项
下表显示 Object 数据类型的封送处理选项。MarshalAsAttribute 属性提供若干个 UnmanagedType 枚举值以封送对象。

枚举类型 非托管格式的说明
UnmanagedType.Struct COM 样式变量。
UnmanagedType.Interface 如果可能,为 IDispatch 接口;否则为 IUnknown 接口。
UnmanagedType.IUnknown IUnknown 接口。
UnmanagedType.IDispatch IDispatch 接口。

下面的示例显示 MarshalObject 的托管接口定义。

[C#]
interface MarshalObject {
void SetVariant(Object o);
void SetVariantRef(ref Object o);
Object GetVariant();

void SetIDispatch ([MarshalAs(UnmanagedType.IDispatch)]Object o);
void SetIDispatchRef([MarshalAs(UnmanagedType.IDispatch)]ref Object o);
[MarshalAs(UnmanagedType.IDispatch)] Object GetIDispatch();
void SetIUnknown ([MarshalAs(UnmanagedType.IUnknown)]Object o);
void SetIUnknownRef([MarshalAs(UnmanagedType.IUnknown)]ref Object o);
[MarshalAs(UnmanagedType.IUnknown)] Object GetIUnknown();
}
下面的代码将 MarshalObject 接口导出到类型库。

interface MarshalObject {
HRESULT SetVariant([in] VARIANT o);
HRESULT SetVariantRef([in,out] VARIANT *o);
HRESULT GetVariant([out,retval] VARIANT *o)
HRESULT SetIDispatch([in] IDispatch *o);
HRESULT SetIDispatchRef([in,out] IDispatch **o);
HRESULT GetIDispatch([out,retval] IDispatch **o)
HRESULT SetIUnknown([in] IUnknown *o);
HRESULT SetIUnknownRef([in,out] IUnknown **o);
HRESULT GetIUnknown([out,retval] IUnknown **o)
}
注意 Interop 封送拆收器在调用后自动释放变量内的任何已分配对象。
下面的示例显示格式化的值类型。

[C#]
public struct ObjectHolder {
Object o1;
[MarshalAs(UnmanagedType.IDispatch)]public Object o2;
}
下面的代码将格式化的类型导出到类型库。

struct ObjectHolder {
VARIANT o1;
IDispatch *o2;
}
将对象封送到接口
当对象作为接口向 COM 公开时,该接口是托管类型 Object 的类接口(即 _Object 接口)。该接口被类型化为 IDispatch (UnmanagedType.IDispatch) 或得到的类型库中的 IUnknown (UnmanagedType.IUnknown)。通过 _Object 接口,COM 客户端可以动态调用该托管类的成员或由该托管类的派生类实现的任何成员。客户端还可以调用 QueryInterface 以获取任何由该托管类型显式实现的其他接口。

将对象封送到变量
将对象封送到变量时,内部变量类型将在运行时根据下列规则确定:

如果对象引用为空,则将对象封送到 VT_EMPTY 类型的变量。
如果对象是下表中列出的任何类型的实例,则得到的变量类型由内置在封送拆收器中的规则确定,并显示在表中。
需要显式控制封送处理行为的其他对象可以实现 IConvertible 接口。在这种情况下,变量类型由从 IConvertible.GetTypeCode 方法返回的类型代码确定。否则,将对象作为 VT_UNKNOWN 类型的变量封送。
将系统类型封送到变量
下表显示托管对象类型及其对应的 COM Variant 类型。只有被调用的方法的签名属于 System.Object 类型时才转换这些类型。

对象类型 COM Variant 类型
空对象引用。 VT_EMPTY
System.DBNull VT_NULL
System.Runtime.InteropServices.ErrorWrapper VT_ERROR
System.Reflection.Missing 带 E_PARAMNOTFOUND 的 VT_ERROR
System.Runtime.InteropServices.DispatchWrapper VT_DISPATCH
System.Runtime.InteropServices.UnknownWrapper VT_UNKNOWN
System.Runtime.InteropServices.CurrencyWrapper VT_CY
System.Boolean VT_BOOL
System.SByte VT_I1
System.Byte VT_UI1
System.Int16 VT_I2
System.UInt16 VT_UI2
System.Int32 VT_I4
System.UInt32 VT_UI4
System.Int64 VT_I8
System.UInt64 VT_UI8
System.Single VT_R4
System.Double VT_R8
System.Decimal VT_DECIMAL
System.DateTime VT_DATE
System.String VT_BSTR
System.IntPtr VT_INT
System.UIntPtr VT_UINT
System.Array VT_ARRAY

110,571

社区成员

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

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

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