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
这个流程图描述了以下过程:
-
开始执行
GetActiveObject
方法。Start executing the
GetActiveObject
method. -
调用
GetActiveObject
方法,传入 progID 参数。Call the
GetActiveObject
method with the progID parameter.ProgID(Programmatic Identifier)是 Windows 编程中的一个重要概念,特别是在 COM(Component Object Model)编程中。让我为您详细解释:
-
定义:
- ProgID 是一个人类可读的字符串,用于标识一个 COM 类。
- 它是 CLSID(Class Identifier)的一种更友好的别名。
-
格式:
- 通常采用 "ApplicationName.ObjectType" 的格式。
- 例如:"Excel.Application" 或 "Word.Document"。
-
用途:
- 提供一种更容易记忆和使用的方式来引用 COM 对象。
- 程序员可以使用 ProgID 来创建或获取 COM 对象实例,而不需要记住复杂的 CLSID。
-
与 CLSID 的关系:
- 每个 ProgID 都映射到一个特定的 CLSID。
- 系统维护着 ProgID 到 CLSID 的映射关系。
-
在系统中的存储:
- ProgID 和对应的 CLSID 映射存储在 Windows 注册表中。
- 通常位于 HKEY_CLASSES_ROOT 下。
-
版本控制:
- 可以包含版本信息,如 "Excel.Application.11"。
- 不带版本号的 ProgID 通常指向最新版本。
-
在代码中的应用:
- 在您提供的
GetActiveObject
方法中,ProgID 作为参数传入。 - 方法内部将 ProgID 转换为对应的 CLSID。
- 在您提供的
-
优点:
- 使代码更易读和维护。
- 允许开发者使用描述性名称而不是复杂的 GUID。
-
示例:
- "Excel.Application" 用于 Microsoft Excel
- "Word.Application" 用于 Microsoft Word
- "Scripting.FileSystemObject" 用于文件系统操作
-
使用场景:
- 创建 COM 对象实例。
- 获取已运行的 COM 对象(如您代码中的情况)。
- 在脚本语言中引用 COM 对象。
代码中,
GetActiveObject
方法接受一个 ProgID 作为参数,然后内部使用CLSIDFromProgIDEx
或CLSIDFromProgID
方法将其转换为 CLSID。这展示了 ProgID 在简化 COM 编程中的重要作用,使得开发者可以使用更直观的标识符来操作 COM 对象。 -
-
尝试使用
CLSIDFromProgIDEx
方法获取 CLSID。Try to get the CLSID using the
CLSIDFromProgIDEx
method.CLSID(Class Identifier)是一个非常重要的概念,尤其在 Windows 编程和 COM(Component Object Model)技术中。我来为您详细解释一下:
-
定义:
- CLSID 是 Class Identifier 的缩写。
- 它是一个 128 位的全局唯一标识符(GUID)。
-
用途:
- CLSID 用于唯一标识 COM 组件中的一个类。
- 它允许 Windows 和其他程序准确地识别和加载特定的 COM 对象。
-
格式:
- 通常表示为 32 个十六进制数字,分为 5 组,用连字符分隔。
- 例如:{00000000-0000-0000-0000-000000000000}
-
在系统中的存储:
- CLSID 通常存储在 Windows 注册表中。
- 路径通常是 HKEY_CLASSES_ROOT\CLSID
-
与 ProgID 的关系:
- ProgID(Programmatic Identifier)是一个更易读的字符串,用于表示 CLSID。
- 系统可以通过 ProgID 查找对应的 CLSID。
-
在代码中的应用:
- 在您提供的代码中,
CLSIDFromProgIDEx
和CLSIDFromProgID
方法就是用来将 ProgID 转换为 CLSID。 - 获取 CLSID 后,可以用它来创建或获取 COM 对象的实例。
- 在您提供的代码中,
-
重要性:
- CLSID 确保了 COM 组件的唯一性和版本控制。
- 它允许不同版本的同一组件共存,因为每个版本可以有不同的 CLSID。
-
创建:
- 开发者通常使用工具(如 Visual Studio)自动生成 CLSID。
- 也可以使用专门的 GUID 生成工具创建。
代码中,
GetActiveObject
方法首先尝试通过 ProgID 获取 CLSID,然后使用这个 CLSID 来获取活动的 COM 对象。这个过程展示了 CLSID 在 COM 编程中的核心作用。 -
-
如果
CLSIDFromProgIDEx
成功,直接获取 CLSID。If
CLSIDFromProgIDEx
succeeds, directly get the CLSID. -
如果
CLSIDFromProgIDEx
失败(抛出异常),则调用CLSIDFromProgID
方法。If
CLSIDFromProgIDEx
fails (throws an exception), call theCLSIDFromProgID
method. -
获取 CLSID 后,调用 COM 接口的
GetActiveObject
方法。After getting the CLSID, call the COM interface's
GetActiveObject
method. -
返回获取到的对象。
Return the obtained object.
-
方法执行结束。
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);
}
现在让我们详细解释每个方法:
-
GetActiveObject(String progID)
方法:- 这是一个公共静态方法,用于获取已经运行的 COM 对象的引用。
- 参数
progID
是程序标识符,用于标识特定的 COM 类。 - 方法流程:
- 首先尝试使用
CLSIDFromProgIDEx
获取 CLSID。 - 如果失败(可能是因为
CLSIDFromProgIDEx
不存在),则回退到使用CLSIDFromProgID
。 - 获取 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.
-
CLSIDFromProgIDEx
方法:- 这是一个私有静态方法,通过 P/Invoke 调用 OLE32.dll 中的同名函数。
- 用于将 ProgID 转换为 CLSID,支持更新的 COM 功能。
[DllImport(OLE32, PreserveSig = false)]
表示这是一个从 OLE32.dll 导入的函数。[MarshalAs(UnmanagedType.LPWStr)]
指定progId
参数应被封送为宽字符字符串。
-
CLSIDFromProgID
方法:- 功能类似于
CLSIDFromProgIDEx
,但用于较旧的系统或不支持 Ex 版本的情况。 - 这是一个后备方法,在
CLSIDFromProgIDEx
失败时使用。
- 功能类似于
-
GetActiveObject
方法(COM 接口调用):- 这是对 COM 接口
GetActiveObject
函数的 P/Invoke 调用。 - 用于通过 CLSID 获取已运行的 COM 对象的引用。
ref Guid rclsid
参数传递 CLSID。IntPtr reserved
保留参数,通常设为 IntPtr.Zero。[MarshalAs(UnmanagedType.Interface)] out Object ppunk
用于接收返回的 COM 对象接口。
- 这是对 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
);
}
}
这段代码的主要功能和特点:
-
命名空间和类:
- 代码定义在
Bumblebee
命名空间中。 Marshal2
是一个静态类,用于处理 COM 对象的封送处理。
- 代码定义在
-
常量定义:
OLEAUT32
和OLE32
定义了需要调用的 DLL 文件名。
-
GetActiveObject 方法:
- 这是主要的公共方法,用于获取已运行的 COM 对象的引用。
- 它首先尝试使用
CLSIDFromProgIDEx
,如果失败则回退到CLSIDFromProgID
。 - 最后调用 COM 接口的
GetActiveObject
方法获取对象引用。
-
CLSIDFromProgIDEx 和 CLSIDFromProgID 方法:
- 这两个方法都用于将 ProgID 转换为 CLSID。
CLSIDFromProgIDEx
是更新的版本,支持更多功能。- 它们都是通过 P/Invoke 调用 OLE32.dll 中的同名函数。
-
GetActiveObject 方法(COM 接口调用):
- 这个方法是对 COM 接口
GetActiveObject
函数的 P/Invoke 调用。 - 用于通过 CLSID 获取已运行的 COM 对象的引用。
- 这个方法是对 COM 接口
-
特性(Attributes)使用:
[DllImport]
:指定要导入的 DLL 和函数。[ResourceExposure]
:指示资源暴露范围。[SuppressUnmanagedCodeSecurity]
:抑制对非托管代码的安全检查,提高性能。[System.Security.SecurityCritical]
:表示这些方法需要完全信任。
这段代码展示了如何在 .NET 环境中安全地与 COM 对象交互,特别是如何获取已经运行的 COM 对象的引用。这在与 Office 应用程序或其他 COM 对象交互时非常有用,允许程序连接到已经运行的实例,而不是创建新实例。