GP SoftwareTwitter
Opus FAQsManualCommandsObjects

Please help test this plugin

plugin-development

#1

@leo @jon
Please help test this plugin for memory leaks or cause dopus crashes, thank you!

DOpusViewerPluginSample.zip (837.1 KB)

library DOpusViewerPluginSample;

{ Important note about DLL memory management: ShareMem must be the
  first unit in your library's USES clause AND your project's (select
  Project-View Source) USES clause if your DLL exports any procedures or
  functions that pass strings as parameters or function results. This
  applies to all strings passed to and from your DLL--even those that
  are nested in records and classes. ShareMem is the interface unit to
  the BORLNDMM.DLL shared memory manager, which must be deployed along
  with your DLL. To avoid using BORLNDMM.DLL, pass string information
  using PChar or ShortString parameters. }

{$IF CompilerVersion >= 21.0}
{$WEAKLINKRTTI ON}
{$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
{$ENDIF}

{.$DEFINE USERDEFINE}
{$IFDEF  USERDEFINE}
  {$DEFINE INIT}
  {$DEFINE INITEX}
  {$DEFINE USBSAFE}
  {$DEFINE UNINIT}
  {$DEFINE IDENTIFY}
  {$DEFINE IDENTIFYFILE}
  {$DEFINE IDENTIFYFILESTREAM}
  {$DEFINE IDENTIFYFILEBYTES}
  {$DEFINE LOADBITMAP}
  {$DEFINE LOADBITMAPSTREAM}
  {$DEFINE LOADTEXT}
  {$DEFINE SHOWPROPERTIES}
  {$DEFINE SHOWPROPERTIESSTREAM}
  {$DEFINE CREATEVIEWER}
  {$DEFINE CONFIGURE}
  {$DEFINE ABOUT}
  {$DEFINE GETFILEINFOFILE}
  {$DEFINE GETFILEINFOFILESTREAM}
  {$DEFINE SETMETADATA}
  {$DEFINE VIEWERCACHELOAD}
  {$DEFINE VIEWERCACHEFREE}
{$ENDIF}

{.$R *.res}
{$R *.dres}

uses
  System.SysUtils,
  System.Classes,
  Winapi.Windows,
  Winapi.Messages,
  Vcl.Forms,
  Vcl.Controls,
  Vcl.StdCtrls,
  Vcl.ComCtrls,
  DOpus.ViewerPlugin in 'DOpus.ViewerPlugin.pas',
  Unit1 in 'Unit1.pas' {Form1};

{.$IF VIEWERPLUGINVERSION < 5 AND Defined(USBSAFE)}
{.$R USBSafe.res}
{.$ENDIF}
const
  DVWClassName: PChar = 'DOpus.ViewerPlugin';

function DVP_Load(Filename: string): BOOL;
begin
  Form1.edt1.Text := Filename;
  Result := True;
end;

procedure ReleaseResources;
begin
  UnregisterClass(DVWClassName, HInstance);
  Form1.Close;
  FreeAndNil(Form1);
  PostQuitMessage(0);
end;

function WindowProc(hWnd,Msg:Longint; wParam : WPARAM; lParam: LPARAM):Longint; stdcall;
begin
  case Msg of
      DVPLUGINMSG_LOAD : Result := DVP_Load(PChar(lParam)).ToInteger;

      WM_DESTROY: ReleaseResources;
  else
      Result := DefWindowProc(hWnd,Msg,wParam,lParam);
  end;
end;

function RegisterWindowClass(hDllModule: HMODULE): ATOM;
var
  wc : WNDCLASS;
begin
  // Register window class
  wc.style := 0;
  wc.lpfnWndProc := @WindowProc;
  wc.cbClsExtra := 0;
  wc.cbWndExtra := 0;
  wc.hInstance := hDllModule;
  wc.hIcon := 0;
  wc.hCursor := 0;
  wc.hbrBackground := COLOR_WINDOW + 1;
  wc.lpszMenuName := 0;
  wc.lpszClassName := DVWClassName;
  Result := (RegisterClass(&wc));
end;


function UnregisterWindowClass(hDllModule : HMODULE):Boolean;
begin
  Result := (UnregisterClass(DVWClassName, hDllModule));
end;

function CreateViewerWindow(hDllModule: HMODULE; hWndParent: HWND; lpRc : TRECT; dwFlags : DWORD): HWND;
type
NVC_CreateParams = record
  lpRc             : PRECT;
  dwFlags          : DWORD;
end;

var
  wrc       : TRECT;
  cp        : NVC_CreateParams;
  dwExStyle : DWORD;
begin
  GetClientRect(hWndParent, &wrc);
  cp.lpRc    := @lpRc;
  cp.dwFlags := dwFlags;
  dwExStyle  := 0;        //WS_EX_NOPARENTNOTIFY;

//  if (dwExStyle or (dwFlags and DVPCVF_Border)) <> 0 then
//    dwFlags := WS_EX_CLIENTEDGE
//  else
//    dwFlags := 0;

  RegisterWindowClass(hDllModule);
  Result := CreateWindowEx(
                dwExStyle,
                DVWClassName,
                nil,
                WS_CHILD or WS_VISIBLE or WS_CLIPCHILDREN or WS_CLIPSIBLINGS,
                lpRc.left, lpRc.top, lpRc.right - lpRc.left + 1, lpRc.bottom - lpRc.top + 1,
                hWndParent,
                0,
                hDllModule,
                @cp);
//  MessageBoxW(0, PChar('Result: '#13#10'hWndParent: ' + IntToStr(hWndParent) + #13#10'HInstance: ' + IntToStr(HInstance)), 'CreateViewerWindow', MB_OK);
  if not Assigned(Form1) then
    Form1 := TForm1.CreateParented(Result);
//  Form1.Parent := FindControl(Result);
//  Form1.SetBounds(lpRc.Left, lpRc.Top, lpRc.Width, lpRc.Height);
  if not Form1.Showing then
    Form1.Show;
end;


const
  //press Ctrl + Shift + G get new PluginGUID
  Plugin_GUID          : TGUID     = '{1362C93C-4F72-4AA6-AE1B-DD325B95922E}';
  Plugin_VersionHigh   : DWORD     = $00000000;
  Plugin_VersionLow    : DWORD     = $00000001;

  Plugin_HandleExts    : PChar     = '*.*';  // separated by ";", example: '.txt;.html'
  Plugin_Name          : PChar     = 'Plugin Sample';
  Plugin_Description   : PChar     = 'DOpus Viewer Plugin Sample';
  Plugin_Copyright     : PChar     = 'Copyright (C)';
  Plugin_URL           : PChar     = '';

  Plugin_MinFileSize   : DWORDLONG = 0;
  Plugin_MaxFileSize   : DWORDLONG = $01400000; // 20971520(2Mib);
  Plugin_MajorFileType : Word      = DVPMajorType_Other;


function MAKE64BITVERSIONNUMBER(a,b,c,d: DWORD): DWORD64;
begin
  Result := ((DWORD64(a)) shl 48) + ((DWORD64(b)) shl 32) + ((DWORD64(c)) shl 16) + ((DWORD64(d)) shl 00);
end;


//{.$IF VIEWERPLUGINVERSION < 5 AND Defined(USBSAFE)}
//function DVP_USBSafe(pUSBSafeData: PUSBSafeData): BOOL; cdecl;
//begin
//  Result := True;
//end;
//{.$ENDIF}

{.$IFDEF UNINIT}
procedure DVP_Uninit; cdecl;
begin

end;
{.$ENDIF}

function DVP_Init: BOOL; cdecl;
begin
  Result := False;
end;

{.$IFDEF INITEX}
function DVP_InitEx(pInitExData: PDVPInitExData): BOOL; cdecl;
begin

end;
{.$ENDIF}

function DVP_Identify(InitInfo: PViewerPluginInfo): BOOL; cdecl;
begin
  Result := False;

  InitInfo^.dwFlags               := DVPFIF_ExtensionsOnly or DVPFIF_InitialDisable;

  InitInfo^.dwVersionHigh         := Plugin_VersionHigh;
  InitInfo^.dwVersionLow          := Plugin_VersionLow;

  InitInfo^.lpszHandleExts        := Plugin_HandleExts;
  InitInfo^.lpszName              := Plugin_Name;
  InitInfo^.lpszDescription       := Plugin_Description;
  InitInfo^.lpszCopyright         := Plugin_Copyright;
  InitInfo^.lpszURL               := Plugin_URL;

  InitInfo^.dwlMinFileSize        := Plugin_MinFileSize;
  InitInfo^.dwlMaxFileSize        := Plugin_MaxFileSize;
  InitInfo^.dwlMinPreviewFileSize := Plugin_MinFileSize;
  InitInfo^.dwlMaxPreviewFileSize := Plugin_MaxFileSize;
  InitInfo^.uiMajorFileType       := Plugin_MajorFileType;   //DVPMajorType_Other;
  InitInfo^.idPlugin              := Plugin_GUID;

  Result:= True;
end;

function DVP_IdentifyFile(hWnd: HWND; lpszName: LPSTR; lpVPFileInfo: PViewerPluginFileInfo; hAbortEvent: THANDLE): BOOL; cdecl;
begin
  Result := False;
  if not Result then
  begin
    lpVPFileInfo^.dwFlags        := DVPFIF_CanReturnViewer;
    lpVPFileInfo^.wMajorType     := Plugin_MajorFileType;
    lpVPFileInfo^.wMinorType     := 0;
    lpVPFileInfo^.szImageSize.cx := 0;
    lpVPFileInfo^.szImageSize.cy := 0;
    lpVPFileInfo^.iNumBits       := 0;
    Result := True;
  end;
end;

function DVP_CreateViewer(hWndParent: HWND; lpRc: TRect; dwFlags: DWORD): HWND; cdecl;

begin
//  dwFlags := DVPCVF_Preview;
//  TDVPViewer.CreateParented(hWndParent);
//  SetWindowLong(TDVPViewer.Handle, GWL_STYLE, GetWindowLong(TDVPViewer.Handle, GWL_STYLE) or WS_CHILDWINDOW);
//  DVPViewer.Parent := FindControl(hWndParent);
  Result := CreateViewerWindow(HInstance, hWndParent, lpRc, dwFlags);
end;

//--------------------------------------------------------------------//

//  exports DVP_InitEx       name DVPFUNCNAME_INITEX;
//  exports DVP_Uninit       name DVPFUNCNAME_UNINIT;
  exports DVP_Identify     name DVPFUNCNAME_IDENTIFY;
  exports DVP_CreateViewer name DVPFUNCNAME_CREATEVIEWER;
  exports DVP_IdentifyFile name DVPFUNCNAME_IDENTIFYFILE;
//  exports DVP_Configure    name DVPFUNCNAME_CONFIGURE;

begin
  ReportMemoryLeaksOnShutdown := True;

end.

#2

(We kept this version of the post since it was already here, and deleted the duplicate that was in the moderation queue.)

Quoting from the extra paragraph from the other version of this post:

I used Delphi to create a plugin, but the use of Delphi form would cause Dopus to crash and also generate a memory leak.

Using Delphi directly should work, since it's been used to write a few plugins. I think most were ANSI and 32-bit but one is Unicode and 64-bit, so it's definitely possible.

I refer to the Leo plug-in, use the win API to build a form, and then embed the Delphi form into the form created by the win API, I do not know if this method is right, but this is the only solution I can find now.

That should be fine. If it works, one extra window should not be a problem. But it will make things more complicated for you, since you will have to proxy a lot of messages sent to and from Opus via the intermediate window, and sometimes modify the messages on the way. The animated gif plugin has example code of how to do that, since it does the same thing with the window which hosts the toolbar control. (I probably would not write it the same way now, but when I started writing it the toolbar did not exist, so it was retrofitted.)

Doing it directly is probably best, but I don't know enough about Delphi to give much advice.

One thing to note is that you need to be exporting the W versions of the APIs which take strings (e.g. DVP_LoadFileW and not DVP_LoadFileA, nor DVP_LoadFile) and they need to take UTF-16 strings as arguments. I think Delphi may default to ANSI or UTF-8 strings, although I don't know for sure.

Your DLL looks like it is exporting the W functions now, but presumably via the C++ wrapper you've made. If the original Delphi-only code wasn't doing that, or was using the wrong type of strings, that might explain the crashes you saw.