【C#】【EXCEL】Bumblebee/Classes/Marshal2.cs

Bumblebee/Classes/Marshal2.cs

这个流程图将描述 Marshal2.GetActiveObject 方法的执行过程。
成功 / Success 失败 / Failure 开始 / Start 调用 GetActiveObject
/ Call GetActiveObject 尝试 CLSIDFromProgIDEx
/ Try CLSIDFromProgIDEx 获取 CLSID
/ Get CLSID 调用 CLSIDFromProgID
/ Call CLSIDFromProgID 调用 GetActiveObject
/ Call GetActiveObject 返回对象 / Return Object 结束 / End

这个流程图描述了以下过程:

  1. 开始执行 GetActiveObject 方法。

    Start executing the GetActiveObject method.

  2. 调用 GetActiveObject 方法,传入 progID 参数。

    Call the GetActiveObject method with the progID parameter.

    ProgID(Programmatic Identifier)是 Windows 编程中的一个重要概念,特别是在 COM(Component Object Model)编程中。让我为您详细解释:

    1. 定义:

      • ProgID 是一个人类可读的字符串,用于标识一个 COM 类。
      • 它是 CLSID(Class Identifier)的一种更友好的别名。
    2. 格式:

      • 通常采用 "ApplicationName.ObjectType" 的格式。
      • 例如:"Excel.Application" 或 "Word.Document"。
    3. 用途:

      • 提供一种更容易记忆和使用的方式来引用 COM 对象。
      • 程序员可以使用 ProgID 来创建或获取 COM 对象实例,而不需要记住复杂的 CLSID。
    4. 与 CLSID 的关系:

      • 每个 ProgID 都映射到一个特定的 CLSID。
      • 系统维护着 ProgID 到 CLSID 的映射关系。
    5. 在系统中的存储:

      • ProgID 和对应的 CLSID 映射存储在 Windows 注册表中。
      • 通常位于 HKEY_CLASSES_ROOT 下。
    6. 版本控制:

      • 可以包含版本信息,如 "Excel.Application.11"。
      • 不带版本号的 ProgID 通常指向最新版本。
    7. 在代码中的应用:

      • 在您提供的 GetActiveObject 方法中,ProgID 作为参数传入。
      • 方法内部将 ProgID 转换为对应的 CLSID。
    8. 优点:

      • 使代码更易读和维护。
      • 允许开发者使用描述性名称而不是复杂的 GUID。
    9. 示例:

      • "Excel.Application" 用于 Microsoft Excel
      • "Word.Application" 用于 Microsoft Word
      • "Scripting.FileSystemObject" 用于文件系统操作
    10. 使用场景:

      • 创建 COM 对象实例。
      • 获取已运行的 COM 对象(如您代码中的情况)。
      • 在脚本语言中引用 COM 对象。

    代码中,GetActiveObject 方法接受一个 ProgID 作为参数,然后内部使用 CLSIDFromProgIDExCLSIDFromProgID 方法将其转换为 CLSID。这展示了 ProgID 在简化 COM 编程中的重要作用,使得开发者可以使用更直观的标识符来操作 COM 对象。

  3. 尝试使用 CLSIDFromProgIDEx 方法获取 CLSID。

    Try to get the CLSID using the CLSIDFromProgIDEx method.

    CLSID(Class Identifier)是一个非常重要的概念,尤其在 Windows 编程和 COM(Component Object Model)技术中。我来为您详细解释一下:

    1. 定义:

      • CLSID 是 Class Identifier 的缩写。
      • 它是一个 128 位的全局唯一标识符(GUID)。
    2. 用途:

      • CLSID 用于唯一标识 COM 组件中的一个类。
      • 它允许 Windows 和其他程序准确地识别和加载特定的 COM 对象。
    3. 格式:

      • 通常表示为 32 个十六进制数字,分为 5 组,用连字符分隔。
      • 例如:{00000000-0000-0000-0000-000000000000}
    4. 在系统中的存储:

      • CLSID 通常存储在 Windows 注册表中。
      • 路径通常是 HKEY_CLASSES_ROOT\CLSID
    5. 与 ProgID 的关系:

      • ProgID(Programmatic Identifier)是一个更易读的字符串,用于表示 CLSID。
      • 系统可以通过 ProgID 查找对应的 CLSID。
    6. 在代码中的应用:

      • 在您提供的代码中,CLSIDFromProgIDExCLSIDFromProgID 方法就是用来将 ProgID 转换为 CLSID。
      • 获取 CLSID 后,可以用它来创建或获取 COM 对象的实例。
    7. 重要性:

      • CLSID 确保了 COM 组件的唯一性和版本控制。
      • 它允许不同版本的同一组件共存,因为每个版本可以有不同的 CLSID。
    8. 创建:

      • 开发者通常使用工具(如 Visual Studio)自动生成 CLSID。
      • 也可以使用专门的 GUID 生成工具创建。

    代码中,GetActiveObject 方法首先尝试通过 ProgID 获取 CLSID,然后使用这个 CLSID 来获取活动的 COM 对象。这个过程展示了 CLSID 在 COM 编程中的核心作用。

  4. 如果 CLSIDFromProgIDEx 成功,直接获取 CLSID。

    If CLSIDFromProgIDEx succeeds, directly get the CLSID.

  5. 如果 CLSIDFromProgIDEx 失败(抛出异常),则调用 CLSIDFromProgID 方法。

    If CLSIDFromProgIDEx fails (throws an exception), call the CLSIDFromProgID method.

  6. 获取 CLSID 后,调用 COM 接口的 GetActiveObject 方法。

    After getting the CLSID, call the COM interface's GetActiveObject method.

  7. 返回获取到的对象。

    Return the obtained object.

  8. 方法执行结束。

    End of method execution.

这个流程图对应了代码中的主要步骤,特别是 GetActiveObject 方法中的异常处理和备用方法调用。它展示了如何通过 progID 获取 CLSID,然后使用 CLSID 获取活动对象的过程。

Description

csharp 复制代码
public static class Marshal2
{
    internal const String OLEAUT32 = "oleaut32.dll";
    internal const String OLE32 = "ole32.dll";

    // GetActiveObject 方法
    [System.Security.SecurityCritical]  // auto-generated_required
    public static Object GetActiveObject(String progID)
    {
        Object obj = null;
        Guid clsid;

        // 首先尝试调用 CLSIDFromProgIDEx,如果失败则回退到 CLSIDFromProgID
        // First try CLSIDFromProgIDEx, fall back to CLSIDFromProgID if it fails
        try
        {
            CLSIDFromProgIDEx(progID, out clsid);
        }
        catch (Exception)
        {
            CLSIDFromProgID(progID, out clsid);
        }

        // 使用获取的 CLSID 来获取活动对象
        GetActiveObject(ref clsid, IntPtr.Zero, out obj);
        return obj;
    }

    // CLSIDFromProgIDEx 方法
    [DllImport(OLE32, PreserveSig = false)]
    [ResourceExposure(ResourceScope.None)]
    [SuppressUnmanagedCodeSecurity]
    [System.Security.SecurityCritical]  // auto-generated
    private static extern void CLSIDFromProgIDEx([MarshalAs(UnmanagedType.LPWStr)] String progId, out Guid clsid);

    // CLSIDFromProgID 方法
    [DllImport(OLE32, PreserveSig = false)]
    [ResourceExposure(ResourceScope.None)]
    [SuppressUnmanagedCodeSecurity]
    [System.Security.SecurityCritical]  // auto-generated
    private static extern void CLSIDFromProgID([MarshalAs(UnmanagedType.LPWStr)] String progId, out Guid clsid);

    // GetActiveObject 方法(COM 接口调用)
    [DllImport(OLEAUT32, PreserveSig = false)]
    [ResourceExposure(ResourceScope.None)]
    [SuppressUnmanagedCodeSecurity]
    [System.Security.SecurityCritical]  // auto-generated
    private static extern void GetActiveObject(ref Guid rclsid, IntPtr reserved, [MarshalAs(UnmanagedType.Interface)] out Object ppunk);
}

现在让我们详细解释每个方法:

  1. GetActiveObject(String progID) 方法:

    • 这是一个公共静态方法,用于获取已经运行的 COM 对象的引用。
    • 参数 progID 是程序标识符,用于标识特定的 COM 类。
    • 方法流程:
      1. 首先尝试使用 CLSIDFromProgIDEx 获取 CLSID。
      2. 如果失败(可能是因为 CLSIDFromProgIDEx 不存在),则回退到使用 CLSIDFromProgID
      3. 获取 CLSID 后,调用 COM 接口的 GetActiveObject 方法获取活动对象。
    • 返回值是获取到的 COM 对象的引用。

    这个方法展示了如何通过 ProgID 获取正在运行的 COM 对象,这在与已打开的应用程序(如 Excel、Word 等)交互时非常有用。

    This method demonstrates how to obtain a reference to a running COM object using its ProgID, which is particularly useful when interacting with already open applications like Excel or Word.

  2. CLSIDFromProgIDEx 方法:

    • 这是一个私有静态方法,通过 P/Invoke 调用 OLE32.dll 中的同名函数。
    • 用于将 ProgID 转换为 CLSID,支持更新的 COM 功能。
    • [DllImport(OLE32, PreserveSig = false)] 表示这是一个从 OLE32.dll 导入的函数。
    • [MarshalAs(UnmanagedType.LPWStr)] 指定 progId 参数应被封送为宽字符字符串。
  3. CLSIDFromProgID 方法:

    • 功能类似于 CLSIDFromProgIDEx,但用于较旧的系统或不支持 Ex 版本的情况。
    • 这是一个后备方法,在 CLSIDFromProgIDEx 失败时使用。
  4. GetActiveObject 方法(COM 接口调用):

    • 这是对 COM 接口 GetActiveObject 函数的 P/Invoke 调用。
    • 用于通过 CLSID 获取已运行的 COM 对象的引用。
    • ref Guid rclsid 参数传递 CLSID。
    • IntPtr reserved 保留参数,通常设为 IntPtr.Zero。
    • [MarshalAs(UnmanagedType.Interface)] out Object ppunk 用于接收返回的 COM 对象接口。

这些方法共同工作,实现了通过 ProgID 获取正在运行的 COM 对象的功能。这在与 Office 应用程序或其他 COM 对象交互时非常有用,允许程序连接到已经运行的实例,而不是创建新实例。

代码中使用了多个特性(Attributes)来确保安全性和正确的内存管理:

  • [System.Security.SecurityCritical] 表示这些方法需要完全信任。
  • [SuppressUnmanagedCodeSecurity] 用于提高性能,但可能会降低安全性。
  • [ResourceExposure(ResourceScope.None)] 指示这些方法不暴露任何资源。

这段代码展示了如何安全地与底层 Windows API 交互,同时提供了一个高级抽象,使得在 .NET 环境中更容易使用 COM 对象。

Code

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Text;
using System.Threading.Tasks;

namespace Bumblebee
{
    // Marshal2 类用于处理 COM 对象的封送处理
    public static class Marshal2
    {
        // 定义常量,指定需要调用的 DLL 文件名
        internal const String OLEAUT32 = "oleaut32.dll";
        internal const String OLE32 = "ole32.dll";

        /// <summary>
        /// 获取已经运行的 COM 对象的引用
        /// </summary>
        /// <param name="progID">COM 对象的程序标识符</param>
        /// <returns>返回 COM 对象的引用</returns>
        [System.Security.SecurityCritical]  // 表示此方法需要完全信任
        public static Object GetActiveObject(String progID)
        {
            Object obj = null;
            Guid clsid;

            // 首先尝试使用 CLSIDFromProgIDEx 方法获取 CLSID
            // 如果 CLSIDFromProgIDEx 不存在,则回退到使用 CLSIDFromProgID
            try
            {
                CLSIDFromProgIDEx(progID, out clsid);
            }
            catch (Exception)
            {
                // 如果 CLSIDFromProgIDEx 失败,使用 CLSIDFromProgID
                CLSIDFromProgID(progID, out clsid);
            }

            // 使用获取到的 CLSID 来获取活动对象
            GetActiveObject(ref clsid, IntPtr.Zero, out obj);
            return obj;
        }

        /// <summary>
        /// 将 ProgID 转换为 CLSID(扩展版本)
        /// </summary>
        [DllImport(OLE32, PreserveSig = false)]
        [ResourceExposure(ResourceScope.None)]
        [SuppressUnmanagedCodeSecurity]
        [System.Security.SecurityCritical]  // 自动生成的安全关键代码
        private static extern void CLSIDFromProgIDEx(
            [MarshalAs(UnmanagedType.LPWStr)] String progId, 
            out Guid clsid
        );

        /// <summary>
        /// 将 ProgID 转换为 CLSID(标准版本)
        /// </summary>
        [DllImport(OLE32, PreserveSig = false)]
        [ResourceExposure(ResourceScope.None)]
        [SuppressUnmanagedCodeSecurity]
        [System.Security.SecurityCritical]  // 自动生成的安全关键代码
        private static extern void CLSIDFromProgID(
            [MarshalAs(UnmanagedType.LPWStr)] String progId, 
            out Guid clsid
        );

        /// <summary>
        /// 获取活动对象的 COM 接口
        /// </summary>
        [DllImport(OLEAUT32, PreserveSig = false)]
        [ResourceExposure(ResourceScope.None)]
        [SuppressUnmanagedCodeSecurity]
        [System.Security.SecurityCritical]  // 自动生成的安全关键代码
        private static extern void GetActiveObject(
            ref Guid rclsid, 
            IntPtr reserved, 
            [MarshalAs(UnmanagedType.Interface)] out Object ppunk
        );
    }
}

这段代码的主要功能和特点:

  1. 命名空间和类

    • 代码定义在 Bumblebee 命名空间中。
    • Marshal2 是一个静态类,用于处理 COM 对象的封送处理。
  2. 常量定义

    • OLEAUT32OLE32 定义了需要调用的 DLL 文件名。
  3. GetActiveObject 方法

    • 这是主要的公共方法,用于获取已运行的 COM 对象的引用。
    • 它首先尝试使用 CLSIDFromProgIDEx,如果失败则回退到 CLSIDFromProgID
    • 最后调用 COM 接口的 GetActiveObject 方法获取对象引用。
  4. CLSIDFromProgIDEx 和 CLSIDFromProgID 方法

    • 这两个方法都用于将 ProgID 转换为 CLSID。
    • CLSIDFromProgIDEx 是更新的版本,支持更多功能。
    • 它们都是通过 P/Invoke 调用 OLE32.dll 中的同名函数。
  5. GetActiveObject 方法(COM 接口调用)

    • 这个方法是对 COM 接口 GetActiveObject 函数的 P/Invoke 调用。
    • 用于通过 CLSID 获取已运行的 COM 对象的引用。
  6. 特性(Attributes)使用

    • [DllImport]:指定要导入的 DLL 和函数。
    • [ResourceExposure]:指示资源暴露范围。
    • [SuppressUnmanagedCodeSecurity]:抑制对非托管代码的安全检查,提高性能。
    • [System.Security.SecurityCritical]:表示这些方法需要完全信任。

这段代码展示了如何在 .NET 环境中安全地与 COM 对象交互,特别是如何获取已经运行的 COM 对象的引用。这在与 Office 应用程序或其他 COM 对象交互时非常有用,允许程序连接到已经运行的实例,而不是创建新实例。

相关推荐
军训猫猫头1 小时前
20.抽卡只有金,带保底(WPF) C#
ui·c#·wpf
半盏茶香1 小时前
在21世纪的我用C语言探寻世界本质 ——编译和链接(编译环境和运行环境)
c语言·开发语言·c++·算法
Evand J2 小时前
LOS/NLOS环境建模与三维TOA定位,MATLAB仿真程序,可自定义锚点数量和轨迹点长度
开发语言·matlab
LucianaiB2 小时前
探索CSDN博客数据:使用Python爬虫技术
开发语言·爬虫·python
Ronin3052 小时前
11.vector的介绍及模拟实现
开发语言·c++
计算机学长大白3 小时前
C中设计不允许继承的类的实现方法是什么?
c语言·开发语言
PieroPc4 小时前
Python 写的 智慧记 进销存 辅助 程序 导入导出 excel 可打印
开发语言·python·excel
2401_857439696 小时前
SSM 架构下 Vue 电脑测评系统:为电脑性能评估赋能
开发语言·php
SoraLuna7 小时前
「Mac畅玩鸿蒙与硬件47」UI互动应用篇24 - 虚拟音乐控制台
开发语言·macos·ui·华为·harmonyos
xlsw_7 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis