gpt4 book ai didi

C# 如何获取 COM 接口(interface)的实例

转载 作者:行者123 更新时间:2023-11-30 23:17:17 25 4
gpt4 key购买 nike

我一直在进行大量谷歌搜索,试图找到获取 COM 接口(interface)实例的标准方法。

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 接口(interface)。

对于我感兴趣的接口(interface)IDesktopWallpaper,似乎没有实现COM对象来实例化。

我找到的一个例子here定义一些实例化的类,然后像 msdn 示例一样将其转换为接口(interface):

[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.TypeSystem.Runtime.InteropServices.Marshal 实例化对象,然后将其转换为接口(interface):

IntPtr ptrRet;
SHGetMalloc(out ptrRet);

System.Type mallocType = System.Type.GetType("IMalloc");
Object obj = Marshal.GetTypedObjectForIUnknown(ptrRet,mallocType);
IMalloc pMalloc = (IMalloc)obj;

这个方法似乎在请求一个指向接口(interface)的现有实例的指针。我在 Windows Shell 文档中找不到任何方法,例如 SHGetMalloc for IDesktopWallpaper

问题

那么,长话短说,获取 COM 接口(interface)实例的标准方法是什么?

如果没有一刀切的解决方案,可以使用哪些标准方法来获取 COM 接口(interface)的实例,以及每种方法在什么情况下这些方式最有用?

编辑

在下载 Windows 10 SDK 并根据 IDesktopWallpaper interface documentation 的要求部分引用它之后,我发现您可以从 Shobjidl.h 中查找 MIDL,并在 GuidAttribute 中使用它作为接口(interface)声明,然后从 Shobjidl 中查找 CLSID .idl 并将其与 Type.GetTypeFromCLSID(Guid)Activator.CreateInstance(Type) 结合使用以获取实现 的对象的实例>IDesktopWallpaper.

我现在还看到,CLSID 是上面列出的第二种方法中用于看似任意对象的 GuidAttribute 的内容。似乎此方法允许您通过实例化类然后将实例转换为 COM 接口(interface)来模拟对象的托管实例化。

但是我仍然有兴趣知道这是否是执行此操作的最佳方法,以及此方法与其他方法相比有哪些优缺点。

最佳答案

您可以通过多种方法获取指向 COM 对象引用的指针:

  • P/调用 CoCreateInstance
  • P/调用 CLSIDFromProgIDCoCreateInstance
  • P/调用 IRunningObjectTable.GetObject
  • Type.GetTypeFromCLSIDActivator.CreateInstance
  • Type.GetTypeFromProgIDActivator.CreateInstance
  • new SomeType() 其中 SomeType 被标记为 ComImport

Activator.CreateInstancenew SomeType() 最终命中 CoCreateInstance(如果它们没有被各种应用程序域内的东西拦截).为进程外服务器调用 CoCreateInstance 最终将使用类名字对象(我认为)命中 IRunningObjectTable。最佳选择取决于您要执行的操作:

  • 对于进程内服务器,只需使用 ComImport
  • 对于未在 .Net 中实现的进程外服务器,ComImport 将起作用,我更愿意调用 CoCreateInstance 来传递正确的 CLSCTX.
  • 对于在 .Net 中实现的 .net 进程外服务器,您必须直接调用 CoCreateInstance 以避免 ComImport 添加的“优化”导致服务器运行在 -过程
  • 如果您正在处理名字对象,请使用 IRunningObjectTable
  • 如果您开始使用 ProgID 而不是 CLSID,请使用 CLSIDFromProgIDType.GetTypeFromProgID

无论我们如何获得对对象的引用,我们都是从 IUnknown(.Net 中的 object)开始,然后必须调用 IUnknown->QueryInterface 获取指向特定接口(interface)的指针。在 .Net 中调用 QueryInterface 是通过转换为标记为 ComVisible 的接口(interface)(通常用 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);
}
}

生成表单:

Form showing list of monitors and result of GetWallpaper

关于C# 如何获取 COM 接口(interface)的实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41516979/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com