在 ABBYY FineReader Engine 12 中,共有三种方式加载 Engine 对象。每种加载方式都有其特定的使用场景,会影响该对象在不同情况下的使用方式。前两种方式最适合用于无需同时处理多个请求的交互式应用程序,第三种方式则最适合服务器端解决方案。
手动加载 FREngine.dll 并使用”裸”接口
这是加载 Engine 对象的标准方法。要获取 Engine 对象的引用,请调用 InitializeEngine 函数。
优点 | 限制 |
|---|
- 可实现最高性能。
- 无需注册 FREngine.dll。
| |
public class EngineLoader : IDisposable
{
public EngineLoader()
{
// 使用 FREngine.dll 的完整路径、您的 Customer Project ID,
// 以及(如适用)Online License 令牌文件的路径和 Online License 密码来初始化这些变量
string enginePath = "";
string customerProjectId = "";
string licensePath = "";
string licensePassword = "";
// 加载 FREngine.dll 库
dllHandle = LoadLibraryEx(enginePath, IntPtr.Zero, LOAD_WITH_ALTERED_SEARCH_PATH);
try
{
if (dllHandle == IntPtr.Zero)
{
throw new Exception("Can't load " + enginePath);
}
IntPtr initializeEnginePtr = GetProcAddress(dllHandle, "InitializeEngine");
if (initializeEnginePtr == IntPtr.Zero)
{
throw new Exception("Can't find InitializeEngine function");
}
IntPtr deinitializeEnginePtr = GetProcAddress(dllHandle, "DeinitializeEngine");
if (deinitializeEnginePtr == IntPtr.Zero)
{
throw new Exception("Can't find DeinitializeEngine function");
}
IntPtr dllCanUnloadNowPtr = GetProcAddress(dllHandle, "DllCanUnloadNow");
if (dllCanUnloadNowPtr == IntPtr.Zero)
{
throw new Exception("Can't find DllCanUnloadNow function");
}
// 将指针转换为委托
initializeEngine = (InitializeEngine)Marshal.GetDelegateForFunctionPointer(
initializeEnginePtr, typeof(InitializeEngine));
deinitializeEngine = (DeinitializeEngine)Marshal.GetDelegateForFunctionPointer(
deinitializeEnginePtr, typeof(DeinitializeEngine));
dllCanUnloadNow = (DllCanUnloadNow)Marshal.GetDelegateForFunctionPointer(
dllCanUnloadNowPtr, typeof(DllCanUnloadNow));
// 调用 InitializeEngine 函数,
// 传入 Online License 文件路径和 Online License 密码
int hresult = initializeEngine(customerProjectId, licensePath, licensePassword,
"", "", false, ref engine);
Marshal.ThrowExceptionForHR(hresult);
}
catch (Exception)
{
// 释放 FREngine.dll 库
engine = null;
// 在调用 FreeLibrary 之前销毁所有对象
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
FreeLibrary(dllHandle);
dllHandle = IntPtr.Zero;
initializeEngine = null;
deinitializeEngine = null;
dllCanUnloadNow = null;
throw;
}
}
// Kernel32.dll 函数
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibraryEx(string dllToLoad, IntPtr reserved, uint flags);
private const uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;
[DllImport("kernel32.dll")]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
private static extern bool FreeLibrary(IntPtr hModule);
// FREngine.dll 函数
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private delegate int InitializeEngine(string customerProjectId, string licensePath,
string licensePassword, string tempFolder, string dataFolder, bool isSharedCPUCoresMode,
ref FREngine.IEngine engine);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int DeinitializeEngine();
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate int DllCanUnloadNow();
// 私有变量
private FREngine.IEngine engine = null;
// FREngine.dll 的 Handle
private IntPtr dllHandle = IntPtr.Zero;
private InitializeEngine initializeEngine = null;
private DeinitializeEngine deinitializeEngine = null;
private DllCanUnloadNow dllCanUnloadNow = null;
}
Engine 作为进程内服务器加载到应用程序运行的同一进程中。Engine 对象通过 InprocLoader 对象加载,该对象实现了 IEngineLoader 接口。
优点 | 限制 |
|---|
- 所有 ABBYY FineReader Engine 对象均完全线程安全,可在不同线程中创建和使用。
- 从主 STA 单元工作时,性能与直接使用裸接口相同。从不同线程访问时可能会产生封送开销,但在大多数场景下可忽略不计。
| - 在最终用户计算机上安装应用程序时,需要注册 FREngine.dll。
|
IEngineLoader engineLoader = new FREngine.InprocLoader();
IEngine engine = engineLoader.InitializeEngine( customerProjectId, licensePath, licensePassword, "", "", false );
try {
...
} finally {
engineLoader.ExplicitlyUnload();
}
若要在最终用户计算机上安装应用程序时注册 FREngine.dll,请使用 regsvr32 工具。如果您使用的是 64 位操作系统,则默认运行 64 位版本的 regsvr32。请使用以下命令行:
regsvr32 /s /n /i:"<path to the Inc folder>" "<path to FREngine.dll>"
Engine 作为进程外服务器加载到独立进程中。Engine 对象通过 OutprocLoader 对象加载,该对象实现了 IEngineLoader 接口。
优点 | 限制 |
|---|
- 所有 ABBYY FineReader Engine 对象均完全线程安全。每个 Engine 实例在独立进程中并行运行。
- 可组建处理器池,充分利用计算机的 CPU 性能。
| - 存在少量封送处理开销。
- 在最终用户计算机上安装应用程序时,需要注册 FREngine.dll。
- 在权限受限的账户下运行时,必须授予必要的权限。
- 无法以 HBITMAP 形式访问页面图像。
- 不能与 Visual Components 配合使用,因为 Visual Components 不支持多进程工作。
|
IEngineLoader engineLoader = new FREngine.OutprocLoader();
IEngine engine = engineLoader.InitializeEngine( customerProjectId, licensePath, licensePassword, "", "", false);
try {
...
} finally {
engineLoader.ExplicitlyUnload();
}
这种加载 Engine 对象的方式尤其体现在 EnginesPool 代码示例中,该示例为多线程应用程序提供了可复用的解决方案。
- 可使用 DCOM Config 实用程序设置账户权限 (在命令行中输入 DCOMCNFG,或依次选择”控制面板”>“管理工具”>“组件服务”) 。在控制台树中,找到”组件服务”>“计算机”>“我的电脑”>“DCOM 配置”文件夹,右键单击 ABBYY FineReader Engine 12.5 Loader (本地服务器) ,然后单击”属性”。此时将打开一个对话框。单击”安全”选项卡。在”启动权限”下,单击”自定义”,然后单击”编辑”以指定可启动该应用程序的账户。
请注意,在 64 位操作系统上,已注册的 DCOM 应用程序可在 32 位 MMC 控制台中使用,可通过以下命令行启动该控制台:
- 在最终用户计算机上安装应用程序时,若要注册 FREngine.dll,请使用 regsvr32 工具。如果您使用的是 64 位操作系统,则默认运行 64 位版本的 regsvr32。请使用以下命令行:
regsvr32 /s /n /i:"<path to the Inc folder>" "<path to FREngine.dll>"
- 我们建议您在调试和运行服务器应用程序时均使用 Network 许可证。
此外,您可以通过 IHostProcessControl 接口管理 host process 的优先级,并控制其是否保持活动状态。
下表汇总了三种加载方式的特性:
| 特性 | 裸接口 | 进程内服务器 | 进程外服务器 |
|---|
| 无需代理即可运行 (无编组开销) | 是 | 存在限制 | 否 |
| 适用于多线程应用程序 | 存在限制 | 是 | 是 |
| 可并行运行进程 (适用于服务器解决方案) | 否 | 否 | 是 |
| 无需注册 FREngine.dll | 是 | 否 | 否 |
| 无需配置运行权限 | 是 | 是 | 否 |
| 可使用 new 运算符创建对象,支持脚本调用 | 否 | 是 | 是 |
| 支持以 HBITMAP 形式访问页面图像 | 是 | 是 | 否 |
| 在独立进程中运行 (容错性强,支持回收) | 否 | 否 | 是 |
| 可与 Visual Components 配合使用 | 是 | 是 | 否 |
在多线程服务器应用程序中使用 ABBYY FineReader Engine