NPAPI plugin framework Error
Asked Answered
S

2

-1

I am trying to use NPAPI Framework from Yury Sidorov by following this answer:

How to embed Delphi VCL form into HTML page using NPAPI

but I get an error with NPPlugin.pas. I am using delphi XE7 and here what i did by following Krom Stern instructions

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

but still getting error with compiler with this message

[dcc32 Error] NPPlugin.pas(2215): E2010 Incompatible types: 'PWideChar' and 'PAnsiChar'

Error raised on this procedure

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;

here is the procedure NPN_SetException

procedure NPN_SetException(npobj: PNPObject; msg: PNPUTF8);
begin
  NavigatorFuncs.SetException(npobj, msg);
end;
Sarchet answered 15/3, 2015 at 14:30 Comment(0)
B
1

This NPAPI code was clearly designed for older versions of Delphi before the switch to Unicode in Delphi 2009. The default String/(P)Char types are no longer aliases for AnsiString/(P)AnsiChar, they are now aliases for UnicodeString/(P)WideChar. A UnicodeString cannot be casted to a PAnsiChar, just like an AnsiString could never be casted to a PWideChar.

In DefDebugOut(), the simplest fix is to change PAnsiChar to PChar and change OutputDebugStringA() to OutputDebugString():

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

This is compatible with all Delphi versions (the code should have been doing this from the beginning - there was no reason to call OutputDebugStringA() directly). PChar and OutputDebugString() map to PAnsiChar and OutputDebugStringA() in Delphi 2007 and earlier, and to PWideChar and OutputDebugStringW() in Delphi 2009 and later. So everything matches.

In TPlugin.SetException(), UTF8Encode() returns a UTF8String in all versions of Delphi. However, prior to Delphi 2009, UTF8String was just an alias for AnsiString itself, but in Delphi 2009 it was changed to a true UTF-8 string type with full RTL support (it still has an AnsiString base, so it can still be casted to PAnsiChar). When a UTF8String is assigned to a UnicodeString, the compiler performs an implicit data conversion from UTF-8 to UTF-16. And as stated above, UnicodeString cannot be casted to PAnsiChar. So you need to change the s variable from String to UTF8String for all Delphi versions:

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;

With that said, if you are still getting the same error on the NPN_SetException() call, then it means the second parameter of NPN_SetException() is declared as PChar. It needs to be declared as PAnsiChar instead.

Bitterroot answered 15/3, 2015 at 16:28 Comment(2)
True, but that wouldn't explain the compiler error that was reported. DefDebugOut compiles, although it is clearly wrong.Aweinspiring
The edits are better, but you are still missing the root problem. My answer explains what that is. In short, NPN_SetException is declared incorrectly to accept PChar. That's the only explanation for the compilation error.Aweinspiring
A
2

I'll start with a piece by piece breakdown of what we can see. Bear in mind that we don't have NPPlugin.pas at hand and have to infer its contents from the information in the question. All the same, I think it's possible for us to do that accurately.


s := UTF8Encode(msg);

Here s is of type string. That's an alias for UnicodeString, encoded as UTF-16. So you convert from UTF-16 to UTF-8 and then back to UTF16.

You need it like this:

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

Alternatively, if you need a variable to hold UTF-8 encoded text, declare it to be UTF8String which is AnsiString(65001). If you changed the type of s to be UTF8String then the code in the question would be correct. Although somewhat more verbose than it would need to be.


Another problem is here:

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

Your cast doesn't make Msg actually be 8 bit encoded. However, you don't want to use the ANSI version of the function. You need this:

OutputDebugString(PChar(Msg + sLineBreak));

Your exception handler is misguided. It is the DLL's job not to leak exceptions. If you attempt to catch and suppress them all you will simply mask errors in your own code. You need to remove that exception handler and check for errors by following the instructions given by the library documentation.


Now to the bigger picture. None of the above explains your reported error. The only sound explanation for that is that your declaration for NPN_SetException accepts wide text. In which case you could make the code compile simply by writing this:

NPN_SetException(m_pScriptableObject, PChar(msg));

Of course, that makes the appearance of UTF-8 somewhat inexplicable. In fact the library Mozilla does accept 8 bit text, UTF-8 encoded. So why would NPN_SetException expect to be passed UTF-16 text? Well it doesn't. The explanation is that you have declared NPN_SetException incorrectly. So, just to be clear, whilst PChar(msg) would make your code compile, it would not resolve your problem. You would be left with code that failed at runtime.

So, how did this happen? You've taken a working piece of code that used PChar aliased to PAnsiChar onto a Delphi with PChar aliased to PWideChar and not translated correctly. Even when you get your code to compile, it will not work correctly. You started with code like this:

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

On older Delphi versions where PChar was PAnsiChar then this was correct. You are compiling this now on XE7 where PChar is PWideChar and so this is not correct. It needs to be:

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

Then the calling code can be:

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

My advice is that you:

  1. Step back and revisit the handling of Unicode in Delphi.
  2. Go back to you original code and change all the Mozilla interface code that uses PChar to PAnsiChar.
  3. Whenever you need to provide PAnsiChar do it with PAnsiChar(UTF8Encode(str)).
Aweinspiring answered 15/3, 2015 at 16:26 Comment(4)
NPN_SetException() accepts only UTF-8 as input.Bitterroot
@Remy Indeed. That's my point. Which is why the asker is wrong to declare the function as accepting PWideChar. That's the key point that you have not yet realised. That it is so can be seen from the error message.Aweinspiring
mozdev.org/bugs/attachment.cgi?id=7590 here is the actual pluginSarchet
I don't think I need to see the plugin. I was able to diagnose the problem without it. I see you accepted Remy's answer even though I told him what the problem was, and he then edited that in with that final paragraph!! Perhaps my answer was badly written and I didn't get the message across clearly.Aweinspiring
B
1

This NPAPI code was clearly designed for older versions of Delphi before the switch to Unicode in Delphi 2009. The default String/(P)Char types are no longer aliases for AnsiString/(P)AnsiChar, they are now aliases for UnicodeString/(P)WideChar. A UnicodeString cannot be casted to a PAnsiChar, just like an AnsiString could never be casted to a PWideChar.

In DefDebugOut(), the simplest fix is to change PAnsiChar to PChar and change OutputDebugStringA() to OutputDebugString():

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

This is compatible with all Delphi versions (the code should have been doing this from the beginning - there was no reason to call OutputDebugStringA() directly). PChar and OutputDebugString() map to PAnsiChar and OutputDebugStringA() in Delphi 2007 and earlier, and to PWideChar and OutputDebugStringW() in Delphi 2009 and later. So everything matches.

In TPlugin.SetException(), UTF8Encode() returns a UTF8String in all versions of Delphi. However, prior to Delphi 2009, UTF8String was just an alias for AnsiString itself, but in Delphi 2009 it was changed to a true UTF-8 string type with full RTL support (it still has an AnsiString base, so it can still be casted to PAnsiChar). When a UTF8String is assigned to a UnicodeString, the compiler performs an implicit data conversion from UTF-8 to UTF-16. And as stated above, UnicodeString cannot be casted to PAnsiChar. So you need to change the s variable from String to UTF8String for all Delphi versions:

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;

With that said, if you are still getting the same error on the NPN_SetException() call, then it means the second parameter of NPN_SetException() is declared as PChar. It needs to be declared as PAnsiChar instead.

Bitterroot answered 15/3, 2015 at 16:28 Comment(2)
True, but that wouldn't explain the compiler error that was reported. DefDebugOut compiles, although it is clearly wrong.Aweinspiring
The edits are better, but you are still missing the root problem. My answer explains what that is. In short, NPN_SetException is declared incorrectly to accept PChar. That's the only explanation for the compilation error.Aweinspiring

© 2022 - 2024 — McMap. All rights reserved.