C#中有关com接口的定义

zhangjjjc 2011-06-13 01:57:37
一个标准的IE面板组件,必须要实现IDeskBand,IObjectWithSite,IDockingWindow和IPersistStream等COM接口,同时还需要用到如IOleWindow等接口。不幸的是,.Net Framework中并没有为我们事先完成这些COM接口的.Net类型定义,同时也无法通过导入COM类型库来引入这些接口定义,因此需要自己来手工完成定义,需要说明的是将COM接口转化为可互操作的.Net类型要遵循下面的规则,

COM 数据类型 对应的.NET数据类型
bool, bool * System.Int32
char, char *, small , small * System.SByte
short, short * System.Int16
long, long *, int , int * System.Int32
hyper, hyper * System.Int64
unsigned char, unsigned char *, byte, byte * System.Byte
wchar_t, wchar_t * unsigned short, unsigned short * System.UInt16
unsigned long, unsigned long * unsigned int, unsigned int * System.UInt32
unsigned hyper, unsigned hyper * System.UInt64
float, float * System.Single
double, double * System.Double
VARIANT_BOOL、VARIANT_BOOL * System.Boolean
void *, void ** System.IntPtr
HRESULT, HRESULT * System.Int16或System.IntPtr
SCODE, SCODE * System.Int32
BSTR, BSTR * System.String
LPSTR [string, …] char * LPSTR * System.String
LPWSTR [string, …] wchar_t * LPWSTR * System.String
VARIANT, VARIANT * System.Object
DECIMAL, DECIMAL *, CURRENCY, CURRENCY * System.Decimal
DATE, DATE * System.DateTime
GUID, GUID * System.Guid
IUnknown *, IUnknown ** System.Object
IDispatch *, IDispatch ** System.Object
SAFEARRAY(type), SAFEARRAY(type) * type[]

同时,COM接口需要用InterfaceType, ComImport和Guid特性进行修饰,其中ComImport特性标示指示该化类型是以前在 COM 中定义的。IntefaceType特性则指示该COM接口是是双重接口的、还是调度接口、还是IUnknown接口的。这里使用的接口都是IUnknown接口,因此类型为InterfaceIsIUnknown,Guid特性则用来指定该接口的Guid标识符。注意,接口中的有些方法需要使用PreserveSig特性进行修饰,这个特性告诉COM互操作将方法的返回值看做是COM HRESULT,而不是一个输出值。下面就是面板组件用到的所有COM接口的定义:
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("FC4801A3-2BA9-11CF-A229-00AA003D7352")]
public interface IObjectWithSite
{
void SetSite([In, MarshalAs(UnmanagedType.IUnknown)] Object pUnkSite);
void GetSite(ref Guid riid, [MarshalAs(UnmanagedType.IUnknown)] out Object ppvSite);
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("00000114-0000-0000-C000-000000000046")]
public interface IOleWindow
{
void GetWindow(out System.IntPtr phwnd);
void ContextSensitiveHelp([In] bool fEnterMode);
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("012dd920-7b26-11d0-8ca9-00a0c92dbfe8")]
public interface IDockingWindow
{
void GetWindow(out System.IntPtr phwnd);
void ContextSensitiveHelp([In] bool fEnterMode);
void ShowDW([In] bool fShow);
void CloseDW([In] UInt32 dwReserved);
void ResizeBorderDW(IntPtr prcBorder,
[In, MarshalAs(UnmanagedType.IUnknown)] Object punkToolbarSite, bool fReserved);
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("EB0FE172-1A3A-11D0-89B3-00A0C90A90AC")]
public interface IDeskBand
{
void GetWindow(out System.IntPtr phwnd);
void ContextSensitiveHelp([In] bool fEnterMode);
void ShowDW([In] bool fShow);
void CloseDW([In] UInt32 dwReserved);
void ResizeBorderDW(IntPtr prcBorder,
[In, MarshalAs(UnmanagedType.IUnknown)] Object punkToolbarSite, bool fReserved);
void GetBandInfo(UInt32 dwBandID, UInt32 dwViewMode, ref DESKBANDINFO pdbi);
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("6d5140c1-7436-11ce-8034-00aa006009fa")]
public interface _IServiceProvider
{
void QueryService(ref Guid guid,
ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out Object Obj);
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("68284faa-6a48-11d0-8c78-00c04fd918b4")]
public interface IInputObject
{
void UIActivateIO(Int32 fActivate, ref MSG msg);
[PreserveSig]
Int32 HasFocusIO();
[PreserveSig]
Int32 TranslateAcceleratorIO(ref MSG msg);
}

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("f1db8392-7331-11d0-8c99-00a0c92dbfe8")]
public interface IInputObjectSite
{
[PreserveSig]
Int32 OnFocusChangeIS([MarshalAs(UnmanagedType.IUnknown)] Object punkObj, Int32 fSetFocus);
}

除了上面的定义的接口之外,由于我们还需要调用IE浏览器来访问我的个人网站,这需要调用IE所实现的COM接口的方法来实现,这回我们不需要手工来定义需要使用的接口,直接导入IE的COM类型库即可,VS.Net会使用tlbimp.exe生成互操作Assembly,其中就有我们需要的Web浏览器类WebBrowserClass。如下图示意:

作为一个面板组件,必须要有可以同用户进行交互的界面,这里我们使用一个用户组件来事先,接下来向项目中添加一个UserControl,起名为BandObject,创建完UserControl后,修改BandObject的类定义,使其实现相关的接口。

首先我们来看一下IObjectWithSite接口,顾名思义这个接口就是一个嵌入在COM容器中的组件需要实现的接口,当用户选中一个浏览器面板的时候,IE这个COM组件容器会调用面板组件的IObjectWithSite的SetSite方法,这个方法传递过来的punkSite参数就是COM容器的IUnknown接口,通过这个接口我们可以获得IE提供的各个COM接口,进而可以调用IE提供的所有功能。这个方法的一般实现是:

1、 如果方法传过来的punkSite参数为null,表示面板组件正在被隐藏释放。

2、 如果punkSite参数不为null,则通过punkSite获得我们所需要的COM实例(如IE的WebBrowserClass类),并将其保存起来。

下面是SetSite方法的实现代码,注意代码中对COM对象的引用记数进行了处理:
protected WebBrowserClass Explorer;
protected IInputObjectSite BandObjectSite;

public void SetSite(Object pUnkSite)
{
if (BandObjectSite != null)
Marshal.ReleaseComObject(BandObjectSite);

if (Explorer != null)
{
Marshal.ReleaseComObject(Explorer);
Explorer = null;
}

BandObjectSite = (IInputObjectSite) pUnkSite;
if (BandObjectSite != null)
{
//获取浏览器接口
_IServiceProvider sp = BandObjectSite as _IServiceProvider;
Guid guid = ExplorerGUIDs.IID_IWebBrowserApp;
Guid riid = ExplorerGUIDs.IID_IUnknown;
try
{
object w;
sp.QueryService(ref guid, ref riid, out w);
//一旦获得COM接口,就可以创建RCW以便使用
Explorer = (WebBrowserClass) Marshal.CreateWrapperOfType(
w as IWebBrowser, typeof(WebBrowserClass));
}
catch (COMException)
{
}
}
}

IObjectWithSite还有一个GetSite方法也需要实现,在这个方法中我们只需要返回在SetSite方法中保存的Site接口就可以了,代码实现如下:
public void GetSite(ref Guid riid, out Object ppvSite)
{
ppvSite = BandObjectSite;
}

接下来的IDeskBand接口就是我们面板组件所要实现的核心接口了。IDeskBand接口的ContextSensitiveHelp方法是用来提供上下文帮助支持的,为了简单起见,这里我们不提供上下文帮助支持,所以什么都不做就可以了。而ResizeBorderDW 方法系统也从来不会被IE调用,因此我们也无须实现。
...全文
350 6 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
心如止水521 2011-10-28
  • 打赏
  • 举报
回复
用C#做这个的人比较少,不过我正好用到了,谢谢分享
zhangjjjc 2011-06-13
  • 打赏
  • 举报
回复
嗯 现在这条路越来越难走喽
xuexiaodong2009 2011-06-13
  • 打赏
  • 举报
回复
那就学习了
zhangjjjc 2011-06-13
  • 打赏
  • 举报
回复
第一次了解这东东了,就贴贴
xuexiaodong2009 2011-06-13
  • 打赏
  • 举报
回复
做啥?
lihanbing 2011-06-13
  • 打赏
  • 举报
回复
贴这玩意做啥?

111,098

社区成员

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

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

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