Search code examples
delphinpapidelphi-xe7browser-plugin

NPAPI plugin framework Error


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;

Solution

  • 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.