AutoHotkey Community

It is currently May 26th, 2012, 10:35 pm

All times are UTC [ DST ]




Post new topic Reply to topic  [ 11 posts ] 
Author Message
PostPosted: September 9th, 2009, 7:05 am 
Offline

Joined: February 24th, 2005, 8:45 am
Posts: 278
I have been trying to get a script working which will retrieve the messages sent (chat history) in a Windows Live Messenger conversation without activating the window, and with the awesome ACC.ahk and COM.ahk libraries by Sean and a function made by Lexikos on my request, here is the working version.

Requires ACC.ahk and COM.ahk libraries found here A big thanks to Sean for this.

Set contact's Name (WLM 8.1, 8.5) or email (WLM 2009) to the variable "title". The chat history is displayed in a message box and saved in a text file.

Code:
#Include ACC.ahk
#Include COM.ahk

SetTitleMatchMode, 2

title = contactemail@hotmail.com ; In Windows Live v 8.1 - 8.5, use the contact display name. Email will work in Windows Live Messenger 2009 only

WinGet, winid, ID, %title%
controlid := DllCall("FindWindowEx", Uint, winid, Uint, 0, str, "DirectUIHWND", int, 0)
ACC_Init()
controlpacc := Acc_AccessibleObjectFromWindow(controlid)
number := acc_ChildCount(controlpacc)
childpacc := ACC_AccessibleChildren(controlpacc, varChildren)

Loop, %number%
{
apacc := GetAccessibleChildFromArray(childpacc, varChildren, A_Index)
name := acc_Name(apacc, _idChild_)
If name = History
   {
          history := acc_value(apacc, idChild_)
    MsgBox, %history%
          FileAppend, %history%, Chat History of %title%.txt
        Break
       }
}
ACC_Term()

; The following function was written by Lexikos to get the Variant array data returned by AccessibleChildren, so a big Thanks to him
GetAccessibleChildFromArray(pacc, ByRef array, i)
{
    vt := NumGet(array, i*16, "ushort")
    val := NumGet(array, i*16 + 8)
    ; If array[i].vt == VT_DISPATCH, array[i].pdispVal contains an IDispatch interface pointer.
    if vt = 9
       return acc_Query(val)
    ; Otherwise it should be VT_I4 and array[i].lVal should contain the child's id.
    ; if vt = 3
    ; return acc_Child(pacc, val)
    ; return 0
}


Alternate version by Sean:

Code:
#Include ACC.ahk
#Include COM.ahk

SetTitleMatchMode, 2

title = contactemail@hotmail.com ; In Windows Live v 8.1 - 8.5, use the contact display name. Email will work in Windows Live Messenger 2009 only

WinGet, winid, ID, %title%
controlid := DllCall("FindWindowEx", Uint, winid, Uint, 0, str, "DirectUIHWND", int, 0)

controlpacc := Acc_AccessibleObjectFromWindow(controlid)
Acc_Init()
pacc := Acc_AccessibleObjectFromWindow(controlid)
penm := COM_QueryInterface(pacc, "{00020404-0000-0000-C000-000000000046}")
Loop, %   COM_Invoke(pacc, "accChildCount")
If   pobj := COM_Invoke(pacc, "accChild", A_Index)
{
   COM_Invoke(pobj, "accName", 0)="History" ? history:=COM_Invoke(pobj, "accValue", 0):"", COM_Release(pobj)
   If   history
   {
      MsgBox, %history%
      FileAppend, %history%, Chat History of %title%.txt
      Break
   }
}
COM_Release(penm)
COM_Release(pacc)
Acc_Term()

_________________
My small "thanks" to AHK in shape of these dedicated 3d images
Image


Last edited by sosaited on September 10th, 2009, 8:47 pm, edited 5 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 9th, 2009, 10:06 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
I'm glad to see you've got it working. :)


Actually, I've discovered a flaw in my logic:
Code:
    if vt = 3
        return acc_Child(pacc, val)

AccessibleChildren will sometimes return a child as an ID (vt == 3) instead of an IDispatch interface pointer (vt == 9). My original intention was to call acc_Child to convert each ID to an IDispatch interface pointer, but I think that will never work in practice since an ID seems to be returned only if the child has no IDispatch interface in the first place.

If an ID is returned the code needs to pass it to each function; for instance, if "pacc" is the IAccessible interface pointer which was used to get the child ID "child_id", to get the child's name you'd need to do this:
Code:
name := acc_Name(pacc, child_id)

If your code works, I suppose the child you are accessing always has an IDispatch interface; I recommend removing the two lines shown above. Then again, it might work in some obscure case, and probably isn't harmful.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 9th, 2009, 1:28 pm 
Offline

Joined: October 7th, 2006, 4:50 pm
Posts: 3157
Location: MN, USA
Is this something different than the option that WLM has under
Tools -> Options -> Messages -> "Automatically keep a history of my conversations" :?:


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 9th, 2009, 3:41 pm 
Offline

Joined: February 24th, 2005, 8:45 am
Posts: 278
Lexikos wrote:
If your code works, I suppose the child you are accessing always has an IDispatch interface; I recommend removing the two lines shown above. Then again, it might work in some obscure case, and probably isn't harmful.


I have tried removing the If expressions one by one, and on my pc it works even without the If vt = 9 expression, so I have commented out the rest of the stuff until some more members can test either version.

jaco0646 wrote:
Is this something different than the option that WLM has under
Tools -> Options -> Messages -> "Automatically keep a history of my conversations" :?:

It is different than that in a few ways. First of all that option saves every chat you have with every contact, or it doesn't save any. Second it only saves the log after you close the chat window or sign out. Both of which were a problem for my scenario.

_________________
My small "thanks" to AHK in shape of these dedicated 3d images
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 9th, 2009, 5:01 pm 
Offline

Joined: February 12th, 2007, 7:54 am
Posts: 2462
I expected you to pursue in the direction of WinEvent as you asked me to translate the code using it. Anyway, not bad as the first trial of Accessibility, however, one thing is that you never released any COM object which can leak memory. And, I often felt that AccessibleChildren is somewhat a waste as it always retrieves all the children even if it's unnecessary. I'd rather approach it as the following. (not tested)

Code:
Acc_Init()
pacc := Acc_AccessibleObjectFromWindow(controlid)
penm := COM_QueryInterface(pacc, "{00020404-0000-0000-C000-000000000046}")
While   COM_Enumerate(penm, pobj, vt)=0
If   vt=9
{
   COM_Invoke(pobj, "accName", 0)="History" ? history:=COM_Invoke(pobj, "accValue", 0):"", COM_Release(pobj)
   If   history
   {
      MsgBox, %history%
      FileAppend, %history%, Chat History of %title%.txt
      Break
   }
}
COM_Release(penm)
COM_Release(pacc)
Acc_Term()


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 10th, 2009, 12:27 am 
Offline

Joined: February 24th, 2005, 8:45 am
Posts: 278
[quote=Sean]I expected you to pursue in the direction of WinEvent as you asked me to translate the code using it. Anyway, not bad as the first trial of Accessibility, however, one thing is that you never released any COM object which can leak memory[/quote]
I couldn't figure out what exactly Min and Maximum event values to set in SetWinEvent function, so I didn't work on that. And basically I was just angry why AccessbleObjectFromPoint was working but not *fromWindow method.

I thought that Acc_Term() which in turn terminated the COM as well took care of memory leaks?. So should I used COM_Release(apacc) OR COM_Release(pName) and COM_Release(pValue)?

By the way the code you posted doesn't work for some reason. And while we are on the subject, how do calls like these work:
Code:
DllCall(NumGet(NumGet(1*ppv)+0)

Aren't DLL functions static inside a .dll file?

_________________
My small "thanks" to AHK in shape of these dedicated 3d images
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 10th, 2009, 2:18 am 
Offline

Joined: February 12th, 2007, 7:54 am
Posts: 2462
sosaited wrote:
By the way the code you posted doesn't work for some reason.
Exactly where did it fail? As a matter of fact, I assumed a few as Live Messenger is a MS product. Notice that I replaced acc_Name/acc_Value functions with COM_Invoke. If I could test, I might even have replaced
Code:
While   COM_Enumerate(penm, pobj, vt)=0
If   vt=9
with
Code:
Loop, %   COM_Invoke(pacc, "accChildCount")
If   pobj := COM_Invoke(pacc, "accChild", A_Index)
; 1-based index is correct here, but, not in your code which should have been 0-based

Or maybe your history was empty when you tested? In that case, there will be no prompt.

Quote:
I thought that Acc_Term() which in turn terminated the COM as well took care of memory leaks? So should I used COM_Release(apacc) OR COM_Release(pName) and COM_Release(pValue)?
I mentioned it lightly to suggest to try to do always in the right way as possible as you can, but since you asked, remember Messenger is not in your process, but running as a separate process. Actually you're more likely leaking resources in Messenger. And, COM_Release is not just a memory-cleaner. You should apply it only on COM objects.

Quote:
Code:
DllCall(NumGet(NumGet(1*ppv)+0)
Aren't DLL functions static inside a .dll file?
What you mean by static? What's your programming background, btw? Anyway, static/dynamic is not an issue here, the issue is how to access the function, regrdless whether static or not.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 10th, 2009, 9:25 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7502
Location: Australia
sosaited wrote:
on my pc it works even without the If vt = 9 expression, so I have commented out the rest of the stuff
If some IAccessible object returns a child as an ID, the function will try to interpret "val" as an IDispatch interface pointer and trouble may ensue. I would recommend leaving "If vt = 9" where it was, even if it doesn't seem necessary in practice.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 10th, 2009, 8:41 pm 
Offline

Joined: February 24th, 2005, 8:45 am
Posts: 278
Sean wrote:
Exactly where did it fail? As a matter of fact, I assumed a few as Live Messenger is a MS product. Notice that I replaced acc_Name/acc_Value functions with COM_Invoke. If I could test, I might even have replaced

I didn't run the script when the history object was empty. I just replaced the two lines you mentioned, and now it works. But its doesn't work with first code. Thanks

Sean wrote:
What you mean by static? What's your programming background, btw? Anyway, static/dynamic is not an issue here, the issue is how to access the function, regrdless whether static or not.

Sorry for asking, but there was no need to mock me like that. I started using AHK way back and that is when I read everything about the commands, and at that time DLLCall didn't support calls to function pointers. And after that I wasn't active for almost tow years, and my mistake that I didn't realize that you were using a pointer which is now supported in AHK, and it wasn't something uber-clever and new in the WinAPI or MSAA.

Lexikos wrote:
I would recommend leaving "If vt = 9" where it was, even if it doesn't seem necessary in practice.

Updated. Thanks man.

_________________
My small "thanks" to AHK in shape of these dedicated 3d images
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 11th, 2009, 12:02 am 
Offline

Joined: February 12th, 2007, 7:54 am
Posts: 2462
sosaited wrote:
Sorry for asking, but there was no need to mock me like that.
Just out of curiosity, why did you think that I was mocking you? It was a simple question. FYI, I didn't have any other programming background myself.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: September 14th, 2009, 12:05 pm 
Offline

Joined: June 16th, 2009, 9:56 am
Posts: 78
Location: Aleksinac, Serbia (I wish)
@Sosaited Awesome script thanks; this tool will be invaluable. Well done ;)

Code:
StringReplace, History, History, %EncRectangle%, `n, All ; change to the small rectangle made from encoding issues.
FileAppend, %History%, Msn.txt



@Sean
Quote:
FYI, I didn't have any other programming background myself.
*Sniff*


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC [ DST ]


Who is online

Users browsing this forum: Bing [Bot], Cristi®, jrav, Yahoo [Bot] and 13 guests


You can post new topics in this forum
You can reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Powered by phpBB® Forum Software © phpBB Group