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 版本中 PChar
是 PAnsiChar
那么这是正确的。您现在正在 XE7 上编译它,其中 PChar
是 PWideChar
,所以这是不正确的。必须是:
function NPN_SetException(..., Msg: PAnsiChar): ...;
那么调用代码可以是:
NPN_SetException(m_pScriptableObject, PAnsiChar(UTF8Encode(msg)));
我的建议是:
- 退后一步,重新审视 Delphi 中对 Unicode 的处理。
- 回到你原来的代码,把所有使用
PChar
的Mozilla界面代码改成PAnsiChar
。
- 每当您需要提供
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()
)。 PChar
和 OutputDebugString()
映射到 Delphi 2007 年及更早版本中的 PAnsiChar
和 OutputDebugStringA()
,以及 [=] 中的 PWideChar
和 OutputDebugStringW()
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
而不是。
我正在尝试按照以下答案使用 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 版本中 PChar
是 PAnsiChar
那么这是正确的。您现在正在 XE7 上编译它,其中 PChar
是 PWideChar
,所以这是不正确的。必须是:
function NPN_SetException(..., Msg: PAnsiChar): ...;
那么调用代码可以是:
NPN_SetException(m_pScriptableObject, PAnsiChar(UTF8Encode(msg)));
我的建议是:
- 退后一步,重新审视 Delphi 中对 Unicode 的处理。
- 回到你原来的代码,把所有使用
PChar
的Mozilla界面代码改成PAnsiChar
。 - 每当您需要提供
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()
)。 PChar
和 OutputDebugString()
映射到 Delphi 2007 年及更早版本中的 PAnsiChar
和 OutputDebugStringA()
,以及 [=] 中的 PWideChar
和 OutputDebugStringW()
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
而不是。