Shoutbox

[Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) - Printable Version

-Shoutbox (https://shoutbox.menthix.net)
+-- Forum: MsgHelp Archive (/forumdisplay.php?fid=58)
+--- Forum: Messenger Plus! for Live Messenger (/forumdisplay.php?fid=4)
+---- Forum: Scripting (/forumdisplay.php?fid=39)
+----- Thread: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) (/showthread.php?tid=82705)

[Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by phalanxii on 03-27-2008 at 12:05 PM

Subclassing is quite a useful way of monitoring the messages sent to the keyboard (such as key presses), mouse (such as right mouse clicks) and windows (such as minimizing).

Plus! can already do some subclassing; take for example matty's hotkey code and SpunkyLoveMuff's StatusKey script. These both involve an invisible Plus! window which monitors the keyboard for a specific hotkey.

However, sometimes Plus! windows are not enough. For example, (as far as I know) it is impossible to detect the changing of tabbed chats using only the Plus! scripting engine and the Win32 API. It is even more impossible to detect messages sent to external applications (like Winamp for example).

This is where the ActiveXObjects come in. By using these, we are able to do all these things, right in Plus! itself!

After doing some browsing, I came across two free subclassing ActiveX files.


1. ARSubclass

Link
Download

ARSubclass is a very lightweight ActiveX library. It allows you to monitor the messages sent to a specific window (given its handle). Firstly, the DLL is required to be registered when the script is imported, so this line must be added to ScriptInfo.xml:
code:
<OleFiles>
    <FileName>ARSubclass.dll</FileName>
</OleFiles>
Here is an example code of how to use it:
code:
var ARSubclass = new ActiveXObject("ARSubclassLib.ARSubclass");

function OnEvent_Initialize(MessengerStart) {
    ARSubclass.hWnd = Messenger.ContactListWndHandle;
    ARSubclass.Message(/* WM_ACTIVATE */ 6) = true;
}

function OnEvent_ContactListWndCreated() { OnEvent_Initialize(); }

var Event = function() {
    function ARSubclass::WindowProc(hWnd, iMessage, wParam, lParam, Res) {
        if(wParam === /* WA_INACTIVE */ 0) Debug.Trace("WA_INACTIVE");
        if(wParam === /* WA_ACTIVE */ 1) Debug.Trace("WA_ACTIVE");
        if(wParam === /* WA_CLICKACTIVE */ 2) Debug.Trace("WA_CLICKACTIVE");
    }
}

Event();
This will trace when the WM_ACTIVATE message is sent to the contact list and show whether it is activated, activated by click or deactivated. (Here, "activated" simply means that the window gets "focus".) When the contact list is created, the ActiveXObject takes its handle and is set to receive all WM_ACTIVATE messages. When it receives one of these messages, it is processed by the WindowProc function with wParam and lParam. (Cheers to Pai for the ActiveX event trick).

More information can be found on the website.


2. SubClassX5 and SubClassX6

Link
Download

SubClassX5 and SubClassX6 work identically except they are compiled with different versions of VisualBasic. They work very similar to ARSubclass, except they can deal with more than one window. This makes them especially useful for our tabbed chats scenario as previously mentioned. Unfortunately, the size is quite a bit larger than ARSubclass, but still useable nonetheless! Again, we must register the DLL with ScriptInfo.xml:
code:
<OleFiles>
    <FileName>SubClassX6.dll</FileName>
</OleFiles>
Here is an example code of how to use it:
code:
var SubClassX6 = new ActiveXObject("SubClassX6.Window");

function OnEvent_ChatWndCreated(ChatWnd) {
    SubClassX6.SubClass_Start(ChatWnd.Handle);
}

function OnEvent_ChatWndDestroyed(ChatWnd) {
    SubClassX6.SubClass_Stop(ChatWnd.Handle);
}

var Event = function() {
    function SubClassX6::Message(hWnd, Msg, wParam, lParam, Return_Value, Return_SetValue) {
        if(Msg == /* WM_ACTIVATE */ 6 && wParam != /* WA_INACTIVE */ 0) Debug.Trace(hWnd);
    }
}

Event();
This will trace the handle of a chat window when it is activated (set in focus). Notice that we are able to subclass more than one window simultaneously from the one ActiveXObject. Here, the callback function is Message.

A full documentation of all the functions and properties comes with the DLL.


Finally, I have an example of how this may be useful to scripters. I have attached a script which uses subclassing that will change the window icon (yes, in the toolbar too!) of tabbed chats to reflect the current contact's status. This should work with tabbed chats disabled too. (You may need to restart WLM first.) Credits to Veggie for his original StatusIcon script.

Hopefully this has been useful to some of you. All mistakes are entirely deliferate.

TabbedStatusIconV3 - Download

EDIT: Updated version of TabbedStatusIcon has been attached. This fixes some minor bugs.

EDIT: Updated again because I stuffed up the contact status change. It should all be fixed now!
RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by effection on 03-27-2008 at 04:45 PM

very very nice piece of work


RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by CookieRevised on 03-27-2008 at 07:25 PM

quote:
Originally posted by phalanxii
However, sometimes Plus! windows are not enough. For example, (as far as I know) it is impossible to detect the changing of tabbed chats using only the Plus! scripting engine and the Win32 API.
That's very possible iirc.

quote:
Originally posted by phalanxii
It is even more impossible to detect messages sent to external applications (like Winamp for example).
Also possible....

No need for an ActiveX for all of this. Just some API stuff... In fact, the ActiveXs do exactly what you already can do in Plus! by using the Windows API.

Though using this ActiveX might be the easy solution for some. So, very good and nice find though (y)...



But then again, some problems may arise when you use ActiveX (especially if more than one script is going to use the same one):
quote:
Originally posted by phalanxii
Firstly, the DLL is required to be registered when the script is imported, so this line must be added to ScriptInfo.xml:
code:
<OleFiles>
    <FileName>ARSubclass.dll</FileName>
</OleFiles>
Here is an example code of how to use it:
code:
var ARSubclass = new ActiveXObject("ARSubclassLib.ARSubclass");

I actually do not recommend all of this (even including the use of <OleFiles>). (I think Patchou will shoot me now :p) The reason is again that you will get into trouble when more than one script is going to use that DLL and you later install or remove even more scripts which use it.




---------

As for the tabbed chat window example, I think that can be done much easier and with subclassing just 1 window. The hidden Plus! window which controls the tabs.
RE: RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by phalanxii on 03-28-2008 at 12:47 AM

quote:
Originally posted by effection
very very nice piece of work
Thanks. :)
quote:
Originally posted by CookieRevised
No need for an ActiveX for all of this. Just some API stuff... In fact, the ActiveXs do exactly what you already can do in Plus! by using the Windows API.

Though using this ActiveX might be the easy solution for some. So, very good and nice find though (Y)...
Would you be able to give an example of such a code (using only Plus! and the Windows API)? I'm quite interested in knowing how to do this. I didn't think it would be possible to have real-time events (when the windows messages are received), even with the new Interop.GetCallbackPtr() which only supports synchronous calls (I assume this means the calls are returned immediately).
quote:
Originally posted by CookieRevised
I actually do not recommend all of this (even including the use of <OleFiles>). (I think Patchou will shoot me now :P) The reason is again that you will get into trouble when more than one script is going to use that DLL and you later install or remove even more scripts which use it.
I just experimented with this and noticed that it does cause a few problems. However, the ActiveX is not completely unregistered and the script's functionality returns to normal after restarting WLM.
quote:
Originally posted by CookieRevised
As for the tabbed chat window example, I think that can be done much easier and with subclassing just 1 window. The hidden Plus! window which controls the tabs.
Hmm... I tried subclassing just the hidden Plus! window, but I wasn't sure which message needed to be detected (WM_ACTIVATE doesn't work with it). The problem is that tabs can be changed with left mouse clicks as well as ctrl+tab on the keyboard. Also, subclassing each individual conversation window makes it easier to keep track of the current conversation handle. Suggestions?

I've also updated the TabbedStatusIcon to fix a few bugs.
RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by felipEx on 03-28-2008 at 05:00 AM

Hi :]

phalanxii:  i'm not totally sure if it's correct, but it works fine here

code:
var GWL_WNDPROC = -4
var lpPrevWndProc = 0

function OnEvent_Initialize() {
    if(Messenger.MyStatus > 0) OnEvent_SigninReady();
}

function OnEvent_SigninReady() {
    for (var e = new Enumerator(Messenger.CurrentChats); !e.atEnd(); e.moveNext()) OnEvent_ChatWndCreated(e.item());
}

function OnEvent_Uninitialize(MessengerExit){
    for (var e = new Enumerator(Messenger.CurrentChats); !e.atEnd(); e.moveNext()){
        if (lpPrevWndProc != 0)    Interop.Call('user32', 'SetWindowLongW', e.item().Handle, GWL_WNDPROC, lpPrevWndProc)
    }
}

function OnEvent_ChatWndCreated(ChatWnd){
    lpPrevWndProc = Interop.Call('user32', 'SetWindowLongW', ChatWnd.Handle, GWL_WNDPROC,  Interop.GetCallbackPtr('WindowProc') )
}

function OnEvent_ChatWndDestroyed(ChatWnd){
    if (lpPrevWndProc != 0)    Interop.Call('user32', 'SetWindowLongW', ChatWnd.Handle, GWL_WNDPROC, lpPrevWndProc)
}

function WindowProc(hwnd, uMsg, wParam, lParam){
    if (uMsg == 0x6 && wParam != 0){
        // stuff
    }
    return Interop.Call('user32', 'CallWindowProcW', lpPrevWndProc, hwnd, uMsg, wParam, lParam)
}
* felipEx waits for Cookie's reply :D
RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by phalanxii on 03-28-2008 at 07:03 AM

Thanks felipEx! It works fine over here too. I must say that it is also a lot more reliable than the ActiveX libraries and the script file size is obviously much smaller.

I'm wondering about the documentation though:

quote:
Originally posted by Messenger Plus! Live Scripting Documentation

Interop::GetCallbackPtr

Remember that the use of callback functions is restricted to synchronous calls. This means that all the calls made to the callback function must be done before the callee returns. Asynchronous calls are not permitted and must not be attempted in any circumstances.
Would this be considered an asynchronous call? :S

If this method is fine, I will release an updated TabbedStatusIcon (possibly in a separate thread).
RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by CookieRevised on 03-28-2008 at 04:57 PM

quote:
Originally posted by felipEx
* felipEx waits for Cookie's reply :D
That's what I meant, yes.

At phalanxii, hmmm, very good point. I guess this has something todo with the fact that scripts can be unloaded, removed, etc. But then again, as felipEx showed, you could handle that with Uninitialize() and ChatWndDestroyed() and stuff. Dunno really.... This is something for Patchou to answer I think: why it is that it isn't allowed, but still possible.


Anyways, even if it wouldn't have been possible like that, you could also make a very small ActiveX dll which does the same as  GetCallbackPtr(). I have used such an ActiveX a long time ago as kind of experiment, before GetCallbackPtr existed, and it worked nice. You call the ActiveX, the ActiveX returns a pointer you could use in a call to the SetWindowLongW API and you needed a var function (like the one used with Xniff and other ActiveX which call back to the script).

And, but I never made this experiment yet, I think you could also device something with a Plus! Window of which you 'borrowed' the WinProc address to be used to overwrite the WinProc of another window. And with OnWindowidEvent_MessageNotification you can catch the window messages of that 'external' window. If this works, it also means you could subclass already long before GetCallbackPtr() existed.
RE: RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by deAd on 03-28-2008 at 06:22 PM

quote:
Originally posted by CookieRevised
And, but I never made this experiment yet, I think you could also device something with a Plus! Window of which you 'borrowed' the WinProc address to be used to overwrite the WinProc of another window. And with OnWindowidEvent_MessageNotification you can catch the window messages of that 'external' window. If this works, it also means you could subclass already long before GetCallbackPtr() existed.
I tried this once, but it doesn't work because you only have control over certain messages. You can't call the original window proc for messages you don't handle yourself because Plus! only tells you about the ones you ask for, and if you ask for everything you'll experience severe delays since JScript is so slow.
RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by CookieRevised on 03-28-2008 at 06:49 PM

Isn't the reason you want to subclass something because you need to catch just a certain message? I don't see why you would want to catch all possible messages there are, that would be useless (unless you make a Windows Message Spying program).


RE: RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by deAd on 03-28-2008 at 07:20 PM

quote:
Originally posted by CookieRevised
Isn't the reason you want to subclass something because you need to catch just a certain message? I don't see why you would want to catch all possible messages there are, that would be useless (unless you make a Windows Message Spying program).
Yes, but you have to send the messages you don't want back to the original windowproc. You can't do this with a Plus! window because you only have control over the messages you ask for.
RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by Patchou on 03-28-2008 at 07:34 PM

the only thing I'll say on this subject is: careful. Subclassing windows properly is something that takes time and anybody doing this kind of thing needs to make sure his code is tested extensively.

As for using GetCallbackPtr with SetWindowLong: don't. The documentation specifically mentions that this function can only be used for synchronous calls. If you make a script that defys this rule, it may work at first but the combination of several scripts will certainely cause unexpected results / crashes.


RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by CookieRevised on 03-29-2008 at 03:56 AM

quote:
Originally posted by deAd
quote:
Originally posted by CookieRevised
Isn't the reason you want to subclass something because you need to catch just a certain message? I don't see why you would want to catch all possible messages there are, that would be useless (unless you make a Windows Message Spying program).
Yes, but you have to send the messages you don't want back to the original windowproc. You can't do this with a Plus! window because you only have control over the messages you ask for.
But aren't all other messages already forwarded to the original WinProc by Plus!?

Though I know this wouldn't work in all situations either though, and it is a bit of a dirty method. But I think this might be possible for specific things

EDIT: Plus! doesn't know the original WinProc, that would of course be the reason, no?


quote:
Originally posted by Patchou
As for using GetCallbackPtr with SetWindowLong: don't. The documentation specifically mentions that this function can only be used for synchronous calls. If you make a script that defys this rule, it may work at first but the combination of several scripts will certainely cause unexpected results / crashes.
I know this would be a lot to ask, but can you explain this in more detail and/or with an example, as I like to understand in much detail why it would cause a crash/unexpected results in some cases.

EDIT: apparently not possible :'( Just had a quick chat with Patchou. In short:
quote:
Originally posted by Patchou
the problem is a technical limitation: for callbacks I dont have much other choice than using static functions in my code so I cant have two scripts using a callback at the same time, which is why they have to be synchronous.
...
Thus right now, calling GetCallbackPtr() many times, from different scripts, will always return the same pointer for a function with a given number of arguments.
...
Meaning two scripts can't subclass any two window at the same time. Even one single script wouldn't be able to subclass two windows with that method.
...
Now, he also gave a hint as in how it would be possible, but that involves asm (which _is_ possible in scripting, but extremely advanced).
RE: RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by deAd on 03-29-2008 at 06:17 PM

quote:
Originally posted by CookieRevised
EDIT: Plus! doesn't know the original WinProc, that would of course be the reason, no?
Yes, this is the reason. Plus! would call the default Windows message handler and the chat window's message handler would never see the messages.

quote:
Originally posted by CookieRevised
Now, he also gave a hint as in how it would be possible, but that involves asm (which _is_ possible in scripting, but extremely advanced).
It's very messy, but you can inject the ASM bytes of a new wndproc into memory and use a pointer to those. It's really better to just use a dll if you need to subclass.
RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by CookieRevised on 03-29-2008 at 07:18 PM

quote:
Originally posted by deAd
quote:
Originally posted by CookieRevised
Now, he also gave a hint as in how it would be possible, but that involves asm (which _is_ possible in scripting, but extremely advanced).
It's very messy, but you can inject the ASM bytes of a new wndproc into memory and use a pointer to those. It's really better to just use a dll if you need to subclass.
I know, hence why I said it is possible. I use ASM in one of my own private scripts... (which is not messy imo though, actually straightforward) ;)

RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by riahc4 on 03-31-2008 at 12:59 AM

quote:
Originally posted by phalanxii
<snip>
Thanks for the resource. Even though SOMEONE has to bitch about the hard work you went thru to make a scripter's life easier, I for one appreciate your time and work/effort.

Also big thanks for "TabbedStatusIcon" but if its suppose to work like Veggie's Status Icon, it doesnt. At least not here:
Vista
Plus! 3.60
Messenger 9.0
RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by phalanxii on 04-12-2008 at 12:44 AM

Well, I certainly have no idea about ASM, but for those who are interested, deAd has explained how to do it in this separate thread.

Anyway, I was browsing around MSDN and found this page explaining how to subclass windows.

It uses the SetWindowSubclass, RemoveWindowSubclass and DefSubclassProc functions from comctl32.dll which comes with Windows XP (?).

Is it possible to use these functions with GetCallbackPtr in order to subclass, or is there still the same problem as using SetWindowLong and GetCallbackPtr?

quote:
Originally posted by riahc4
Also big thanks for "TabbedStatusIcon" but if its suppose to work like Veggie's Status Icon, it doesnt. At least not here:
Vista
Plus! 3.60
Messenger 9.0
I'm using XP at the moment with Plus! 3.60, but I have had confirmation that it works on Vista too. I don't know whether it works on Messenger 9.0 though, but I see no reason why it shouldn't. Perhaps you need to restart messenger?
RE: [Resource] Subclassing ActiveXObjects (and [Release] TabbedStatusIcon too!) by roflmao456 on 04-12-2008 at 02:13 AM

quote:
Originally posted by riahc4
Also big thanks for "TabbedStatusIcon" but if its suppose to work like Veggie's Status Icon, it doesnt. At least not here:
Vista
Plus! 3.60
Messenger 9.0
you can't use 3.60 with 9.0 :P

what is the difference in this script and the Status Icon script?