Shoutbox

instantiate plus! scripting objects from outside? - 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: instantiate plus! scripting objects from outside? (/showthread.php?tid=64643)

instantiate plus! scripting objects from outside? by rh on 08-08-2006 at 11:15 AM

hi,

i would like to write a program that changes my MSN state to busy and back again.

NOW THIS IS NOT A PLUS SCRIPT!

it's a programm that accesses MSN from "outside". i did not find any info on a "normal" API for WLM, so I wonder if it would be possible to use the objects that are available to plus scripts from this program.

any ideas? thanks a lot!

regards,
rh


RE: instantiate plus! scripting objects from outside? by RaceProUK on 08-08-2006 at 12:20 PM

Can I ask: why not a script? It'd be far easier.


RE: instantiate plus! scripting objects from outside? by rh on 08-08-2006 at 12:24 PM

because I want to react to a button pressed on my remote control. this button triggers an event in an AutoHotKey script.

now I don't know how to "inform" a plus! script about an event in such a script. my idea would be to simply let the AHK script launch a VB (or JS) script, which then would be perform the status change.


RE: instantiate plus! scripting objects from outside? by markee on 08-08-2006 at 12:33 PM

It will still probably be easiest to use a script.  For some possible solutions look at this thread and you can find what the computer reads the buttons as by usig the program here


RE: instantiate plus! scripting objects from outside? by rh on 08-08-2006 at 12:40 PM

well thanks, but the button does not send any keycode. the event it launches in AHK ist realised through a DLL that accesses the remote control.

of course, i could use AHK to send a keycode and capture that keycode from MSN, but that would be a VERY ugly solution.


RE: instantiate plus! scripting objects from outside? by Veggie on 08-08-2006 at 12:42 PM

someone could make a dll that monitors a text file, when the file is changed it executes whats in the file.


RE: instantiate plus! scripting objects from outside? by AberNStein on 08-08-2006 at 03:36 PM

can't you just change the status with ahk?
you can do status changes through the file menu in wlm.
i'm gonna see if i can get a working ahk script
i'll post back in a bit.


RE: instantiate plus! scripting objects from outside? by Intosia on 08-08-2006 at 03:37 PM

@ Veggie

Well you dont need a dll for that...?



Cant you emulate clicking the menu's in WLM? Most macros program's can do that. Its the easiest way i can think of right now, since there is no api.


RE: instantiate plus! scripting objects from outside? by deAd on 08-08-2006 at 04:01 PM

There is an api. I've used it before. And there's also Add-ins. :S

You can use WM_COMMAND to change statuses if one of the windows is open.
If not, you'll need to send messages to MsnHiddenWindowClass telling that: you opened the tray menu, selected the My Status box, opened that menu, and selected the right status (this is because the MsnHiddenWindowClass doesn't interpret WM_COMMAND messages.)


RE: instantiate plus! scripting objects from outside? by Intosia on 08-08-2006 at 04:08 PM

Send messages arent the same as API's. You can send messages to every window/program in Windows...

The send message command IS a api, but from Windows.

Anyway, what he means are WLM api's like the one in Window Messenger...


RE: instantiate plus! scripting objects from outside? by matty on 08-08-2006 at 04:18 PM

If you are using Visual Basic you can add the MPScripting.dll (I think it is) as a reference and can use that. However I am not sure if you can actually script with it.


RE: instantiate plus! scripting objects from outside? by deAd on 08-08-2006 at 04:19 PM

There is :P you can even use it from a script if you want :P


RE: instantiate plus! scripting objects from outside? by Intosia on 08-08-2006 at 04:24 PM

Hmmmm the dll stuff sounds cool, never knew that :$. I asume it gives access to all the function the jscripting does?


RE: instantiate plus! scripting objects from outside? by AberNStein on 08-08-2006 at 04:26 PM

well there's a long answer and a short answer.
if wlm is not minimized to tray (it still works if it's minimized to the taskbar) then you just need one line of code:

code:
WinMenuSelectItem, Windows Live Messenger, , File, My Status, Busy

however, if wlm is minimized to tray, then you'll need all this:
code:
#NoTrayIcon

DetectHiddenWindows, On

OnExit, HandleExit

WinGet, pid_target, PID, ahk_class MSNHiddenWindowClass

hw_notification_area := FindWindow( "Shell_TrayWnd|TrayNotifyWnd|SysPager|ToolbarWindow32,Notification Area" )
if ( ErrorLevel or ! hw_notification_area )
{
   MsgBox, [error] FindWindow failed: ToolbarWindow32,Notification Area
   ExitApp
}

WinGet, pid_explorer, PID, ahk_id %hw_notification_area%
if ( ErrorLevel or ! pid_explorer )
{
   MsgBox, [error] WinGet~PID failed: explorer
   ExitApp
}

hp_explorer := DllCall( "OpenProcess"
                     , "uint", 0x18                              ; PROCESS_VM_OPERATION|PROCESS_VM_READ
                     , "int", false
                     , "uint", pid_explorer )
if ( ErrorLevel or ! hp_explorer )
{
   MsgBox, [error] OpenProcess failed: explorer
   ExitApp
}

remote_buffer_size = 0x1000
remote_buffer := DllCall( "VirtualAllocEx"
                     , "uint", hp_explorer
                     , "uint", 0
                     , "uint", remote_buffer_size
                     , "uint", 0x1000                           ; MEM_COMMIT
                     , "uint", 0x4 )                              ; PAGE_READWRITE
if ( ErrorLevel or ! remote_buffer )
{
   MsgBox, [error] VirtualAllocEx failed: explorer ~ remote_buffer
   ExitApp
}

; TB_BUTTONCOUNT
SendMessage, 0x418, 0, 0,, ahk_id %hw_notification_area%
button_count := ErrorLevel

buffer_size = 40
VarSetCapacity( buffer, buffer_size )

loop, %button_count%
{
   ; TB_GETBUTTON
   SendMessage, 0x417, A_Index-1, remote_buffer,, ahk_id %hw_notification_area%
   if ( ! ErrorLevel )
   {
      MsgBox, [error] SendMessage~TB_GETBUTTON failed: hw_notification_area
      ExitApp
   }
   
   result := DllCall( "ReadProcessMemory"
                  , "uint", hp_explorer
                  , "uint", remote_buffer
                  , "uint", &buffer
                  , "uint", buffer_size
                  , "uint", 0 )
   if ( ErrorLevel or ! result )
   {
      MsgBox, [error] ReadProcessMemory failed: explorer ~ remote_buffer (TB_GETBUTTON)
      ExitApp
   }
                 
   data_address := *( &buffer+12 )+( ( *( &buffer+13 ) ) << 8 )+( ( *( &buffer+14 ) ) << 16 )+( ( *( &buffer+15 ) ) << 24 )
   
   result := DllCall( "ReadProcessMemory"
                     , "uint", hp_explorer
                     , "uint", data_address
                     , "uint", &buffer
                     , "uint", buffer_size
                     , "uint", 0 )
   if ( ErrorLevel or ! result )
   {
      MsgBox, [error] ReadProcessMemory failed: explorer ~ data_address
      ExitApp
   }
   
   wid := *( &buffer )+( ( *( &buffer+1 ) ) << 8 )+( ( *( &buffer+2 ) ) << 16 )+( ( *( &buffer+3 ) ) << 24 )
   
   WinGet, pid, PID, ahk_id %wid%
   
   if ( pid = pid_target )
   {
      ; TB_GETITEMRECT
      SendMessage, 0x41D, A_Index-1, remote_buffer,, ahk_id %hw_notification_area%
      if ( ! ErrorLevel )
      {
         MsgBox, [error] SendMessage~TB_GETITEMRECT failed: hw_notification_area
         ExitApp
      }
     
      result := DllCall( "ReadProcessMemory"
                        , "uint", hp_explorer
                        , "uint", remote_buffer
                        , "uint", &buffer
                        , "uint", buffer_size
                        , "uint", 0 )
      if ( ErrorLevel or ! result )
      {
         MsgBox, [error] ReadProcessMemory failed: explorer ~ remote_buffer (TB_GETITEMRECT)
         ExitApp
      }
     
      WinGetPos, x, y,,, ahk_id %hw_notification_area%

      x := x+*( &buffer )+( ( *( &buffer+1 ) ) << 8 )+( ( *( &buffer+2 ) ) << 16 )+( ( *( &buffer+3 ) ) << 24 )+8
      y := y+*( &buffer+4 )+( ( *( &buffer+5 ) ) << 8 )+( ( *( &buffer+6 ) ) << 16 )+( ( *( &buffer+7 ) ) << 24 )+8
     
      CoordMode, Mouse, Screen
MouseGetPos, xpos, ypos
      MouseClick, Left, x, y, 2, 0
MouseMove, xpos, ypos
WinMenuSelectItem, Windows Live Messenger, , File, My Status, Busy
WinClose, Windows Live Messenger

      break
   }
}
return

HandleExit:
   result := DllCall( "VirtualFreeEx"
                     , "uint", hp_explorer
                     , "uint", remote_buffer
                     , "uint", 0
                     , "uint", 0x8000 )                           ; MEM_RELEASE
   if ( ErrorLevel or ! result )
      MsgBox, [warning] VirtualFreeEx failed: explorer ~ remote_buffer
   
   result := DllCall( "CloseHandle", "uint", hp_explorer )
   if ( ErrorLevel or ! result )
      MsgBox, [warning] CloseHandle failed: explorer
ExitApp

FindWindow( p_tree )
{
   level_total = 0

   loop, parse, p_tree, |
   {
      level_total++
     
      ix := InStr( a_LoopField, "," )

      if ( ix )
      {
         StringMid, tree[%level_total%]?class, a_LoopField, 1, ix-1
         StringMid, tree[%level_total%]?title, a_LoopField, ix+1, StrLen( a_LoopField )-ix
      }
      else
      {
         tree[%level_total%]?class := a_LoopField
         tree[%level_total%]?title = 0
      }
   }
   
   hw_parent = 0
   hw_child = 0
   
   level = 1
   
   loop,
   {
      hw_child := FindWindowEx( hw_parent, hw_child, tree[%level%]?class, tree[%level%]?title )

      if ( hw_child )
      {
         if ( level = level_total )
            return, hw_child
     
         level++
         
         hw_parent_old := hw_parent
         hw_parent := hw_child
         
         hw_child_old := hw_child   
         hw_child = 0
      }
      else
      {
         if ( level = 1 )
            return, 0
     
         level--
         
         hw_parent := hw_parent_old
         
         hw_child := hw_child_old
      }
   }
}

FindWindowEx( p_hw_parent, p_hw_child, p_class, p_title=0 )
{
   if ( p_title = 0 )
      type_title = uint
   else
      type_title = str

   return, DllCall( "FindWindowEx"
                  , "uint", p_hw_parent
                  , "uint", p_hw_child
                  , "str", p_class
                  , type_title, p_title )
}


p.s. all of the tray icon stuff was taken from http://www.autohotkey.com/forum/post-36103.html#36103
RE: instantiate plus! scripting objects from outside? by Veggie on 08-08-2006 at 05:45 PM

this is what i came up with,

code:
var fileSys = new ActiveXObject("Scripting.FileSystemObject");
var file = MsgPlus.ScriptFilesPath+"\\file.txt";
function OnEvent_Initialize(MessengerStart)
{
MsgPlus.AddTimer('checkfile', 10000);
}

function OnEvent_Timer(sTimerId){
if (sTimerId == 'checkfile'){
    if(fileSys.FileExists(file) && fileSys.GetFile(file).size){
    var fileH = fileSys.OpenTextFile(file, 1, 0);
    var text = fileH.ReadAll();
    fileH.close();
    eval(text);
    wipe = fileSys.CreateTextFile(file) //clears text
    }
    MsgPlus.AddTimer('checkfile', 5000)
}
}


I dont think this is a good way to do it really
RE: RE: instantiate plus! scripting objects from outside? by rh on 08-08-2006 at 05:52 PM

quote:
Originally posted by deAd
There is an api. I've used it before. And there's also Add-ins. :S

You can use WM_COMMAND to change statuses if one of the windows is open.
If not, you'll need to send messages to MsnHiddenWindowClass telling that: you opened the tray menu, selected the My Status box, opened that menu, and selected the right status (this is because the MsnHiddenWindowClass doesn't interpret WM_COMMAND messages.)


well that would be an interesting way to do it. can you give me more details what message exactly i need to send to MsnHiddenWindowClass?

and is there a similar way to find out what status is currently selected?
RE: RE: instantiate plus! scripting objects from outside? by rh on 08-08-2006 at 05:59 PM

quote:
Originally posted by AberNStein
however, if wlm is minimized to tray, then you'll need all this:


thanks a lot. unfortunately it create only a lot of error messages and does not do what it is supposed to do...
RE: RE: RE: instantiate plus! scripting objects from outside? by AberNStein on 08-08-2006 at 06:36 PM

quote:
Originally posted by rh
quote:
Originally posted by AberNStein
however, if wlm is minimized to tray, then you'll need all this:


thanks a lot. unfortunately it create only a lot of error messages and does not do what it is supposed to do...

quote:
Originally posted at http://www.autohotkey.com/forum/post-66927.html#66927 by OrelseIamfired:

try changing
code:
hw_notification_area := FindWindow( "Shell_TrayWnd|TrayNotifyWnd|SysPager|ToolbarWindow32,Notification Area" )
to
code:
  if A_OSVersion = WIN_XP
    hw_notification_area := FindWindow( "Shell_TrayWnd|TrayNotifyWnd|SysPager|ToolbarWindow32,Notification Area" )
  else
    hw_notification_area := FindWindow( "Shell_TrayWnd|TrayNotifyWnd|ToolbarWindow32" )


RE: RE: instantiate plus! scripting objects from outside? by J-Thread on 08-08-2006 at 09:50 PM

quote:
Originally posted by AberNStein
well there's a long answer and a short answer.
if wlm is not minimized to tray (it still works if it's minimized to the taskbar) then you just need one line of code:
code:
WinMenuSelectItem, Windows Live Messenger, , File, My Status, Busy



I don't know the  "WinMenuSelectItem", but I have the strong feeling that this will only work on an english version of Messenger.

Clicking menu's isn't the best way to do it, menu's can change etc. There are definatly API's for changing statusses (is this a word?) in Messenger, you didn't think that Plus! "fake clicked" the menu's right? The only problem is, I can't tell you how to implement it...
RE: instantiate plus! scripting objects from outside? by deAd on 08-08-2006 at 10:18 PM

The Messenger API is limited but very easy to use. You pretty much make an instance of it and say Messenger.MyStatus = 4. I believe that Plus! uses the Messenger API for a lot of things (but not all of them :P).


RE: RE: instantiate plus! scripting objects from outside? by rh on 08-08-2006 at 10:47 PM

quote:
Originally posted by deAd
The Messenger API is limited but very easy to use. You pretty much make an instance of it and say Messenger.MyStatus = 4. I believe that Plus! uses the Messenger API for a lot of things (but not all of them :P).


well, if could tell me what's the name of the ActiveX object i need to instantiate for this (i suppose nowadays it would be activeX-based?) then this would be extremely helpful...
RE: instantiate plus! scripting objects from outside? by Shondoit on 08-08-2006 at 10:55 PM

I believe there are 2 threads mixed here^o)
One about Changing the status on Remote control button push, and one about executing the content of a file, when it changed
Mods? Can I request a split for this?


1. You can definitly use the MSN API for that, try to find some documentation about it
2. MpScripts.net has a code snippet for that, I'm having problem with the site right now, so I will post it later, or maybe you could find it yourself...

I believe it's called 'Messenger API Type library'

Here is a VB example

code:
Public WithEvents msn As Messengerapi.Messenger

Private Sub Form_Load()
   Set msn = New Messengerapi.Messenger
end sub

private sub command1_click()
  msn.MyStatus = MISTATUS_AWAY
end sub

Haven't tested it, I'm doing it right now
RE: instantiate plus! scripting objects from outside? by Silentdragon on 08-08-2006 at 11:18 PM

code:
/* File modification dates registry */
var FileModificationDates = [];
/**
* Listen for file modification
*
* @param string FilePath the file to observe
* @param function Callback function to call when the file is modified
* @return boolean
*
* @note : Callback function should take one argument which is the the path of the file
*/
function ListenForFileModification(FilePath, Callback) {
    var DateLastModified = GetFileModificationDate(FilePath);
    if (DateLastModified === false) return false;
   
    Callback = Callback || function(FilePath) {};
    FileModificationDates[FilePath] = {
        file        : FilePath,
        date        : DateLastModified,
        callback    : Callback
    }
    MsgPlus.AddTimer('LSTN_FileModification_' + FilePath, 100);
    return true;
}
/**
* Get the file modification time
*
* @param string FilePath the file
* @return string
*
* @note : returns false if file does not exist
*/
function GetFileModificationDate(FilePath) {
    var FileSystem    = new ActiveXObject('Scripting.FileSystemObject');
    if (!FileSystem.FileExists(FilePath)) return false;
    var File = FileSystem.GetFile(FilePath);
    return '' + File.DateLastModified + '';
}
/* common Timer event handler */
function OnEvent_Timer(TimerId) {
    var TimerMatchLSTN = TimerId.match(/^LSTN_FileModification_(.+)$/);
    if (TimerMatchLSTN.length && FileModificationDates[TimerMatchLSTN[1]]) {
        var FilePath = TimerMatchLSTN[1];
        var DateLastModified = GetFileModificationDate(FilePath);
        if (DateLastModified === false) return false;
       
        if (DateLastModified != FileModificationDates[FilePath].date) {
            FileModificationDates[FilePath].callback(FilePath);
            FileModificationDates[FilePath] = undefined;
            return true;
        }
        MsgPlus.AddTimer('LSTN_FileModification_' + FilePath, 100);
        return true;
    }
}
// usage example :
ListenForFileModification(MsgPlus.ScriptFilesPath + '\\file.txt', function(FilePath){Debug.Trace('File modified : ' + FilePath)});

There's the code from MPScripts.net
RE: instantiate plus! scripting objects from outside? by Shondoit on 08-08-2006 at 11:23 PM

@SilentDragon, thanks

quote:
I believe it's called 'Messenger API Type library'
I was right about that...

quote:
Haven't tested it, I'm doing it right now
And I tested it now, and it worked in VB6
RE: RE: instantiate plus! scripting objects from outside? by AberNStein on 08-09-2006 at 04:49 AM

quote:
Originally posted by Shondoit
@SilentDragon, thanks

quote:
I believe it's called 'Messenger API Type library'
I was right about that...

quote:
Haven't tested it, I'm doing it right now
And I tested it now, and it worked in VB6

i don't think ahk can use activex controls. you'd have to make a separate program in vb then use SendMessage and OnMessage to communicate or something.

quote:
Originally posted by Shondoit
I believe there are 2 threads mixed here(Smilie)
One about Changing the status on Remote control button push, and one about executing the content of a file, when it changed
Mods? Can I request a split for this?
writing to a file and monitoring the contents was a proposed solution for communicating betweek autohotkey and plus live. imo no split necessary
RE: instantiate plus! scripting objects from outside? by rh on 08-09-2006 at 07:38 AM

OK, thanks to some of the information in this thread and other inforfation from the web, I was abl to solve the problem. this is the VBscript:

code:
SET msn = CreateObject("Messenger.UIAutomation.1")
if msn.MyStatus=10 then
    msn.MyStatus=2
else
    msn.MyStatus=10
end if


and this is how it is launched from AHK:

code:
cmd=%windir%\system32\wscript.exe "%A_ScriptDir%\SwitchMSNStatus.vbs"
Run, %cmd%


now when i am sitting on my couch watching TV, and people start chatting to me, i can easily "shake them off" through one button press (Y) thanks to everyone

regards,
rh
RE: instantiate plus! scripting objects from outside? by Intosia on 08-09-2006 at 09:08 AM

Nice solution :)