我一直在谷歌搜索试图找到获得COM接口实例的标准方法。
Microsoft在其文章COM Interop Part 1: Client Tutorial中提供了此示例:
// Create an instance of a COM coclass:
FilgraphManager graphManager = new FilgraphManager();
// See if it supports the IMediaControl COM interface.
// Note that this will throw a System.InvalidCastException if
// the cast fails. This is equivalent to QueryInterface for
// COM objects:
IMediaControl mc = (IMediaControl) graphManager;
// Now you call a method on a COM interface:
mc.Run();
但是,看起来好像是在实例化COM对象并将其转换为COM接口。
对于我感兴趣的接口IDesktopWallpaper
,似乎没有实现COM对象来实例化。
我发现here的一个例子定义了一些实例化的类,然后以与msdn示例相同的方式将其强制转换为接口:
[ComImport, Guid("C2CF3110-460E-4fc1-B9D0-8A1C0C9CC4BD")]
internal class IDesktopWallpaper
{
}
[Guid("B92B56A9-8B55-4E14-9A89-0199BBB6F93B"), //B92B56A9-8B55-4E14-9A89-0199BBB6F93B
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface DesktopWallpaperInterface
{
// declared members
}
我不明白实例化对象是什么。它似乎是一个任意对象,它有GuidAttribute
,似乎表明它是一个真正的COM对象。
另一个例子我发现here System.Type
和System.Runtime.InteropServices.Marshal
来实例化一个对象,然后将其强制转换为接口:
IntPtr ptrRet;
SHGetMalloc(out ptrRet);
System.Type mallocType = System.Type.GetType("IMalloc");
Object obj = Marshal.GetTypedObjectForIUnknown(ptrRet,mallocType);
IMalloc pMalloc = (IMalloc)obj;
此方法似乎是请求指向接口的现有实例的指针。我在Windows Shell文档中找不到SHGetMalloc
IDesktopWallpaper
之类的任何方法。
所以,长话短说,获取COM接口实例的标准方法是什么?
如果没有一个通用的解决方案,那么可以使用什么标准方式来获取COM接口的实例,以及在什么情况下每个这些方式最有用吗?
下载Windows 10 SDK并根据IDesktopWallpaper interface documentation的要求部分引用它后,我发现您可以从Shobjidl.h
查找MIDL并在GuidAttribute
中使用它为您的接口声明,然后从Shobjidl.idl
查找CLSID并将其与Type.GetTypeFromCLSID(Guid)
和Activator.CreateInstance(Type)
结合使用,以获取实现IDesktopWallpaper
的对象的实例。
我现在也看到CLSID是上面列出的第二种方法中用于看似任意对象的GuidAttribute
的内容。看起来这个方法允许您通过实例化类然后将实例强制转换为COM接口来模仿对象的托管实例化。
然而 我仍然有兴趣知道这是否是这种方法的最佳方式,以及与其他方法相比,这种方法可能有哪些优缺点。
答案 0 :(得分:0)
您可以通过各种方法获取指向COM对象引用的指针:
CoCreateInstance
CLSIDFromProgID
→CoCreateInstance
IRunningObjectTable.GetObject
Type.GetTypeFromCLSID
→Activator.CreateInstance
Type.GetTypeFromProgID
→Activator.CreateInstance
new SomeType()
其中SomeType
标有ComImport
Activator.CreateInstance
和new SomeType()
最终点击了CoCreateInstance
(如果它们没有被各种应用内域内的内容拦截)。对进程外服务器的CoCreateInstance
调用最终将使用类名字对象IRunningObjectTable
命中(我认为)。最佳选择取决于您要做的事情:
ComImport
ComImport
将起作用,我更愿意致电CoCreateInstance
以传递正确的CLSCTX
。ComImport
添加,这将导致服务器在进程中运行IRunningObjectTable
CLSIDFromProgID
或Type.GetTypeFromProgID
无论我们如何获得对象的引用,我们都从IUnknown
(.Net中的object
开始),然后必须调用IUnknown->QueryInterface
来获取指向特殊界面。在.Net中调用QueryInterface
是通过强制转换为标记为ComVisible
的接口(通常用GuidAttribute
注释)来实现的。
在您命名的示例中,您最终会得到:
// based off of https://bitbucket.org/ciniml/desktopwallpaper
[ComImport]
[Guid("B92B56A9-8B55-4E14-9A89-0199BBB6F93B")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDesktopWallpaper
{
void SetWallpaper([MarshalAs(UnmanagedType.LPWStr)] string monitorID, [MarshalAs(UnmanagedType.LPWStr)] string wallpaper);
[return: MarshalAs(UnmanagedType.LPWStr)]
string GetWallpaper([MarshalAs(UnmanagedType.LPWStr)] string monitorID);
[return: MarshalAs(UnmanagedType.LPWStr)]
string GetMonitorDevicePathAt(uint monitorIndex);
[return: MarshalAs(UnmanagedType.U4)]
uint GetMonitorDevicePathCount();
[return: MarshalAs(UnmanagedType.Struct)]
Rect GetMonitorRECT([MarshalAs(UnmanagedType.LPWStr)] string monitorID);
void SetBackgroundColor([MarshalAs(UnmanagedType.U4)] uint color);
[return: MarshalAs(UnmanagedType.U4)]
uint GetBackgroundColor();
void SetPosition([MarshalAs(UnmanagedType.I4)] DesktopWallpaperPosition position);
[return: MarshalAs(UnmanagedType.I4)]
DesktopWallpaperPosition GetPosition();
void SetSlideshow(IntPtr items);
IntPtr GetSlideshow();
void SetSlideshowOptions(DesktopSlideshowDirection options, uint slideshowTick);
void GetSlideshowOptions(out DesktopSlideshowDirection options, out uint slideshowTick);
void AdvanceSlideshow([MarshalAs(UnmanagedType.LPWStr)] string monitorID, [MarshalAs(UnmanagedType.I4)] DesktopSlideshowDirection direction);
DesktopSlideshowDirection GetStatus();
bool Enable();
}
[ComImport]
[Guid("C2CF3110-460E-4fc1-B9D0-8A1C0C9CC4BD")]
public class DesktopWallpaper
{
}
[Flags]
public enum DesktopSlideshowOptions
{
None = 0,
ShuffleImages = 0x01
}
[Flags]
public enum DesktopSlideshowState
{
None = 0,
Enabled = 0x01,
Slideshow = 0x02,
DisabledByRemoteSession = 0x04
}
public enum DesktopSlideshowDirection
{
Forward = 0,
Backward = 1
}
public enum DesktopWallpaperPosition
{
Center = 0,
Tile = 1,
Stretch = 2,
Fit = 3,
Fill = 4,
Span = 5,
}
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
其使用示例如下:
public partial class Form1 : Form
{
private IDesktopWallpaper Wallpaper;
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Wallpaper = (IDesktopWallpaper)new DesktopWallpaper();
uint monitorCount = Wallpaper.GetMonitorDevicePathCount();
for (uint i = 0; i < monitorCount; i++)
{
lbMonitors.Items.Add(Wallpaper.GetMonitorDevicePathAt(i));
}
}
private void lbMonitors_SelectedValueChanged(object sender, EventArgs e)
{
var path = (string)lbMonitors.SelectedItem;
tbWallpaper.Text = Wallpaper.GetWallpaper(path);
}
}
生成表格: