Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Retrieve AddressBar of Firefox through DDE Message


  • Please log in to reply
55 replies to this topic
Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

xvariable = %sInfo%
but it does not work (and I have no idea why).

You must declare xvariable as Global with DDEMessage.
I recommend using DDEML.ahk, and the following will apply to DDEML. The variables will be sURL and sTitle. Replace
MsgBox, % sData
with
Loop,	Parse,	sData, CSV
	If	A_Index = 1
		sURL	:= A_LoopField
	Else If	A_Index = 2
		sTitle	:= A_LoopField


a
  • Guests
  • Last active:
  • Joined: --
thank you!

Wade Hatler
  • Members
  • 40 posts
  • Last active: Nov 06 2014 10:46 PM
  • Joined: 28 Sep 2004
Here is the complete code that does the job. It extracts both the current URL and the current Window Title.
   DDE_Connect("firefox" , "WWW_GetWindowInfo")
   Data := DDE_Request("WWW_GetWindowInfo")
   DDE_KILL()

   if(RegexMatch(Data, "^\x22(?P<URL>.*?)\x22,\x22(?P<Title>.*?)\x22", R)) {
      URL   := RURL
      Title := RTitle
   }
   else {
      ; Error Handling Code here if any
   }


Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

Here is the complete code that does the job.

What's the point of it? Parsing Loop exactly does the same. If you want to use regex, try to write one to parse
Data="http://www.autohotkey.com/forum/",""AutoHotkey\\Forum""
The result should be equivalent to
sData="http://www.autohotkey.com/forum/",""AutoHotkey\\Forum""
StringReplace, sData, sData, ", "", All
StringReplace, sData, sData, \\,  \, All
Loop,	Parse,	sData,	CSV
	If	A_Index = 1
		sURL	:= A_LoopField
	Else If	A_Index = 2
		sTitle	:= A_LoopField
MsgBox, %  sURL "`n" sTitle


Wade Hatler
  • Members
  • 40 posts
  • Last active: Nov 06 2014 10:46 PM
  • Joined: 28 Sep 2004
The point of the Regex is that for most parsing jobs a Regex is faster, simpler, shorter, more flexible and easier to understand than the equivalent parsing loop. It's not always better, but it usually is. This particular example is one where the Regex doesn't have as much of an advantage as they usually do, but if you make a habit of using Regexs then you'll find they are better than a parsing loop in the vast majority of cases. The Regex is also self-documenting if you know Regex syntax. The sample I gave actually works with Firefox.

For this particular example, the parsing job is made much simpler by CSV support in AHK, which is fairly rare as a built in feature for languages, so the advantage isn't as big in this particular example as it would be in most languages, unless you add a CSV library.

I also noticed from your example that I didn't properly account for having quotes in the title in my code, so the corrected version that does take it into account, and turns it into a function is as follows:

FF_Page(ByRef URL, ByRef Title) {
   DDE_Connect("firefox" , "WWW_GetWindowInfo")
   Data := DDE_Request("WWW_GetWindowInfo")
   DDE_KILL()

   Data := RegexReplace(Data, "\\([\x22])", "$1")
   if(RegexMatch(Data, "^\x22(?P<URL>.*?)\x22,\x22(?P<Title>.*?)\x22(,|$)", R)) {
      URL   := RURL
      Title := RTitle
      Return True
   }

   else {
      URL   := ""
      Title := ""      
      Return False
   }
}
The biggest differences are:

1. Like in your example, I need to unescape quotes. You show unescaping backslashes as well, although I don't see FF escaping them in the first place, so I wouldn't do that in this case.
2. I didn't have a terminator, so my example would fail if you had a double-quote in the title (notice (,|$) at the end of the Regex).

So to answer your original question, your 7 lines of Parse code become 2 lines of Regex code:
   sData = "http://www.autohotkey.com/forum/","\"AutoHotkey\\Forum\"",
;   StringReplace, sData, sData, \", "", All
;   StringReplace, sData, sData, \\,  \, All
;   Loop,   Parse,   sData,   CSV
;      If   A_Index = 1
;         sURL   := A_LoopField
;      Else If   A_Index = 2
;         sTitle   := A_LoopField

   sData := RegexReplace(sData, "\\([\x22\\])", "$1")
   RegexMatch(sData, "^\x22(?P<URL>.*?)\x22,\x22(?P<Title>.*)\x22(,|$)", s)

   MsgBox, %  sURL . "`n" sTitle
And if the parsing is anything more complicated than a simple CSV list, the Regex advantage just gets bigger.

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

sData := RegexReplace(sData, "\\([\x22\\])", "$1")
RegexMatch(sData, "^\x22(?P<URL>.*?)\x22,\x22(?P<Title>.*)\x22(|$),", s)

Try to parse this.
sData="http://www.autohotkey.com/forum/",""AutoHotkey",Forum"

sData := RegexReplace(sData, "\\([\x22\\])", "$1")
RegexMatch(sData, "^\x22(?P<URL>.*?)\x22,\x22(?P<Title>.*)\x22(,|$)", s)

Then this.
sData="http://www.autohotkey.com/forum/",""AutoHotkey",Forum",""

For this particular example, the parsing job is made much simpler by CSV support in AHK, which is fairly rare as a built in feature for languages, so the advantage isn't as big in this particular example as it would be in most languages, unless you add a CSV library.

So, what's your point again? Are you worried about that I may be unable to use regex? Well, you better be about yourself.

Wade Hatler
  • Members
  • 40 posts
  • Last active: Nov 06 2014 10:46 PM
  • Joined: 28 Sep 2004
Yow, I pasted in the wrong code. Sorry about that.

The correct 2 lines should be
sData := RegexReplace(sData, "\\([\x22\\])", "$1")
RegexMatch(sData, "^\x22(?P<URL>.*?)\x22,\x22(?P<Title>.*)\x22(,|$)", s)

So, what's your point again? Are you worried about that I may be unable to use regex? Well, you don't have to.


I don't quite understand the question, but I'll try to answer it anyway. I pull Regex out of the toolbox first in most cases because it works so well. The Regex support is one of my favorite parts of AHK. It's a lot cleaner and more flexible than most of the other half-dozen scripting languages I use. You have a good point that I posted a buggy Regex before I really tested it… my bad on that one. I also didn't really think of using the built in CSV support, just because I didn't think of it.

Your original question was why I used a Regex and I just tried to explain why I use them by preference. One should of course use non-buggy Regexs, but I do like them. In the case of almost any parsing task other than CSV, a Regex will beat any other solution hands-down.

Having said that, in this case your solution is at least as good and probably better. I just didn't think of using CSV support in the first place, but for this particular parsing task it's probably better.

For example, my Regex only works on this particular flavor of CSV entry. It would fail if you had a third field, or if one of the fields wasn't quoted, and it seems likely your loop, Parse..CSV solution would handle those tasks more gracefully. I'll keep it in mind next time I need to parse something CSV like.

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007

I don't quite understand the question

My point was that you must not remove the escape character \ from the escaped character " before matching. So, it would be, neglecting the case of unquoted fields,
sData="http://www.autohotkey.com/forum/",""AutoHotkey",Forum"
RegExMatch(sData, "^\x22(?<URL>(\\\\|\\\x22|[^\x22])*)\x22,\x22(?<Title>(\\\\|\\\x22|[^\x22])*)\x22", s)
sURL := RegexReplace(sURL, "\\([\\\x22])", "$1")
sTitle := RegexReplace(sTitle, "\\([\\\x22])", "$1")
MsgBox % sURL "`n" sTitle
So, it became more cumbersome than using the Parsing Loop.

Wade Hatler
  • Members
  • 40 posts
  • Last active: Nov 06 2014 10:46 PM
  • Joined: 28 Sep 2004
Correct. I wasn't worrying about unquoted fields because I happened to know that I didn't have them in this case. I wasn't trying to make a generic CSV parser.

Either way, your original solution using the parse loop is probably the simplest and best alternative, so I'd say to stick with that.

fedtegreve
  • Members
  • 80 posts
  • Last active: Jan 28 2014 02:26 PM
  • Joined: 18 Mar 2010
Hi Sean.

I'm using this script alot, but it only works with one explorer/firefox open.
It is always taken the last opened window, and not the last active.
How do I code it to take the active explorer window and tab that is active?

Thanks for the good script.
Benny

Wade Hatler
  • Members
  • 40 posts
  • Last active: Nov 06 2014 10:46 PM
  • Joined: 28 Sep 2004
I ran into the same problem myself, so I made a new program that uses some brute-force DDE code I hacked from somewhere else in this forum to make sure the DDE is directed to the most recently active instance of Firefox. I use it all the time, and it seems to work mostly OK.

This script can be called from another script with RunWait, and then the results are read from an INI file.

;=============================================================================================================
;This reprieves the URL and Title of the page currently displayed in the most recently active instance of
;Firefox.  It uses DDE to read the data, and then writes the results into an INI file.
;
;Results are written to %Temp%\BrowserInfo.ini in the following form:
;  [Global]
;  Address=http://www.autohotkey.com/forum/viewtopic.php?p=381624#381624
;  Title=Retrieve AddressBar of Firefox through DDE Message
;  Link=<a href="http://www.autohotkey.com/forum/viewtopic.php?p=381624#381624">Retrieve AddressBar of Firefox through DDE Message</a>
;
; For details, read the post at the address shown above.
;-------------------------------------------------------------------------------------------------------------
sApplication := "firefox"  ; iexplore, opera
sTopic       := "WWW_GetWindowInfo"
sItem        := "0xFFFFFFFF"
Noise        := "Free source code and programming help"

CF_TEXT          := 1
WM_DDE_INITIATE  := 0x3E0
WM_DDE_TERMINATE := 0x3E1
WM_DDE_ADVISE    := 0x3E2
WM_DDE_UNADVISE  := 0x3E3
WM_DDE_ACK       := 0x3E4
WM_DDE_DATA      := 0x3E5
WM_DDE_REQUEST   := 0x3E6
WM_DDE_POKE      := 0x3E7
WM_DDE_EXECUTE   := 0x3E8

DetectHiddenWindows, On

OnMessage(WM_DDE_ACK , "DDE_ACK")
OnMessage(WM_DDE_DATA, "DDE_DATA")

nAppli := DllCall("GlobalAddAtom", "str", sApplication, "Ushort")
nTopic := DllCall("GlobalAddAtom", "str", sTopic      , "Ushort")

Process, Exist
WinGet, hAHK, ID, ahk_pid %ErrorLevel%

SendMessage, WM_DDE_INITIATE, hAHK, nAppli | nTopic << 16,, ahk_id 0xFFFF

DllCall("GlobalDeleteAtom", "Ushort", nAppli)
DllCall("GlobalDeleteAtom", "Ushort", nTopic)
Return

DDE_ACK(wParam, lParam, MsgID, hWnd) {
   Global sItem, CF_TEXT, WM_DDE_REQUEST
   nItem := DllCall("GlobalAddAtom", "str", sItem, "Ushort")
   PostMessage, WM_DDE_REQUEST, hWnd, CF_TEXT | nItem << 16,, ahk_id %wParam%
   If ErrorLevel
   DllCall("GlobalDeleteAtom", "Ushort", nItem)
}

DDE_DATA(wParam, lParam, MsgID) {
   Global
   DllCall("UnpackDDElParam", "Uint", MsgID, "Uint", lParam, "UintP", hData, "UintP", nItem)
   DllCall(  "FreeDDElParam", "Uint", MsgID, "Uint", lParam)

   pData := DllCall("GlobalLock", "Uint", hData)
   VarSetCapacity(sInfo, DllCall("lstrlen", "Uint", pData+4))
   DllCall("lstrcpy", "str", sInfo, "Uint", pData+4)
   DllCall("GlobalUnlock", "Uint", hData)
   If (*(pData+1) & 0x20)
      DllCall("GlobalFree", "Uint", hData)
   If (*(pData+1) & 0x80)
      PostMessage, WM_DDE_ACK, hWnd, 0x80 << 8 | nItem << 16,, ahk_id %wParam%

   Loop, Parse, sInfo, CSV
   {
      if(A_Index == 1) {
         Address := A_LoopField
         IniWrite %A_LoopField%, C:\Temp\BrowserInfo.ini,Global,Address
      }
      else if(A_Index == 2) {
         Title := A_LoopField
         IniWrite %A_LoopField%, C:\Temp\BrowserInfo.ini,Global,Title
      }
   }

   GSub(Title, " - Opera$", "")
   GSub(Title, "\x22"     , "'")
   GSub(Title, Noise      , "")

   Link := "<a href=""" . Address . """>" . Title "</a>"
   EnvGet Temp, Temp
   IniWrite %Link%, %Temp%\BrowserInfo.ini,Global,Link

   ExitApp
}

DDE_POKE(sItem, sData) {
   Global  WM_DDE_POKE, hWndClient, hWndServer
   If SubStr(sData, -1) <> "`r`n"
      sData .= "`r`n"

   hItem := DllCall("GlobalAddAtom", "str", sItem, "Ushort")
   hData := DllCall("GlobalAlloc", "Uint", 0x0002, "Uint", 2+2+StrLen(sData)+1)   ; GMEM_MOVEABLE
   pData := DllCall("GlobalLock" , "Uint", hData)
   DllCall("ntdll\RtlFillMemoryUlong", "Uint", pData, "Uint", 4, "Uint", 1<<13|1<<14|1<<16) ; bRelease, CF_TEXT
   DllCall("lstrcpy", "Uint", pData+4, "Uint",&sData)
   DllCall("GlobalUnlock", "Uint", hData)

   lParam := DllCall("PackDDElParam", "Uint", WM_DDE_POKE, "Uint", hData, "Uint", hItem)
   PostMessage, WM_DDE_POKE, hWndClient, lParam,, ahk_id %hWndServer%
   If ErrorLevel
   {
      DllCall("GlobalFree", "Uint"  , hData)
      DllCall("GlobalDeleteAtom", "Ushort", hItem)
      DllCall("FreeDDElParam", "Uint", WM_DDE_POKE, "Uint", lParam)
   }
}

DDE_EXECUTE(sCmd) {
   Global  WM_DDE_EXECUTE, hWndClient, hWndServer

   hCmd := DllCall("GlobalAlloc", "Uint", 0x0002, "Uint", StrLen(sCmd)+1)
   pCmd := DllCall("GlobalLock" , "Uint", hCmd)
   DllCall("lstrcpy", "Uint", pCmd, "str",sCmd)
   DllCall("GlobalUnlock", "Uint", hCmd)

   PostMessage, WM_DDE_EXECUTE, hWndClient, hCmd,, ahk_id %hWndServer%
   If ErrorLevel
   DllCall("GlobalFree", "Uint", hCmd)
}

;-------------------------------------------------------------------------------------------------------------
GSub(byref Haystack, Needle, Replacement) {
    Haystack := RegexReplace(Haystack, Needle, Replacement)
    return Haystack
}


I use this all the time with another script that grabs this information, filters it down, there's a bunch of other processing, and then generates an e-mail with a hyperlink to the page. I have various hotkeys that filter the data differently, or send it to different people, etc.

Note that if all you want to do is send a link to the current page via e-mail, there are easier ways to do it. The easiest way is using a small Bookmarklet. That's what I do on my iPad where I don't have access to AHK (which practically cripples me). Make a bookmark with this text:

javascript:(function(){location.href='mailto:?SUBJECT=Link: ' + document.title + '&BODY=<html><body><a href=\x27' + escape(location.href) + '\x27>' + document.title + '</a></body></html>';})()

or look http://www.freewareg...om-any-browser/ or http://www.bing.com/...ail bookmarklet

fedtegreve
  • Members
  • 80 posts
  • Last active: Jan 28 2014 02:26 PM
  • Joined: 18 Mar 2010
Hi Wade.

No, I'm not using it for email, but for my autopassword script:
<!-- m -->http://www.autohotke... ... highlight=<!-- m -->

I use the http-adress to identify the site, so need the active one.

I have now tryed the script, and it is working for firefox, but not for iexplore, witch I'm using mostly. Could you take a look at that, please?

Zork
  • Guests
  • Last active:
  • Joined: --
Hi,

When working with AHK_L DdeConnect() seems to return zero. The same code with the same parameters works great at standard AHK.
Basically it is a simple DLL call:
DllCall("DdeConnect", "Uint", idInst, "Uint", hServer, "Uint", hTopic, "Uint", pCC)
But I don't see how it should be changed to work under AHK_L.

Can someone help porting this DLL code to AHK_L?

fedtegreve
  • Members
  • 80 posts
  • Last active: Jan 28 2014 02:26 PM
  • Joined: 18 Mar 2010
Hi again.
I have tryed Wade's script, but it does not take the URL from the active explorer/firefox window, but the last opened one.

It very annoying, when I'm pasting in username and password, and it is looking at a wrong URL.

Please, can someone help with this issue? Thanks.

Sean
  • Members
  • 2462 posts
  • Last active: Feb 07 2012 04:00 AM
  • Joined: 12 Feb 2007
Which one did you use, DDE.ahk or DDEMessage.ahk? BTW, it always returned the active one in my case. As you can see, there is not much you can control with DDE. In this case, you may try other values, like "0x1", than "0xFFFFFFFF" for sItem in the script. If still not work, then use Accessibility:
<!-- m -->http://www.autohotke...topic40980.html<!-- m -->