NPAPI 插件框架错误

NPAPI plugin framework Error

我正在尝试按照以下答案使用 Yury Sidorov 的 NPAPI 框架:

How to embed Delphi VCL form into HTML page using NPAPI

但是 NPPlugin.pas 出现错误。我正在使用 delphi XE7,在这里我按照 Krom Stern 的说明做了什么

procedure DefDebugOut(const Msg: string);
begin
  OutputDebugStringA(PAnsiChar(Msg + #13#10)); // Changed From Pchar To PAnsiChar
end;

但编译器仍然收到此消息的错误

[dcc32 错误]NPPlugin.pas(2215):E2010 不兼容的类型:'PWideChar' 和 'PAnsiChar'

此过程中出现错误

procedure TPlugin.SetException(const msg: string);
var
  s: String;
begin
  try
    {$ifopt D+} NPP_DebugOut('Exception: ' + msg); {$endif}
    s:=UTF8Encode(msg);
    NPN_SetException(m_pScriptableObject, PAnsiChar(s));
  except
    { prevent any exception from leaking out of DLL }
  end;
end;

这是程序NPN_SetException

procedure NPN_SetException(npobj: PNPObject; msg: PNPUTF8);
begin
  NavigatorFuncs.SetException(npobj, msg);
end;

我将从我们可以看到的内容逐个细分开始。请记住,我们手头没有 NPPlugin.pas 并且必须从问题中的信息中推断其内容。尽管如此,我认为我们有可能准确地做到这一点。


s := UTF8Encode(msg);

这里s是类型string。这是 UnicodeString 的别名,编码为 UTF-16。因此,您从 UTF-16 转换为 UTF-8,然后再转换回 UTF16。

你需要这样的:

NPN_SetException(m_pScriptableObject, PAnsiChar(UTF8Encode(msg)));

或者,如果您需要一个变量来保存 UTF-8 编码的文本,请将其声明为 UTF8String,即 AnsiString(65001)。如果您将 s 的类型更改为 UTF8String 那么问题中的代码将是正确的。虽然比它需要的要冗长一些。


这里还有一个问题:

OutputDebugStringA(PAnsiChar(Msg + #13#10));

您的转换并没有使 Msg 实际上是 8 位编码。但是,您不想使用函数的 ANSI 版本。你需要这个:

OutputDebugString(PChar(Msg + sLineBreak));

您的异常处理程序被误导了。不泄漏异常是 DLL 的工作。如果您试图捕获并抑制它们,您将简单地掩盖您自己代码中的错误。您需要按照库文档给出的说明删除该异常处理程序并检查错误。


现在看大局。以上 None 解释了您报告的错误。唯一合理的解释是您对 NPN_SetException 的声明接受宽文本。在这种情况下,您只需编写以下内容即可使代码编译:

NPN_SetException(m_pScriptableObject, PChar(msg));

当然,这让UTF-8的出现有些莫名其妙。事实上,Mozilla 库确实接受 8 位文本,UTF-8 编码。那么为什么 NPN_SetException 期望传递 UTF-16 文本呢?好吧,事实并非如此。解释是你声明的 NPN_SetException 不正确。所以,要明确一点,虽然 PChar(msg) 会让你的代码编译,但它不会解决你的问题。您将留下在运行时失败的代码。

那么,这是怎么发生的?您将一段使用 PChar 别名为 PAnsiChar 的工作代码放到 Delphi 上,其中 PChar 别名为 PWideChar 并且翻译不正确。即使您编译了代码,它也不会正常工作。您从这样的代码开始:

function NPN_SetException(..., Msg: PChar): ...;

在旧的 Delphi 版本中 PCharPAnsiChar 那么这是正确的。您现在正在 XE7 上编译它,其中 PCharPWideChar,所以这是不正确的。必须是:

function NPN_SetException(..., Msg: PAnsiChar): ...;

那么调用代码可以是:

NPN_SetException(m_pScriptableObject, PAnsiChar(UTF8Encode(msg)));

我的建议是:

  1. 退后一步,重新审视 Delphi 中对 Unicode 的处理。
  2. 回到你原来的代码,把所有使用PChar的Mozilla界面代码改成PAnsiChar
  3. 每当您需要提供 PAnsiChar 时,请使用 PAnsiChar(UTF8Encode(str))

这个 NPAPI 代码显然是为 Delphi 的旧版本设计的,在 Delphi 2009 年切换到 Unicode 之前。默认的 String/(P)Char 类型不再是AnsiString/(P)AnsiChar 的别名,它们现在是 UnicodeString/(P)WideChar 的别名。 UnicodeString 无法转换为 PAnsiChar,就像 AnsiString 永远无法转换为 PWideChar.

DefDebugOut()中,最简单的修复是将PAnsiChar更改为PChar并将OutputDebugStringA()更改为OutputDebugString()

procedure DefDebugOut(const Msg: string);
begin
  OutputDebugString(PChar(Msg + #13#10));
end;

这与所有 Delphi 版本兼容(代码应该从一开始就这样做 - 没有理由直接调用 OutputDebugStringA())。 PCharOutputDebugString() 映射到 Delphi 2007 年及更早版本中的 PAnsiCharOutputDebugStringA(),以及 [=] 中的 PWideCharOutputDebugStringW() 62=] 2009 年及以后。所以一切都匹配。

TPlugin.SetException()中,UTF8Encode() returns在Delphi的所有版本中都有UTF8String。然而,在 Delphi 2009 之前,UTF8String 只是 AnsiString 本身的别名,但在 Delphi 2009 中它被更改为具有完整 RTL 的真正 UTF-8 字符串类型支持(它仍然有一个 AnsiString 基础,所以它仍然可以转换为 PAnsiChar)。将 UTF8String 分配给 UnicodeString 时,编译器执行从 UTF-8 到 UTF-16 的隐式数据转换。如上所述,UnicodeString 不能转换为 PAnsiChar。因此,对于所有 Delphi 版本,您需要将 s 变量从 String 更改为 UTF8String

procedure TPlugin.SetException(const msg: string);
var
  s: UTF8String;
begin
  try
    {$ifopt D+} NPP_DebugOut('Exception: ' + msg); {$endif}

    s:=UTF8Encode(msg);
    {
    UTF8Encode() is deprecated in Delphi 2009+.
    In those versions, you can use this instead:
    s := UTF8String(msg);
    }

    NPN_SetException(m_pScriptableObject, PAnsiChar(s));
  except
    { prevent any exception from leaking out of DLL }
  end;
end;

话虽如此,如果您在 NPN_SetException() 调用中仍然遇到同样的错误,则意味着 NPN_SetException() 的第二个参数被声明为 PChar。它需要声明为 PAnsiChar 而不是。