The ModuleHook Interface 1.1
The ModuleHook Interface is really nothing more than a !bangcommand with a special
syntax. And it is relatively easy to add to existing code, depending on how many of
its features you want to use.
!CallMeWhatYouWant <Userdata[0..N]> <future options> <Path\to\BoxFile.box> <current Box HWND>
I suggest that you use <Modulename>LsBoxHook as BangCommand name, but that's up to you.
Everything but the userdata string is generated by lsbox.
The <Userdata> strings are optional - they are only needed if you want to pass additional
options from the *ModuleHook line in the box file. Label needs this to decide what Label should
be loaded/docked to the Box.
The <Path\to\BoxFile.box> string contains the full expanded path to the .box file. You
can use this together with LiteStep's LoadRC LSAPI to load options from the box file.
RabidVWM is currently the only Module to do this. As of 1.96 the String is countained within Quotes.
The <current Box HWND> argument is the Box's HWND converted to string. I had to pass it as
a string to prevent NULL from closing a string prematurely.
It is important that you evaluate the options that are created by LsBox END of the
string!!!!
I have <future options> planned that will break your module's ModuleHook support if
you evaluate from the front!
Don't evaluate the Userdata string from the end for the same reason.
Sample Code
LsSlider
This Code is a bit different from the others - It's Delphi and my own. First is the
code for the Bang command itself:
procedure slider_hook(Caller:Integer;Args:pchar); cdecl;
Var
boxwnd: Hwnd;
begin
boxwnd := Str2Int(StrRScan(Args,#32));
Create_Slider(boxwnd,'*hooked '+Args);
end;
Not Much to see here. The !hook code is integrated in the Main initalizing procedure:
(....)
With Slider^ Do
begin
if (ParentWnd=0) then begin
hMainWnd := CreateWindowExA(
WS_EX_TOOLWINDOW,'LsSliderClass',nil,WS_POPUP,
xPos, yPos, Width, Height,0,0,Inst,nil);
isDocked := false;
end else
hMainWnd := CreateWindowExA(
0,'LsSliderClass',PChar(Caption),WS_CHILD,
xPos, yPos, Width, Height,ParentWnd,0,
Inst,nil);
If hMainWnd=0 then Messageboxa(0,'error creating main window',nil,0);
If (Boolean(CheckFlag(Flags,'A'))and(ParentWnd = 0)) then
SetWindowPos(hMainWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE+SWP_NOSIZE)
else
SetWindowPos(hMainWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE+SWP_NOMOVE);
(....)
And finally the Unhooking handeling
(....)
WM_DESTROY:
if Slider^.isDocked then
begin
SetWindowLong( Slider^.hMainWnd, GWL_STYLE, WS_CHILD);
SetParent( Slider^.hMainWnd, 0);
SetWindowLong( Slider^.hMainWnd, GWL_STYLE, WS_POPUP);
end;
(....)
Tasks
This is the code i used for hooking Tasks. Since Tasks already had a DockToWindow
option patching it for LsBox was easy - I just had to look at TasksDockToWindow and
add my own bang for the Hook interface.
void TasksBoxHook(HWND caller, LPCTSTR szArgs)
{
UNREFERENCED_PARAMETER(caller);
char *handle = strrchr(szArgs,' ');
if (handle)
{
HWND hWnd = (HWND)atoi(handle+1);
if (hWnd)
{
if (hWnd != ts.DockWindow){
for (int i=0;i<numTasks;i++){
SetWindowLong(tasks[i].hwnd, GWL_STYLE, (GetWindowLong(tasks[i].hwnd, GWL_STYLE) &~ WS_POPUP)|WS_CHILD);
SetParent(tasks[i].hwnd, hWnd);
//This is a Bad idea under Win2k SP2 - don't ask me why.....
//SetWindowLong(tasks[i].hwnd, GWL_STYLE, (GetWindowLong(tasks[i].hwnd, GWL_STYLE) &~ WS_CHILD)|WS_POPUP);
}
ts.DockWindow = hWnd;
}
}
}
return;
}
Well and that's really everything that is needed here - the way Tasks is build so that
it doesn't have to handle the WM_DESTROY case.....
LsXcommand
The Bang really isn't any different from the Task HookBang - it just handles a second
hidden state so the LsXcommand window only shows up when it is docked to a Box.
void BangBoxHook(HWND caller, const char *args)
{
char *handle = strrchr(args,' ');
if (handle)
{
HWND boxwnd = (HWND)atoi(handle+1);
if (boxwnd)
{
lsboxed = TRUE;
if (boxwnd != GetParent(hWnd))
{
SetWindowLong(hWnd, GWL_STYLE, (GetWindowLong(hWnd, GWL_STYLE) &~ WS_POPUP)|WS_CHILD);
SetParent(hWnd, boxwnd);
//This is a Bad idea under Win2k SP2 - don't ask me why.....
// SetWindowLong(hWnd, GWL_STYLE, (GetWindowLong(hWnd, GWL_STYLE) &~ WS_CHILD)|WS_POPUP);
if (cs->WaitForBox && visible)
{
ShowWindow(hWnd, SW_SHOWNORMAL);
cs->WaitForBox = FALSE;
}
}
}
}
return;
}
Since LsBox will send a WM_DESTROY message to it's Child Windows once a box
is destroyed we have to handle the WM_DESTROY Message so LsXcommmand isn't
actually destroyed.
case WM_DESTROY:
if (lsboxed)
{
cs->WaitForBox = TRUE;
visible = TRUE;
SetWindowLong(hWnd, GWL_STYLE, (GetWindowLong(hWnd, GWL_STYLE) &~ WS_CHILD)|WS_POPUP);
SetParent(hWnd, 0);
SetWindowPos(hWnd,HWND_TOP,cs->x,cs->y,0,0,SWP_NOSIZE|SWP_HIDEWINDOW);
lsboxed = FALSE;
return 0;
}
Label
Label was the most difficult of the three Modules to patch since it actually
used more than one window. AND it is written useing OOP. But aside from that
the only difference between the Label and the LsXcommand code is that it actually
uses an Userdata string to select the right Label
void LsBoxHookBangCommand(HWND caller, const char *arguments)
{
char labelName[MAX_LINE_LENGTH];
LPCTSTR nextToken[MAX_LINE_LENGTH];
GetToken(arguments,labelName,nextToken,false);
//check if the label is already running
Label *label = lookupLabel(labelName);
if (label == 0)
{
label = new Label(labelName);
label->load(hInstance);
labelList.insert(labelList.end(), label);
}
char *handle = strrchr(arguments,' ');
if ((label != 0)&&(handle))
{
HWND boxWnd = (HWND)atoi(handle+1);
if (boxWnd)
{
label->setBox(boxWnd);
label->show();
label->update();
}
}
}
The WM_DESTROY case has to be handld, too.
case WM_DESTROY:
{
if(box)
{
//hide or destroy is the Question - i take hide - blkhawk
this->hide();
this->setBox(0);
//Uncomment these to destroy the Label when the parent box gets killed
/*hWnd = 0;
labelList.remove(this);
delete this;*/
}
return false;
}