Shoutbox

A few questions. - 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: A few questions. (/showthread.php?tid=94503)

A few questions. by Emblem on 05-02-2010 at 03:29 AM

Okay, so I'm about to attempt a script that will be quite a challenge. But once I'm done coding it, the final result will be amazing.

But to create this script I need to expand my knowledge, so I present the following questions:

  • Is it possible to draw on the Chat window? (Using GDI+ or something.)
  • Is there any way to display a right click menu? (Anywhere, not just on a Plus! Window.)
  • Is it possible to find out what font settings the currently signed in user is using?

Thanks in advance.

Edit:
I thought I'd expand upon my request, because I kind of expect people to shrug this off as a "He's new, he doesn't know what he's doing." thread.

I'm pretty sure the first two are possible using complex introp calls (which I'm willing to learn how to do, because this project is worth it.)

But, looking through the scripting documentation it appears that the font settings aren't retrievable. So I'm hoping that maybe messenger stores the font in the register, and I can access it that way.

To expand on the font request a bit more: I only need the current users font, and I only need to read it. I don't need to change it.
RE: A few questions. by Matti on 05-02-2010 at 08:33 AM

quote:
Originally posted by Emblem
Is it possible to draw on the Chat window? (Using GDI+ or something.)
I'm afraid that's going to be a nasty one. I think you can retrieve the device context of the window and I think you can then draw on it, but the problem is that windows are very complex objects and drawing on them using low-level GDI will most certainly cause trouble. You're going to need to find a way to redraw your additions every time the chat window needs repainting and even if you manage to do that, it'll probably be over-painted by the controls in the window.

I've seen some scripts trying to draw stuff on chat windows, but none of them managed to do this perfectly in all possible situations. Those scripts used a PlusWnd which is set as child window of the chat window and then positioned. Afterwards, they can reposition it in case the chat window is resized.

The first problem is that this repositioning has to be invoked using a timer, the script can't register an event for when the chat window is resized so it has to do this every x seconds which can cause flickering while resizing.

However the biggest problem is that it's not skin-proof. Every Messenger skin looks different and most of them have their elements in other positions than the default skin. Positioning the child window requires specific client coordinates and thus if it looks fine in one skin, it probably won't in another skin.
quote:
Is there any way to display a right click menu? (Anywhere, not just on a Plus! Window.)
Sure, you can use CreatePopupMenu, populate it with menu items and then show it using TrackPopupMenu. I needed this functionality for one of my own scripts, so I made a nice wrapper class for it (see attachment). Just create a new Menu instance, use AddMenuItem() to add items and use Open() to pop it up somewhere on the screen.

This Menu class is taken straight from the current beta of Screenshot Sender 5, documentation is in the script file itself. If you need to add images in front of the menu items, you're going to have to import the function GdiCreateHBITMAPFromFile() and its dependencies from Countdown Live 2 or Screenshot Sender 5.
quote:
Is it possible to find out what font settings the currently signed in user is using?
I think they're somewhere in the registry, try searching these forums.
RE: A few questions. by CookieRevised on 05-03-2010 at 11:00 AM

quote:
Originally posted by Matti
quote:
Is there any way to display a right click menu? (Anywhere, not just on a Plus! Window.)
Sure, you can use CreatePopupMenu, populate it with menu items and then show it using TrackPopupMenu. I needed this functionality for one of my own scripts, so I made a nice wrapper class for it (see attachment). Just create a new Menu instance, use AddMenuItem() to add items and use Open() to pop it up somewhere on the screen.

This Menu class is taken straight from the current beta of Screenshot Sender 5, documentation is in the script file itself.

This works perfectly for a Plus! window which you can subclass in Plus!, but does this also work for any other window (eg: a conversation window, as that is what he is asking) which you can not subclass in Plus! scripting?

A very long time ago (before SSS), I did the same: A quick 'n dirty rough test showed that creating (and even showing the context menu) worked fine, but there was no way to detect a click because you couldn't subclass the window which would receive the clicked menu IDs (I tested this on the main contact list iirc).

At least, that was a long time ago without too much looking into it (it was a very quick test), and as far as I can remember so I dunno...



quote:
Is it possible to find out what font settings the currently signed in user is using?
HKEY_CURRENT_USER\Software\Microsoft\MSNMessenger\PerPassportSettings\<the MSNID of the user>\IM Format

However, this can not be changed on the fly. Messenger only checks this when opening or changing the font via its own UI (so I suppose you could maybe force it to reload or something by patching it in memory/invoking its internal routines or something. But that is not for the fainth of heart.)
RE: RE: A few questions. by Matti on 05-03-2010 at 04:02 PM

quote:
Originally posted by CookieRevised
This works perfectly for a Plus! window which you can subclass in Plus!, but does this also work for any other window (eg: a conversation window, as that is what he is asking) which you can not subclass in Plus! scripting?

A very long time ago (before SSS), I did the same: A quick 'n dirty rough test showed that creating (and even showing the context menu) worked fine, but there was no way to detect a click because you couldn't subclass the window which would receive the clicked menu IDs (I tested this on the main contact list iirc).

At least, that was a long time ago without too much looking into it (it was a very quick test), and as far as I can remember so I dunno...
Indeed, you need a subclass window to receive those notifications, but I believe this can be done using a simple Plus! window created solely for that purpose. In Screenshot Sender 5, we use the following interface:
XML code:
<Window Id="Subclass" Version="1">
    <Attributes><ShowInTaskbar>false</ShowInTaskbar></Attributes>
    <Position Width="0" Height="0"/>
    <DialogTmpl/>
</Window>

When the popup menu has to be opened, we create the subclass window (if not already created) and pass its handle as hWnd. As MSDN puts it: this window receives all messages from the menu. Therefore, it should not make any difference whether the menu was opened because of an event in a chat window or in a Plus! window or any other source. There's no need to subclass the chat window itself, any window will do, including Plus! windows.
RE: A few questions. by CookieRevised on 05-04-2010 at 05:05 PM

yes, ok, that is if you create the menu starting from your own (a Plus!) window. Aka you have a way to know when and where to display it. But how do you detect a right mouse click on the chat window without subclassing the chat window? AFAIK, you can't...

You hav 2 possebilities as far as I can see atm:

1) or you start from your own window. Then you can indeed detect clicked menu items because the menu belongs to your own subclassed window.
(is what you're saying)

But then you'r stuck because you have no (decent) way to know where and when to show the menu with TrackPopupMenu in the first place, as you can't detect mouse clicks (in a decent not-polling way) on another window because its window messages are send to that other window.

2) or you alter the menu from the chat window itself to include your custom menu items too (very easy to do). But then you're stuck once again because you can't detect it when the user selects the items. Again because you can't subclass that window and detect your custom menu IDs.
(is what I've quickly 'tested' before)

-------

On the other hand I might be missing something obvious here though....


RE: A few questions. by Matti on 05-04-2010 at 06:15 PM

My apologizes, I think I misunderstood your initial point. I assumed that you were saying it wasn't possible to detect a click on an item in the created menu. I think you meant that it wasn't possible to detect a click in the chat window and use that event as a trigger for a pop-up menu. :)

If so, then I completely agree with your point. You need a way to subclass the chat window in order to receive notifications and have a way to react upon them (such as opening a menu). This is indeed not possible with pure Plus! scripting at the moment.

Injecting your own entries in the chat window's pop-up menus is another option indeed, but as you correctly state this will get you into even more trouble than using your own menus.

Perhaps a clever programmer could do some magic by modifying the process code directly (but I doubt that would be easy/effective), however the only possibility I see is to throw in an external DLL to subclass the window. This requires a fair amount of knowledge of C++ or Visual Basic or whatever native language you like, which even I don't have - I'm more a C# and scripting guy. :P


RE: A few questions. by CookieRevised on 05-04-2010 at 06:32 PM

Yup, an external DLL (Native, ActiveX, .NET, whatever)  is the only way at the moment afaik.

Luckally, as you have seen, MnJul has shared a technique which will let you grab Plus! objects directly from your DLL.

So you don't even need a temporary Plus! window anymore either as a kind of man-in-the-middle* to talk to your DLL if you want to manipulate or fire Plus! events.

*This man-in-the-middle technique was also used with the SendTo script for example. The external DLL simply sends a message to a Plus! window, which is a small (1x1px) hidden window dedicated only for this and subclassed by the Plus! script. And the Plus! script on its turn comes in action when it detects a new message on this Plus! window and does what it is suppose to do.

--------------------------

This only leaves the drawing on the chat window problem. Which is, afaik, almost impossible to do in a proper way....
(It is possible though, prooven by Plus! itself, but it would require a hell of a lot of very advanced programming, which nobody but Patchou has mastered to do in a proper reliable way... -damn him and his secrets :p-)


RE: A few questions. by Eljay on 05-04-2010 at 06:51 PM

quote:
Originally posted by CookieRevised
This only leaves the drawing on the chat window problem. Which is, afaik, almost impossible to do in a proper way....
(It is possible though, prooven by Plus! itself, but it would require a hell of a lot of very advanced programming, which nobody but Patchou has mastered to do in a proper reliable way... -damn him and his secrets :p-)

Which stuff is manually draw by Plus! on chat windows? Isn't all the custom Plus! stuff just done by modifying the uifiles and letting WLM handle the drawing?
RE: A few questions. by CookieRevised on 05-04-2010 at 07:01 PM

Yep... that's exactly the advanced programming I mean.

I didn't say manually drawing all the stuff yourself though. That is completely undoable (even for a C++ god like Patchou). Handling simple owner-drawn objects is already hard enough, let alone a whole chat window from another program. You probably end up making your own client :p

But showing graphics, dynamically injected in certain places in the UIFiles, is doable... as Plus! does it...


RE: A few questions. by Eljay on 05-04-2010 at 07:47 PM

Yeah that does seem like it would be amazingly difficult (if not impossible) to do from a script.

Assuming the way that Plus! modifies them is by using detours to proxy resource loading functions (LoadResource etc.), then a script would need to also do this, correct? I'm guessing it also would depend on the order in which things are loaded. If WLM has already loaded uifiles before Plus! has initialised scripts then our poor scripts don't stand a chance of doing this :P

It's also important to note this isn't something which can really be done "on-the-fly". You have to restart messenger for any of the uifiles to be reloaded. It really does seem messy to be modifying Messenger like this from a script...

Would it be easier to create custom buttons using a skin with certain cmdids that you could then process by subclassing the window?


RE: A few questions. by CookieRevised on 05-04-2010 at 08:03 PM

quote:
Originally posted by Eljay
It's also important to note this isn't something which can really be done "on-the-fly". You have to restart messenger for any of the uifiles to be reloaded.
I suppose that depends on what you exactly want to change, where and when. IIRC Patchou once said that some stuff could be done on-the-fly, but Plus! simply reloads Messenger out of 'convenience' so to speak; to avoid difficulties and a lot of messy checks and what not as the purpose of skinning is to skin the entire beast, not just one element or only a chat window or something.

quote:
Originally posted by Eljay
It really does seem messy to be modifying Messenger like this from a script...
Can't be done from a script though (timing issues and indeed the order of things). You need once again an external DLL I think...

quote:
Originally posted by Eljay
Would it be easier to create custom buttons using a skin with certain cmdids that you could then process by subclassing the window?
If that would be possible with skinning (I dunno, am not a skinner)... then I suppose it would be easier...

interesting stuff though
RE: A few questions. by Eljay on 05-04-2010 at 10:14 PM

Hmm well after playing around for a while (I haven't done much scripting/skinning experimentation for ages, damn uni :P) I have managed to add a button with a custom cmdid using a skin and using Mnjul's subclassing dll I have managed to output a message to the debug console when this button is pressed. So this seems like it works ok, but it comes with the problem of having to create a skin for it to work (and if the user already has a skin...)

As for creating a menu, there would need to be some way to communicate with the script from within the dll code so that the script knows when the button is pressed. I couldn't figure out a way to do this in my brief experiment using Mnjul's method (maybe this is easier with the old invisible subclass window method).

It's fun to play around with, but I'm too tired atm and should really be revising... :P


RE: A few questions. by CookieRevised on 05-04-2010 at 11:27 PM

Going a bit of topic with this I guess.... but

Maybe you actually don't need MnJul's method.

In scripts you can use timers. Let's assume these timers are created internally like any other timer in Windows (seems logical). If that is tru, then couldn't you try to find out the id of the timer as it is known by Windows?

1) Allocate a 1 byte datablock in memory (use to check false triggers later on) and set it to 0xFF.
2) Create a timer and set the interval to maximum (one day)
3) Find out the timer id like it is known by Windows (dunno how this would be done in a reliable way though)
4) Pass that timer id (from Windows) and the datablock address to the DLL

5) When the DLL needs to trigger an event, let it change the datablock's value to 0x0 and..
6) ..let it set the timer's interval to like 10ms (after checking if they still exist of course). Or maybe broadcast a WM_TIMER message to the appropiate hWnd. All depends on how Plus! does its timers.

7) When the timer triggers, the script must check the value of the datablock. If it is 0xFF the timer was falsly triggered (you need to have been signed in for one day for that to happen though). If it is 0x0, the timer was triggered by the DLL. => bingo!
8) In either case, reinitialize the timer and set the datablock again to 0xFF.

So, these are a lot of wild assumptions here (steps 3 and 6 are the problem steps). But might be worthwhile or at least interesting to look into it... then again, it can't be that easy, can't it? lol

-------

And if that fails (very very likely), and you still don't want to create a small dedicated subclassed Plus! window to listen to the DLL, you could use ChatWnd.SendMessage (with MnJul's method) I suppose... which would then trigger to appropiate script event.

-------

Then again, to get back "on topic"... If you're going to use an external DLL anyways, wouldn't it already be able to do whatever this "new feature" is suppose to be doing nativly without the need to call back to the script? Thus no need for anything other then initializing the dll, and call it with the appropiate objects and whatever other stuff you might need (even maybe using MnJul's method to pass the needed Plus! and Messenger objects in case you need them).

I obviously need to get some sleep I suppose :p


RE: RE: A few questions. by TheSteve on 05-05-2010 at 07:29 AM

quote:
Originally posted by CookieRevised
quote:
Originally posted by Eljay
It's also important to note this isn't something which can really be done "on-the-fly". You have to restart messenger for any of the uifiles to be reloaded.
I suppose that depends on what you exactly want to change, where and when. IIRC Patchou once said that some stuff could be done on-the-fly, but Plus! simply reloads Messenger out of 'convenience' so to speak; to avoid difficulties and a lot of messy checks and what not as the purpose of skinning is to skin the entire beast, not just one element or only a chat window or something.
Before UIFILE was phased out internally, It was a simple 1 or 2 byte patch that allowed for skins to be reloaded, however this only worked for images loaded from the UIFILE itself. Icons for example could only be changed the first time. (For those who are unaware, UIFILEs are no longer used by messenger. Plus! converts skin's UIFILEs to the new binary format at load time)

As far as the painting directly on the chat window goes, you would need have a DLL hook the EndPaint function.  The PAINTSTRUCT that is passed to the EndPaint function *should* still have a valid HDC that you could paint with.  The only problem with this is that you would have to figure out where to draw your custom control based on the size of the window.  Handling of click events can be caught by a subclassing the window.

Perhaps it would be cool to see a skin modification interface added to plus. Something that would allow scripts to add UI elements at either program start or if the programmers are up to the challenge, in real time.
RE: A few questions. by Eljay on 05-05-2010 at 08:41 AM

Continuing to play around with communicating the other way via some callback objects... not sure if this is dodgy, my code is probably horrible because I've never done anything like this before but it seems to work ok :P

This example script just creates an object with a function named "testing". The object is then passed to the dll which invokes the testing method with 2 string parameters.

Back to revision for now anyway :P


RE: A few questions. by Mnjul on 05-05-2010 at 08:50 AM

quote:
Originally posted by Eljay
As for creating a menu, there would need to be some way to communicate with the script from within the dll code so that the script knows when the button is pressed. I couldn't figure out a way to do this in my brief experiment using Mnjul's method (maybe this is easier with the old invisible subclass window method).
I'm pretty sure you can use Interop.GetCallbackPtr inside the DLL to get the address of a jscript function (as long as you know the function name and the number of parameters). Then, cast the return value into an appropriate C++ function pointer then you should be able to call the function.
RE: A few questions. by CookieRevised on 05-06-2010 at 01:04 AM

quote:
Originally posted by Mnjul
I'm pretty sure you can use Interop.GetCallbackPtr inside the DLL to get the address of a jscript function (as long as you know the function name and the number of parameters). Then, cast the return value into an appropriate C++ function pointer then you should be able to call the function.
afaik, unfortunatly not. That address can only be used for synchronous callbacks, not asynchronous calls. So getting an address might be possible, but it might not be valid anymore at any time.

That is, dunno exactly how the rules go when you use it from inside your DLL though (using your method), but I wouldn't be surprised if the same thing applies for that DLL.

quote:
Originally posted by Mnjul
I'm pretty sure you can use Interop.GetCallbackPtr inside the DLL to get the address of a jscript function (as long as you know the function name and the number of parameters). Then, cast the return value into an appropriate C++ function pointer then you should be able to call the function.
Unfortunatly not. That address can only be used for synchronous callbacks, not async. So getting an address might be possible, but it might not be valid anymore at any time.

That is, dunno exactly how the rules go when you use it from inside your DLL though (using your method), but I wouldn't be surprised if the same thing applies for that DLL.

quote:
Originally posted by Eljay
Continuing to play around with communicating the other way via some callback objects... not sure if this is dodgy, my code is probably horrible because I've never done anything like this before but it seems to work ok :P

This example script just creates an object with a function named "testing". The object is then passed to the dll which invokes the testing method with 2 string parameters.
omg... that can't be working... that was waaaaay too easy and too little code :p:D

(y)(y)

/me pulls up a barrier so Patchou can't come in spoiling the fun by saying there is a catch
RE: A few questions. by Mnjul on 05-06-2010 at 06:49 AM

quote:
Originally posted by CookieRevised
afaik, unfortunatly not. That address can only be used for synchronous callbacks, not asynchronous calls. So getting an address might be possible, but it might not be valid anymore at any time.

That is, dunno exactly how the rules go when you use it from inside your DLL though (using your method), but I wouldn't be surprised if the same thing applies for that DLL.
Well, every time you want to call that jscript function, you call Interop.GetCallbackPtr and use it right away. I think that wouldn't cause you problems. Unless I'm missing something huge here :-/

Forgot to say, Eljay, would you mind me putting your codes in my tutorial (with your name attributed to)?
RE: A few questions. by Eljay on 05-06-2010 at 07:25 AM

quote:
Originally posted by CookieRevised
omg... that can't be working... that was waaaaay too easy and too little code :p:D

Haha, my thoughts exactly. There just has to be something that breaks magically elsewhere :P
RE: A few questions. by Emblem on 05-17-2010 at 05:07 PM

Haha wow. I didn't notice how much discussion this topic had gotten.

Since I have a feeling I won't be going through with this (doesn't stop me from at least trying though) was to create a spellcheck.

One that works properly, with a squggly red line drawn under mis-spelled words and with the corrections displayed in a popup menu.

Edit
For the part about drawing in the window. Does anyone know if the text box where you enter your chat text has it's own DC or if it's shared with the rest of the window. I remember looking into this quickly and finding that it's shared. :/

If it's not shared and it's unique, I could just subclass the window to receive menu clicks, and draw on it using an external DLL and all should be good.