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;
	}