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

2024-04-26

我做了很多谷歌搜索,试图找到获取 COM 接口实例的标准方法。

微软在他们的文章中提供了一个例子COM 互操作第 1 部分:客户端教程 https://msdn.microsoft.com/en-us/library/aa645736(v=vs.71).aspx:

// 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 http://www.dreamincode.net/forums/topic/298478-problems-implementing-idesktopwallpaper-com-interface/定义一些被实例化的类,然后将其转换为接口,与 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 https://www.codeproject.com/Articles/3551/C-does-Shell-Part System.Type and 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;

该方法似乎正在请求指向该接口的现有实例的指针。我找不到类似的方法SHGetMalloc for IDesktopWallpaper在 Windows Shell 文档中。

Question

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

如果没有一刀切的解决方案,那么标准是什么?ways可以用来获取 COM 接口的实例,在什么情况下这些方法最有用?

Edit

下载 Windows 10 SDK 并根据要求部分引用它后IDesktopWallpaper 接口文档 https://msdn.microsoft.com/en-us/library/windows/desktop/hh706946(v=vs.85).aspx,我发现你可以从以下位置查找 MIDLShobjidl.h并在中使用它GuidAttribute获取接口声明,然后查找 CLSIDShobjidl.idl并将其与Type.GetTypeFromCLSID(Guid) and Activator.CreateInstance(Type)获取实现的对象的实例IDesktopWallpaper.

我现在还看到,CLSID 是上面列出的第二种方法中使用的GuidAttribute看似随意的物体。看起来这个方法允许您通过实例化类然后将实例转换为 COM 接口来模拟对象的托管实例化。

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


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

  • P/调用CoCreateInstance
  • P/调用CLSIDFromProgIDCoCreateInstance
  • P/调用IRunningObjectTable.GetObject
  • Type.GetTypeFromCLSIDActivator.CreateInstance
  • Type.GetTypeFromProgIDActivator.CreateInstance
  • new SomeType() where SomeType标有ComImport

Activator.CreateInstance and new SomeType()最终击中CoCreateInstance(如果它们没有被各种应用程序域内的东西拦截)。致电CoCreateInstance对于进程外服务器最终会命中IRunningObjectTable带有班级绰号(我认为)。最佳选择取决于您想要做什么:

  • 对于进程内服务器,只需使用ComImport
  • 对于未在 .Net 中实现的进程外服务器,ComImport会起作用,我更愿意打电话CoCreateInstance通过右侧CLSCTX.
  • 对于在 .Net 中实现的 .net 实现的进程外服务器,您必须直接调用 CoCreateInstance 以避免由ComImport这将导致服务器在进程中运行
  • 如果您正在处理昵称,请使用IRunningObjectTable
  • 如果您从 ProgID 而不是 CLSID 开始,请使用CLSIDFromProgID or Type.GetTypeFromProgID

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

产生以下形式:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C# 如何获取 COM 接口的实例 的相关文章

随机推荐

  • mysql_real_escape_string 未定义

    我正在使用 PHP 版本 5 3 并尝试使用mysql real escape string unescaped string 在我的代码中 但出现错误 Fatal error Call to undefined function mysq
  • 查询字符串:查询字符串是否可以包含也包含查询字符串的 URL?

    Example http foo com generatepdf aspx u http foo com somepage aspx color blue size 15 我添加了 iis 标签 因为我猜它也取决于您使用的服务器技术 服务器
  • OpenSSL 和 CryptoJS SHA256 加密转换

    我的问题是 OpenSSL 的新版本与 CryptoJS 的默认设置不兼容 openssl enc 用于基于密码的密钥派生的默认哈希值 1 1 0 中更改为 SHA256 而较低版本中更改为 MD5 https unix stackexch
  • MPAndroidChart - 饼图 - 自定义标签线

    我正在尝试使用 MPAndroidChart 和饼图绘制如图所示的标签线 我不知道如何 将线条与图表分离 在该行的开头画一个小圆圈 谢谢 这绝非易事 要将线条与图表分离 您可以使用valueLinePart1OffsetPercentage
  • lambda 中的自定义相交

    我想知道是否可以使用 lambda 表达式来解决这个问题 List
  • Kubernetes:用 Calico 替代 Flannel

    我是 Kubernetes 新手 我想尝试不同的 CNI 在我当前的集群中 我正在使用 Flannel 现在 我想使用 Calico 但我找不到清理 Flannel 和安装 Calico 的正确指南 您能指出正确的程序吗 Thanks Ca
  • 涉及优化器的局部变量构造和销毁

    如果我有这个代码 class A class B void dummy A a B b 我知道变量a and b将以相反的分配顺序销毁 b将首先被摧毁 然后a 但我可以确定优化器永远不会交换的分配和构造a and b 或者我必须使用vola
  • 有没有办法在 C++ 中调用类外部的基函数?

    这个问题是类似的 但是是关于从类内部调用函数 如果我重写基类的虚函数 我可以调用它吗 https stackoverflow com questions 672373 can i call a base classs virtual fun
  • 使用 Google 电子表格中的脚本从手机获取我的当前位置

    有没有办法使用 Google Apps 脚本从手机的 GPS 数据中获取我的当前位置 纬度和经度 最好是十进制形式 另外 是否可以打开和关闭 GPS 或者至少检测它是否打开或关闭 这是我尝试做的 我带着电动助力车去一些地方 在每个地方我都会
  • 指定 ghci 中“加载”操作的搜索路径

    In 加载源文件 http www haskell org ghc docs 7 6 1 html users guide loading source files html它指出查找源文件的搜索路径是使用 i 选项指定的 ghci idi
  • XDoclet,一个死工具?

    我正在使用最新的 Eclipse for Java EE 和最新的 JBoss Tools 插件 现在 我正在编写一些 EJB 2 x 代码 我找不到像以前一样生成 xdoclet build xml 文件的方法 经过一番调查后 我开始问自
  • 仅适用于我所在国家/地区的 Google 地图?

    如何在 Android 应用程序中使用 Google 地图 使其仅显示我的国家 地区 我的意思是 世界其他地方不会出现在应用程序中 查看其中之一迈克 威廉姆斯 http econym org uk gmap range htm很棒的 GMa
  • 请求头字段X-Requested

    我正在尝试访问谷歌云存储上的存储桶中的文件 我已经为存储桶设置了 CORS 配置 但当我通过 https 发出请求时 出现此错误 它适用于通过 http 发出的请求 XMLHttpRequest 无法加载 FILENAME 预检响应中的 A
  • 无限地重复可枚举

    是否存在无限重复可枚举的可枚举扩展方法 例如 给定一个返回的枚举 a b c 我想要一个返回无限重复序列的方法 a b c a b c a b c 这听起来有点像可观察 重复 http msdn microsoft com en us li
  • 如何像普通命令行程序一样从 shell 运行 sbt 主类?

    如何从 shell 运行 sbt 应用程序 以便我可以将我的应用程序作为普通的命令行程序运行 就像直接通过scala但不必设置巨大的类路径 我知道我能做到 echo hello sbt run main com foo MyMain3 ar
  • 当我在 Visual Studio 2017 的 C# 中调用方法时,是否有指向显式命名参数的快捷方式?

    在 C 中 我有一个带有许多参数的方法 我想使用所有命名参数调用该方法 VisualStudio 2017 中有执行此操作的快捷方式吗 我使用 EF6 并使用 SQL 2014 从具有 10 多个输入参数的存储过程生成方法 Method p
  • 如何将“&[email protected]”附加到 GoogleWebAuthorizationBroker

    我想将 login hint 附加到向 Google 发出的身份验证请求中 我正在使用以下代码 FileDataStore fDS new FileDataStore Logger Folder true GoogleClientSecre
  • 创建新的 SqlDataAdapter 时出现 C# InvalidOperationException

    我编写了一些代码来建立与 SQL Server 的连接 然后执行 select 过程以从 SQL Server 中的数据表中获取所有数据 但它在声明新的 SqlDataAdapter 的命令中抛出 InvalidOperationExcep
  • 如何使用 ASP.NET MVC 控制器操作将文件发送到浏览器?

    我有一个应用程序 允许我的用户上传任何类型的文件 我将其保存在服务器上的文件系统中 该应用程序只能由两个用户访问 因此我无需担心上传任何可疑文件 如何允许用户按下 MVC 表单上的按钮来请求通过浏览器发回文件并显示标准保存 打开对话框 我想
  • C# 如何获取 COM 接口的实例

    我做了很多谷歌搜索 试图找到获取 COM 接口实例的标准方法 微软在他们的文章中提供了一个例子COM 互操作第 1 部分 客户端教程 https msdn microsoft com en us library aa645736 v vs