[Development] External WLX plugins loader

Discuss and announce Total Commander plugins, addons and other useful tools here, both their usage and their development.

Moderators: Hacker, petermad, Stefan2, white

User avatar
Alextp
Power Member
Power Member
Posts: 2321
Joined: 2004-08-16, 22:35 UTC
Location: Russian Federation
Contact:

[Development] External WLX plugins loader

Post by *Alextp »

I need some help: How to load/unload WLX plugin in Delphi application?
Can anybody give me Delphi example, that will: on button press load a WLX plugin (e.g. IEView or Imagine) and show it on form; on second button press unload plugin and clear form.

I tried to code something, but without good result (unloading causes Exception). So working example is needed...
Last edited by Alextp on 2006-06-28, 09:05 UTC, edited 1 time in total.
poiuytr
Senior Member
Senior Member
Posts: 243
Joined: 2003-02-23, 17:33 UTC

Post by *poiuytr »

Loading and unloading could be done with pure winapi, no delphi specific is envolved.
I just try a little example and it works ok with most of the WLX plugins.
There was some software exceptions with SynPlus, but FileInfo, Imagine works fine.
Here is C example
Load

Code: Select all

HMODULE hLib = LoadLibrary("fileinfo.wlx");
if (!FLib)
  return;
typedef void __stdcall (*TListSetDefaultParams)(ListDefaultParamStruct* dps);

// according to docs ListSetDefaultParams is called before ListLoad 
TListSetDefaultParams ListSetDefaultParams = (TListSetDefaultParams)GetProcAddress(hLib , "ListSetDefaultParams");
if (ListSetDefaultParams)
{
  ListDefaultParamStruct s;
  ZeroMemory(&s, sizeof(ListDefaultParamStruct));
  s.PluginInterfaceVersionLow = 1; // I don't know exact number, so it is my guess
  s.PluginInterfaceVersionHi = 1;
  strcpy(s.DefaultIniName, "lsplugin.ini");
  s.size = sizeof(s);
  ListSetDefaultParams(&s);
}

typedef HWND __stdcall (*TListLoad)(HWND ParentWin,char* FileToLoad,int ShowFlags);
TListLoad ListLoad = (TListLoad)GetProcAddress(hLib, "ListLoad");
if (!ListLoad)
  return;

HWND hWlx = ListLoad(hParent, "some_file", 0); // hParent is HWND of the parent (Lister) window
Unload

Code: Select all

typedef void __stdcall (*TListCloseWindow)(HWND ListWin);

TListCloseWindow ListCloseWindow =
            (TListCloseWindow)GetProcAddress(hLib, "ListCloseWindow");
if (ListCloseWindow)
  ListCloseWindow(hWlx); // hWlx is a HWND, returned by plugin
else
{
  // if plugin did not export ListCloseWindow, then, according to docs, TC will use DestroyWindow
  DestroyWindow(hWlx);
}
Hope this snippet will be helpfull.
I can share an example project, it is written with Borland C++ Builder 2006.
User avatar
Alextp
Power Member
Power Member
Posts: 2321
Joined: 2004-08-16, 22:35 UTC
Location: Russian Federation
Contact:

Post by *Alextp »

2poiuytr
Please share a project (compiled!) that can load/unload Imagine for some picture.
I have problems with Imagine.

Plugins that work:
IEView, OOView, TTFViewer

Problems:
- Imagine: doesn't show (form remains empty)
- ICLView: doen't occupy whole window, only small part of it
- LinkInfo: External Exception in my app, but plugin shows

My code, it's almost as yours:

Code: Select all

var
  WlxHLib: THandle = 0;
  WlxHWnd: THandle = 0;

procedure WlxFree;
var
  Proc2: TListCloseWindow;
begin
  if WlxHLib=0 then Exit;

  if WlxHWnd<>0 then
    begin
    @Proc2:= GetProcAddress(WlxHLib, 'ListCloseWindow');
    if @Proc2<>nil then Proc2(WlxHWnd) else DestroyWindow(WlxHWnd);
    end;

  Sleep(100);
  FreeLibrary(WlxHLib);

  WlxHLib:= 0;
  WlxHWnd:= 0;
end;

procedure WlxLoad(const fPlugin, fFile: string; ParentWnd: THandle; const DefIni: string);
var
  hLib: THandle absolute WlxHLib;
  hWnd: THandle absolute WlxHWnd;
  Proc0: TListGetDetectString;
  Proc1: TListLoad;
  Proc3: TListSetDefaultParams;
  dps: TListDefaultParamStruct;
  buf1: array[0..8*1024] of char;
begin
  WlxFree;

  hLib:= LoadLibrary(PChar(fPlugin));
  if hLib=0 then Exit;

  @Proc0:= GetProcAddress(hLib, 'ListGetDetectString');
  if @Proc0<>nil then
    begin
    FillChar(buf1, SizeOf(buf1), 0);
    Proc0(buf1, SizeOf(buf1));
    end;

  @Proc3:= GetProcAddress(hLib, 'ListSetDefaultParams');
  if @Proc3<>nil then
    begin
    FillChar(dps, SizeOf(dps), 0);
    dps.Size:= SizeOf(dps);
    dps.PluginInterfaceVersionLow:= 30;
    dps.PluginInterfaceVersionHi:= 1;
    lstrcpy(dps.DefaultIniName, PChar(DefIni));
    Proc3(@dps);
    end;

  @Proc1:= GetProcAddress(hLib, 'ListLoad');
  if @Proc1=nil then begin FreeLibrary(hLib); hLib:= 0; Exit end;

  try
    hWnd:= Proc1(ParentWnd, PChar(fFile), lcp_ansi or lcp_forceshow);
    if hWnd=0 then
      ShowMessage('Failed to load')
    else
      ShowMessage('Loaded');
  finally
  end;
end;
poiuytr
Senior Member
Senior Member
Posts: 243
Joined: 2003-02-23, 17:33 UTC

Post by *poiuytr »

Alextp wrote:2poiuytr
Please share a project (compiled!) that can load/unload Imagine for some picture.
Check this out. Sources with compiled exe inside.
Problems:
- Imagine: doesn't show (form remains empty)
- ICLView: doen't occupy whole window, only small part of it
IMHO Lister sets size of the plugin's window by itself, didn't you forget to do this in your project (set initial size and update it on resize event)? I think this will solve your problem with Imagine.
- LinkInfo: External Exception in my app, but plugin shows
I have the same experience with my project and with Lister itself.
My code, it's almost as yours
Yep, main line is the same.
User avatar
Alextp
Power Member
Power Member
Posts: 2321
Joined: 2004-08-16, 22:35 UTC
Location: Russian Federation
Contact:

Post by *Alextp »

IMHO Lister sets size of the plugin's window by itself, didn't you forget to do this in your project (set initial size and update it on resize event)? I think this will solve your problem with Imagine.
How to do it, is it in your source? I'll see.
poiuytr
Senior Member
Senior Member
Posts: 243
Joined: 2003-02-23, 17:33 UTC

Post by *poiuytr »

Code: Select all

  SetWindowPos(WlxHwnd, // HWND, returned by plugin
    HWND_TOP,
    0,
    0,
    Panel1->ClientWidth,
    Panel1->ClientHeight,
    NULL);
In my case I want a plugin to be stretched over Panel1.
User avatar
Alextp
Power Member
Power Member
Posts: 2321
Joined: 2004-08-16, 22:35 UTC
Location: Russian Federation
Contact:

Post by *Alextp »

2poiuytr
Thanks, now all works!

1 problem: Imagine doesn't show its toolbar, even when I check this in options.
poiuytr
Senior Member
Senior Member
Posts: 243
Joined: 2003-02-23, 17:33 UTC

Post by *poiuytr »

Alextp wrote:Thanks, now all works!
Nice to hear!
Imagine doesn't show its toolbar, even when I check this in options.
May be Imagine detects that it is launched in the quick view mode (Imagine will hide button bar in that mode). One technique to determinate QuickVeiwMode is to perform a FindWindow("TLister", 0) and check returned handle. Try to play with your parent class name (make it 'TLister').

Your project have to be as much close to Lister as possible to use most of the wlx plugins without problems.
It will be hard to adopt plugin for your project when plugin author used some hacks (e.g. messing with Lister's main menu).

Poor wlx api is the cause of this (CG, please, do something!).
User avatar
Alextp
Power Member
Power Member
Posts: 2321
Joined: 2004-08-16, 22:35 UTC
Location: Russian Federation
Contact:

Post by *Alextp »

One technique to determinate QuickVeiwMode is to perform a FindWindow("TLister", 0) and check returned handle. Try to play with your parent class name (make it 'TLister').
Class name of TForm descendant or of TPanel on which plugin is shown? And how to change class name in both cases?

Tried to change form type name to TLister, no effect.
poiuytr
Senior Member
Senior Member
Posts: 243
Joined: 2003-02-23, 17:33 UTC

Post by *poiuytr »

Alextp wrote:Class name of TForm descendant or of TPanel on which plugin is shown?
I tried both, but only changing class name of the main form works.
Ok, here is what I did:
* changed name of the main form to 'Lister' (class name of the main form became 'TLister');
* called ListLoad with HWND of the main form;
* resized plugin window over main form;
* yey, Imagine toolbar is visible :D

Check the sources.
And how to change class name in both cases?
To change Panel class name just create the descendant of the TPanel with class name TLister.
To change Form class name just name your main form as 'Lister' and you will see that its class name will automatically change to 'TLister' (at least in Builder I can do this, hopefully it is the same thing with Delphi or you can use the same trick as with Panel).
User avatar
Alextp
Power Member
Power Member
Posts: 2321
Joined: 2004-08-16, 22:35 UTC
Location: Russian Federation
Contact:

Post by *Alextp »

tried both, but only changing class name of the main form works.
Ok, here is what I did:
Confirmed...
But any way to display it on TPanel? I tried this:

Code: Select all

type
  TLister = class(TPanel)
  end;
and change type of Panel1 in .pas/.dfm to TLister. No effect.
Maybe, Imagine looks only for normal windows, not for child windows/controls?
User avatar
Alextp
Power Member
Power Member
Posts: 2321
Joined: 2004-08-16, 22:35 UTC
Location: Russian Federation
Contact:

Post by *Alextp »

poiuytr
Senior Member
Senior Member
Posts: 243
Joined: 2003-02-23, 17:33 UTC

Post by *poiuytr »

Alextp wrote:But any way to display it on TPanel?
Of course! You can easily place plugin window over Panel without making this Panel parent of the this window by using SetWindowPos(.. HWND_TOP ..).
Maybe, Imagine looks only for normal windows, not for child windows/controls?
True if Imagine use FindWindow function (according to msdn FindWindow retrieves the handle to the top-level windows only).
Good work!
User avatar
Alextp
Power Member
Power Member
Posts: 2321
Joined: 2004-08-16, 22:35 UTC
Location: Russian Federation
Contact:

Post by *Alextp »

Of course! You can easily place plugin window over Panel without making this Panel parent of the this window by using SetWindowPos(.. HWND_TOP ..).
This will be odd... Window caption will over panel, window may be resized indep. of panel and etc...
Not nice.
poiuytr
Senior Member
Senior Member
Posts: 243
Joined: 2003-02-23, 17:33 UTC

Post by *poiuytr »

Alextp wrote:
Of course! You can easily place plugin window over Panel without making this Panel parent of the this window by using SetWindowPos(.. HWND_TOP ..).
This will be odd... Window caption will over panel, window may be resized indep. of panel and etc...
Not nice.
Plugin's window doesn't have any caption, it can not be resized by mouse or smth.. At least those plugins I tried (FileInfo, Imagine, IEView, ICLView..).
Post Reply