虽然2025年已接近尾声,但是不得不承认,在我们编写程序的过程中,许多优秀的OCX组件,依旧提供了超强的战斗力。这些先辈们手撕代码写出来的组件,依然在许多最关键的时候发生重大作用。
但毕竟时代发生变化,那些古老的OCX组件要想在今天依旧发光,还是需要做一些适配,才能跟得上时代的步伐。
我使用Vistual Studio编写代码,用到C#和wpf,其中winform控件做为OCX组件的承载。
一、使用OCX组件的最简单方法就是直接注册组件,方法是:
regsvr32 ocxcontrol.ocx
这样组件就注册成功,然后在winform工具箱中加入OCX组件的图标,然后就可以直接拖拽这个图标到winform控件中使用了。(值得注意的是,有些文章提到如果是32位OCX组件在64位系统上应用,那么应当将组件Copy到特定目录,经过测试,感觉这个问题并不存在,也许与操作系统版本有关。)
提议在编写代码时,先使用这种方法注册组件,以方便编写代码。
在实际应用程序分发时,当然也可以使用这种方法对组件注册,以让程序顺利运行。

二、在程序中自带组件注册的代码:这种方法比第一种方法更容易操作一些,适合将程序分发给不太熟悉电脑操作的用户,在软件的界面上实现组件注册。优点是通俗易懂,缺点是需要以管理员身份运行程序才能注册成功。伪代码大体如下:
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace ComponentRegistrationExample
{
/// <summary>
/// 组件注册工具示例类
/// </summary>
public class ComponentRegistrar
{
// 实际组件文件名)
private const string ComponentFileName = “SampleComponent.ocx”;
// 组件配置文件名
private const string ComponentConfigFileName = “SampleComponent.inf”;
// 组件唯一标识(替换为实际CLSID,可从组件开发文档获取)
private const string ComponentCLSID = “{00000000-0000-0000-0000-000000000000}”;
// 调用组件的注册函数(DllRegisterServer是OCX标准注册入口,无需修改)
[DllImport(ComponentFileName, SetLastError = true)]
private static extern int DllRegisterServer();
// 调用组件的反注册函数(标准入口,无需修改)
[DllImport(ComponentFileName, SetLastError = true)]
private static extern int DllUnregisterServer();
/// <summary>
/// 执行组件注册(含文件复制+系统注册)
/// </summary>
/// <returns>注册成功返回true,失败返回false</returns>
public static bool RegisterComponent()
{
try
{
// 1. 定义文件路径(源路径:程序当前目录;目标路径:系统目录)
string sourceComponentPath = Path.Combine(Directory.GetCurrentDirectory(), ComponentFileName);
string sourceConfigPath = Path.Combine(Directory.GetCurrentDirectory(), ComponentConfigFileName);
string systemDir = Environment.GetFolderPath(Environment.SpecialFolder.System);
string targetComponentPath = Path.Combine(systemDir, ComponentFileName);
string targetConfigPath = Path.Combine(systemDir, ComponentConfigFileName);
// 2. 复制组件文件到系统目录(覆盖已存在文件)
if (File.Exists(sourceComponentPath))
{
File.Copy(sourceComponentPath, targetComponentPath, true);
}
else
{
Console.WriteLine($”错误:未找到组件文件 {sourceComponentPath}”);
return false;
}
// 复制配置文件(若组件需要,可根据实际情况启用)
if (File.Exists(sourceConfigPath))
{
File.Copy(sourceConfigPath, targetConfigPath, true);
}
else
{
Console.WriteLine($”警告:未找到配置文件 {sourceConfigPath}(部分组件可能无需此文件)”);
}
// 3. 调用组件注册函数(返回值>=0表明成功,具体含义参考组件文档)
int registerResult = DllRegisterServer();
if (registerResult >= 0)
{
Console.WriteLine(“组件注册成功”);
return true;
}
else
{
Console.WriteLine($”组件注册失败,错误码:{registerResult}(可通过GetLastError获取详细信息)”);
return false;
}
}
catch (Exception ex)
{
Console.WriteLine($”注册过程异常:{ex.Message}”);
return false;
}
}
/// <summary>
/// 检查组件是否已注册(通过注册表CLSID判断)
/// </summary>
/// <returns>已注册返回true,未注册返回false</returns>
public static bool IsComponentRegistered()
{
// 注册表路径:HKEY_CLASSES_ROOTCLSID{你的组件CLSID}(OCX注册的标准路径)
string registryKeyPath = $@”CLSID{ComponentCLSID}”;
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey(registryKeyPath))
{
bool isRegistered = key != null;
Console.WriteLine(isRegistered ? “组件已注册” : “组件未注册”);
return isRegistered;
}
}
/// <summary>
/// 执行组件反注册(可选,用于卸载组件)
/// </summary>
/// <returns>反注册成功返回true,失败返回false</returns>
public static bool UnregisterComponent()
{
try
{
int unregisterResult = DllUnregisterServer();
if (unregisterResult >= 0)
{
Console.WriteLine(“组件反注册成功”);
return true;
}
else
{
Console.WriteLine($”组件反注册失败,错误码:{unregisterResult}”);
return false;
}
}
catch (Exception ex)
{
Console.WriteLine($”反注册过程异常:{ex.Message}”);
return false;
}
}
}
}
三、上面的方法已经能解决大部分问题,但有一种特殊情况无法解决,那就是OCX组件冲突。列如你使用了一个版本,但别人的程序使用了另一个版本,当这两个程序都安装在同一台电脑上时,可能就会有一个程序不能正常运行。
所以我们需要另一种方法来运行它,那就是免注册加载OCX组件。
其核心原理是用“清单文件”替代注册表的组件注册功能,软件启动时,会优先读取清单文件(app.manifest),根据清单中声明的依赖,找到对应的组件清单(如 test.ocx.manifest),再通过组件清单定位 OCX 控件文件,完成组件的激活和加载,全程不依赖系统注册表。
1、获取OCX元数据,可以通过注册组件的注册表信息获得,也可以通过工具(OleView.exe 或注册表)获取目标 OCX(如 ocxcontrol.ocx)的核心元数据:
CLSID:控件唯一标识(示例:{
XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX})
TLBID:类型库标识(示例:{
YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY})
文件版本:OCX 版本号(示例:1.0.0.0,需与实际文件属性一致)
线程模型:控件支持的线程模型(一般为 Apartment)
2、组件清单(ocxcontrol.manifest)
作用:描述 OCX 控件的元数据,供系统识别控件信息。
命名规则:文件名必须与清单中 assemblyIdentity 的 name 属性一致(如 → 文件名 ocxcontrol.manifest)。
<?xml version=”1.0″ encoding=”UTF-8″?>
<assembly xmlns=”urn:schemas-microsoft-com:asm.v1″ manifestVersion=”1.0″>
<!– 组件标识:需与应用清单依赖声明完全一致 –>
<assemblyIdentity
type=”win32″ <!– 固定值,标识 Win32 组件 –>
name=”ocxcontrol” <!– 组件名称,与文件名匹配 –>
version=”1.0.0.0″ <!– OCX 实际版本号,需精准替换 –>
processorArchitecture=”x86″<!– 平台架构:32位 OCX 填 x86,64位填 amd64 –>
/>
<!– 关联 OCX 文件:声明控件物理路径(相对/绝对) –>
<file>
<!– 注册 CLSID:替换为实际 OCX 的 CLSID –>
<comClass
clsid=”{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}”
progid=”ocxcontrol.1″ <!– 可选,程序ID,格式:组件名.版本 –>
threadingModel=”Apartment”<!– 线程模型,与控件实际一致 –>
/>
<!– 注册类型库:替换为实际 OCX 的 TLBID 和版本 –>
<typelib
tlbid=”{YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY}”
version=”1.0″ <!– 类型库版本,与实际一致 –>
helpdir=”” <!– 协助文档路径,可选 –>
/>
</file>
</assembly>
3、应用清单(app.manifest)
<?xml version=”1.0″ encoding=”utf-8″?>
<assembly manifestVersion=”1.0″ xmlns=”urn:schemas-microsoft-com:asm.v1″>
<!– 应用标识:可选,描述应用信息 –>
<assemblyIdentity version=”1.0.0.0″/>
<!– UAC 权限配置:根据需求设置(如普通权限 asInvoker) –>
<trustInfo xmlns=”urn:schemas-microsoft-com:asm.v3″>
<security>
<requestedPrivileges>
<requestedExecutionLevel level=”asInvoker” uiAccess=”false” />
</requestedPrivileges>
</security>
</trustInfo>
<!– 依赖声明:需与组件清单的 assemblyIdentity 完全一致 –>
<dependency>
<dependentAssembly>
<assemblyIdentity
type=”win32″
name=”ocxcontrol” <!– 组件名称,与组件清单一致 –>
version=”1.0.0.0″ <!– 版本号,与组件清单一致 –>
processorArchitecture=”x86″<!– 平台架构,与组件清单一致 –>
/>
</dependentAssembly>
</dependency>
</assembly>
4、文件部署:以下文件需放在同一目录(如 binDebug):
应用程序:MyApp.exe(替换为实际应用名)
清单文件:app.manifest、ocxcontrol.manifest
OCX 控件:ocxcontrol.ocx
互操作程序集:AxocxcontrolLIB.dll、Interop.ocxcontrolLIB.dll(如按上面操作,这个是自动生成的,不用管)
5、验证是否成功,通过反注册 OCX:执行命令(替换为实际路径),确保系统无注册信息:
cmd
regsvr32 /u ocxcontrol.ocx”



