AutoHotkey Community

It is currently May 27th, 2012, 10:31 am

All times are UTC [ DST ]




Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: ShowHTMLDialog
PostPosted: October 10th, 2007, 12:52 pm 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7503
Location: Australia
ShowHTMLDialog( URL [, argIn, Options, hwndParent, Flags ] )
Creates a modal dialog box that displays HTML. (It does not return until the dialog is closed.)

Requires: COM Standard Library
Optional: DispatchObj allows JavaScript embedded in the HTML to interact with the AutoHotkey script.

Parameters
    URL:
      Any Internet Explorer-compatible URL. If the javascript: protocol is specified, the result of the JavaScript expression is used as the HTML content (see Example 2.)
    dialogArguments:
      A string or object to pass to the dialog. It is accessible in JavaScript as window.dialogArguments (usually just dialogArguments will do.) Pass an object by prefixing "+" (quotes included; see Example 2.)
    Options:
      A semicolon-delimited list of values, as described in the script.
    hwndParent:
      The unique ID/hwnd of the parent of the dialog box.
    Flags:
      If specified, ShowHTMLDialogEx is called instead of ShowHTMLDialog. See "Useful flags" at the bottom of this post.
returns: The value of window.returnValue, if assigned by the dialog's script.
Code:
/* Flags: Search MSDN for ShowHTMLDialogEx.
#define HTMLDLG_NOUI                     0x0010 // invisible
#define HTMLDLG_MODAL                    0x0020 // normal behaviour
#define HTMLDLG_MODELESS                 0x0040 // returns immediately
#define HTMLDLG_PRINT_TEMPLATE           0x0080
#define HTMLDLG_VERIFY                   0x0100 // force into viewable portion of desktop
#define HTMLDLG_ALLOW_UNKNOWN_THREAD     0x0200 // IE7+
*/

/*  Options: one or more of the following semicolon-delimited values:
dialogHeight:sHeight
    Sets the height of the dialog window.
    Valid unit-of-measure prefixes: cm, mm, in, pt, pc, em, ex, px
dialogLeft:sXPos
    Sets the left position of the dialog window relative to the upper-left
    corner of the desktop.
dialogTop:sYPos
    Sets the top position of the dialog window relative to the upper-left
    corner of the desktop.
dialogWidth:sWidth
    Sets the width of the dialog window.
    Valid unit-of-measure prefixes: cm, mm, in, pt, pc, em, ex, px
center:{ yes | no | 1 | 0 | on | off }
    Specifies whether to center the dialog window within the desktop. Default: yes
dialogHide:{ yes | no | 1 | 0 | on | off }
    Specifies whether the dialog window is hidden when printing or using print
    preview. Default: no
edge:{ sunken | raised }
    Specifies the edge style of the dialog window. Default: raised
resizable:{ yes | no | 1 | 0 | on | off }
    Specifies whether the dialog window has fixed dimensions. Default: no
scroll:{ yes | no | 1 | 0 | on | off }
    Specifies whether the dialog window displays scrollbars. Default: yes
status:{ yes | no | 1 | 0 | on | off }
    Specifies whether the dialog window displays a status bar. Default: no
unadorned:{ yes | no | 1 | 0 | on | off }
    Specifies whether the dialog window displays the window border.
*/
ShowHTMLDialog(URL, dialogArguments="", Options="", hwndParent=0, Flags=0)
{
    ; "Typically, the COM library is initialized on a thread only once. Subsequent
    ;  calls to CoInitialize or CoInitializeEx on the same thread will succeed,..."
    COM_CoInitialize()
   
    hinstMSHTML := DllCall("LoadLibrary","str","MSHTML.DLL")
    hinstUrlMon := DllCall("LoadLibrary","str","urlmon.dll") ; necessary to keep the URL moniker in memory.
   
    if !hinstMSHTML or !hinstUrlMon
        goto ShowHTMLDialog_Exit
   
    pUrl := COM_SysAllocString(URL)
   
    hr := DllCall("urlmon\CreateURLMonikerEx","uint",0,"uint",pUrl,"uint*",pUrlMoniker,"uint",1)
    if (ErrorLevel) {
        Error = DllCall(CreateURLMoniker)--%ErrorLevel%
        goto ShowHTMLDialog_Exit
    }
    if (hr or !pUrlMoniker) {
        Error = CreateURLMoniker--%hr%
        goto ShowHTMLDialog_Exit
    }

    pOptions := Options!="" ? COM_SysAllocString(Options) : 0
   
    VarSetCapacity(varArgIn,16,0), VarSetCapacity(varResult,16,0)

    if dialogArguments is integer
    {   ; int64 or +object (IDispatch)
        NumPut(SubStr(dialogArguments,1,1)="+" ? 9:20,varArgIn,0)
        NumPut(dialogArguments,varArgIn,8,"int64")
    } else {    ; string
        NumPut(8,varArgIn,0)
        NumPut(pArgIn:=COM_SysAllocString(dialogArguments),varArgIn,8)
    }

    if Flags
        hr := DllCall("mshtml\ShowHTMLDialogEx","uint",hwndParent,"uint",pUrlMoniker,"uint",Flags,"uint",&varArgIn,"uint",pOptions,"uint",&varResult)
    else
        hr := DllCall("mshtml\ShowHTMLDialog","uint",hwndParent,"uint",pUrlMoniker,"uint",&varArgIn,"uint",pOptions,"uint",&varResult)
    if (ErrorLevel) {
        Error = DllCall(ShowHTMLDialog)--%ErrorLevel%
        goto ShowHTMLDialog_Exit
    }
    if (hr) {
        Error = ShowHTMLDialog--%hr%
        goto ShowHTMLDialog_Exit
    }
    ; based on a line from COM_Invoke(). returnValue = varResult as string;
    InStr(" 0 4 5 6 7 14 "," " . NumGet(varResult,0,"Ushort") . " ") ? DllCall("oleaut32\VariantChangeTypeEx","Uint",&varResult,"Uint",&varResult,"Uint",0,"Ushort",0,"Ushort",8) : "", NumGet(varResult,0,"Ushort")=8 ? (returnValue:=COM_Ansi4Unicode(NumGet(varResult,8))) . COM_SysFreeString(NumGet(varResult,8)) : returnValue:=NumGet(varResult,8)
   
ShowHTMLDialog_Exit:
    if pArgIn
        COM_SysFreeString(pArgIn)
    if pOptions
        COM_SysFreeString(pOptions)
    if pUrlMoniker
        COM_Release(pUrlMoniker)
    if pUrl
        COM_SysFreeString(pUrl)
   
    ; "Each process maintains a reference count for each loaded library module.
    ;  This reference count is incremented each time LoadLibrary is called and
    ;  is decremented each time FreeLibrary is called."
    ; -- So FreeLibrary() will not unload these DLLs if they were loaded before
    ;    the function was called. :)
    if hinstMSHTML
        DllCall("FreeLibrary","uint",hinstMSHTML)
    if hinstUrlMon
        DllCall("FreeLibrary","uint",hinstUrlMon)
   
    ; "To close the COM library gracefully, each successful call to CoInitialize
    ;  or CoInitializeEx, including those that return S_FALSE, must be balanced
    ;  by a corresponding call to CoUninitialize."
    COM_CoUninitialize()
   
    ErrorLevel := Error
    return returnValue
}
Covered by Lexikos' default copyright license.

Example 1:
Code:
ShowHTMLDialog("http://www.autohotkey.com/forum/faq.php", "", "dialogWidth:800px;resizable:yes")

Example 2:
Demonstrates how to use a Scripting.Dictionary object to load HTML from a variable and pass multiple named arguments to the dialog.
Code:
html =
(
<HEAD><TITLE>Example Dialog</TITLE></HEAD>
<BODY>
  <INPUT TYPE=text ID="textbox"><BR>
  <BUTTON ID="okButton">Okay</BUTTON><BR>
  <SCRIPT FOR="window" EVENT="onload">
    textbox.value = dialogArguments.Item('DefaultText');
    textbox.select();
  </SCRIPT>
  <SCRIPT FOR="okButton" EVENT="onclick">
    window.returnValue = textbox.value;
    window.close();
  </SCRIPT>
</BODY>
)

COM_Init()
args := COM_ActiveXObject("Scripting.Dictionary")
COM_Invoke(args, "Item=", "HTML", html)
COM_Invoke(args, "Item=", "DefaultText", "default text!")

text := ShowHTMLDialog("javascript:dialogArguments.Item('HTML')", "+" args, "dialogWidth:150px;dialogHeight:50px")
if text !=
    MsgBox % text

COM_Release(args)
COM_Term()

Notes:
It isn't necessary to call COM_CoInitialize() (or COM_Init()) on script start-up, but doing so would probably improve performance (if you call ShowHTMLDialog more than once), since the function would otherwise initialize and free the COM libraries each time the function is called.

The same goes for urlmon.dll and mshtml.dll. (LoadLibrary is used inside the function because urlmon must remain loaded for the URL moniker to persist in memory after CreateURLMonikerEx returns.)

Useful flags:

HTMLDLG_NOUI (0x10)
Quote:
Open the dialog box without a user interface. The dialog box is instantiated and can run scripts, and so on, but is not visible to the user.
This could be used for determining the size a dialog should be before showing it to the user. If the dialog should never be shown, specifying HTMLDLG_NOUI speeds up the call to ShowHTMLDialogEx considerably.

HTMLDLG_MODELESS (0x40) returns immediately without waiting for the dialog to close. The return value of ShowHTMLDialog (window.returnValue) is not usable in this context.

MSDN states that modeless dialogs are "created in a separate process." This does not seem to be the case.

Please note:
Quote:
At least one of the flags HTMLDLG_MODELESS and HTMLDLG_MODAL must be included in dwDialogFlags. If both are declared, HTMLDLG_MODELESS takes precedence over HTMLDLG_MODAL.


Last edited by Lexikos on December 30th, 2008, 1:36 am, edited 5 times in total.

Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 10th, 2007, 9:22 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
This is fantastic.
Thank you.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 10th, 2007, 10:12 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Can you make it to work from string ?

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 10th, 2007, 10:14 pm 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7503
Location: Australia
I'm not sure if that's possible. The ShowHTMLDialog function accepts an IMoniker:
Quote:
The address of an IMoniker interface from which the HTML for the dialog box is loaded.
It might be possible to create an IMoniker for loading HTML from memory, but I don't yet know how.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 10th, 2007, 10:32 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
I hope you can find out something, it would really be useful.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 10th, 2007, 11:13 pm 
Offline

Joined: February 12th, 2007, 7:54 am
Posts: 2462
majkinetor wrote:
Can you make it to work from string ?

You may still use the trick about:, possibly with some limit of length.
Code:
ShowHTMLDialog("about:This is test.")


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 10th, 2007, 11:49 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
I created MsgBoxx function out of this (exit with ESC or OK). You can tweak it , for now font size, colors etc..

Example
Code:
SetWorkingDir, %A_ScriptDir%
    MsgBoxx("   hello world   `nhej there`nhej")

   msg=
   (
    Volume in drive C has no label.
 Volume Serial Number is E41F-6435

 Directory of C:\

17.09.2007  23:26    <DIR>          1
11.10.2007  00:19             6.198 1.ahk
05.10.2007  23:29    <DIR>          Apache
12.08.2007  13:41    <DIR>          Documents and Settings
08.10.2007  23:46    <DIR>          Downloads
05.10.2007  23:29    <DIR>          php
07.10.2007  16:20    <DIR>          Program Files
11.10.2007  00:19               514 ShowHTMLDialog_test.html
10.10.2007  22:38    <DIR>          WINDOWS
               2 File(s)          6.712 bytes
               7 Dir(s)  36.380.971.008 bytes free
)
     MsgBoxx(msg, "Dir c:\", "pre=1 align=left tfg=blue")
return

#include ShowHtmlDialog.ahk
#include MsgBoxx.ahk


Include
Code:
;v1.0 by majkinetor
;Options:
;    bg      - background
;    fg      - foreground
;    tfg   - title color
;    size   - font size (1-6)
;    pre   - use preformated text

MsgBoxx( pMsg, pTitle="", pOptions="") {

   loop, parse, pOptions, %A_Space%
      j := InStr(A_LoopField, "="), o:=SubStr(A_LoopField, 1, j-1), o_%o% := SubStr(A_LoopField, j+1, StrLen(A_LoopField))

   IfEqual, o_align,,   SetEnv o_align, center
   IfEqual, o_size,,   SetEnv o_size, 4
   IfEqual, o_bg,,      SetEnv o_bg, silver
   ifEqual, pTitle, ,   SetEnv pTitle, %A_ScriptName%

   StringReplace, pMsg, pMsg, <, &lt;, A
   StringReplace, pMsg, pMsg, >, &gt;, A
   if (o_pre)
       pMsg = <pre>%pMsg%</pre>
   else {
      StringReplace, pMsg, pMsg, `n, <BR>, A
      StringReplace, pMsg, pMsg, %A_Space%, &nbsp;, A
   }
   func =
      (
         if (document.all) {
            w = document.all['Table'].offsetWidth ;
            h = document.all['Table'].offsetHeight;
            window.returnValue = 'dialogHeight:' +h + 'px;dialogWidth:' + w + 'px;';
            window.close();   
         }
      )


   loop, 2
   {
      FileDelete, ShowHTMLDialog_test.html
      goSub MsgBox_make

      FileAppend, %html%, ShowHTMLDialog_test.html
      res := ShowHTMLDialog("file:///" A_ScriptDir "\ShowHTMLDialog_test.html", "default value", res ";scroll=0;unadorned:yes")
      func =
   }
   
   return   

 MsgBox_make:
   html =
   (LTrim
      <script language="Javascript">
      function onLoad(){%func%}
      </script>

      <BODY onLoad="onLoad();">
      <TABLE CELLPADDING="5" style="border-style:solid; border-width:3; background:%o_bg%" id="Table"   onkeypress="if (event.keyCode==27)window.close();">
      <TR>
         <TD style="color: %o_tfg%" align="left"><h2>%pTitle%</h><hr></TD>
      </TR>
      <TR>
         <TD style="color: %o_fg%" align="%o_align%"><h%o_size%>%pMsg%</h></TD>
      </TR>
            
      <TR>
         <TD align="center"><INPUT TYPE=button VALUE="Okay" onclick="window.close();"></TD>
      </TR>
   )
  return
}


Image Image

Download
MsgBoxx
ShowHtmlDialog


It would be cool to speed it up a bit though, cuz it calls ShowHtmlDialog 2 times and creates file 2 times - first time to get width and height of the table (and exit imediately), and next time to call ShowHtmlDialog with returned size. Createing a file can definitely be avoided if you can provide the way to send string to your function.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 11th, 2007, 5:07 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7503
Location: Australia
Nice work (apart from the colours, but that's just my opinion. :P)
majkinetor wrote:
It would be cool to speed it up a bit though, cuz it calls ShowHtmlDialog 2 times and creates file 2 times - first time to get width and height of the table (and exit imediately), and next time to call ShowHtmlDialog with returned size. Createing a file can definitely be avoided if you can provide the way to send string to your function.
I've been looking at ways of improving your function. First I changed
Code:
         if (document.all) {
to
Code:
         if (document.all && window.dialogArguments==1) {
and
Code:
      res := ShowHTMLDialog("file:///" A_ScriptDir "\ShowHTMLDialog_test.html", "default value", res ";scroll=0;unadorned:yes")
to
Code:
      res := ShowHTMLDialog("file:///" A_ScriptDir "\ShowHTMLDialog_test.html", A_Index, res ";scroll=0;unadorned:yes")
This way you can create the file only once - the JavaScript will decide what to do based on the argIn parameter / window.dialogArguments.

I benchmarked it by adding window.close() to the second iteration, with 20 iterations of msg = dir results, MsgBoxx(). The results still varied a bit (and windows occasionally bugged out; see below) so I changed it to 5 * 20 iterations (and discarded bugged results.)

With and without the above optimization, the result was around 260 ms. Adding
Code:
COM_CoInitialize()
to the start of the script lowered the average (for 5*20 iterations) to around 175 ms.

Occasionally (with or without either of the above optimizations) a window will open entirely blank and not automatically close. I guess this is because the file handle is still open/locked. Yet another drawback of only being able to work with files...


Small note about code boxes and Copy:
Code:
   StringReplace, pMsg, pMsg, <, &lt;, A
   StringReplace, pMsg, pMsg, >, &gt;, A
gets transformed to
Code:
   StringReplace, pMsg, pMsg, <, <, A
   StringReplace, pMsg, pMsg, >, >, A
It had me wondering why you were replacing < with <. :x


Edit: I've added a Flags parameter to the function. If specified, ShowHTMLDialogEx is called instead of ShowHTMLDialog. The most useful flag (for majkinetor ;)) is HTMLDLG_NOUI (0x10)
Quote:
Open the dialog box without a user interface. The dialog box is instantiated and can run scripts, and so on, but is not visible to the user.
Specifying HTMLDLG_NOUI for the first iteration (which determines the required dialog size and automatically closes) cuts off around 80 ms. 8)


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 11th, 2007, 8:24 am 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Quote:
Small note about code boxes and Copy

rofl

Quote:
I've been looking at ways of improving your function.

Indeed much better.

Quote:
I've added a Flags parameter to the function

Thx, that will definitely help.


Now, the only thing left is to see if strings can be used (again).

BTW, this makes me wonder - is it possible now to monitor what is going on in IE page via COM, for instance if button is clicked or key is pressed. With ShowHTMLDialog (SHD), the one can create entire interface, lets say some conifguration, and all values entered by the user can be handled in button click js event with SHD function returning some longer string holding all values/choices entered by the user. The next step would be to react while the "form" is still there, for instance as soon as user changes the combo box selection.

Thx.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 11th, 2007, 8:31 am 
Offline

Joined: July 12th, 2005, 1:21 pm
Posts: 633
Very nice!
I think I've to dig into, because it works to show remote html also over our router (URLDownloadToFile causes a scriptfreeze because of high or missing timeout).

Thanks for that!
Thalon

_________________
AHK-Icon-Changer
AHK-IRC
deutsches Forum


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 11th, 2007, 10:12 am 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7503
Location: Australia
majkinetor wrote:
BTW, this makes me wonder - is it possible now to monitor what is going on in IE page via COM, for instance if button is clicked or key is pressed.
Yes. See COM Event Handler, or my recent example: Handling Individual Events. The modality of the dialog might be a problem.
Quote:
The next step would be to react while the "form" is still there, for instance as soon as user changes the combo box selection.
I have thought about this. The reason I posted ShowHTMLDialog is that I saw it's potential - I plan to eventually try coding an object implementing IDispatch (in AutoHotkey) for invoking (ahk) script functions. This object could then be passed to the dialog via argIn / window.dialogArguments. JavaScript might look like:
Code:
function button1_onclick() {
  window.dialogArguments.UpdateSettings(textbox1.value, textbox2.value);
}
...and AutoHotkey:
Code:
UpdateSettings(ptext1, ptext2) {
  ; get string from string pointer...
   ; etc.
}

I'm not sure how well that would work (in terms of convenience) given that the parameters would be received as UInts if dynamic function calling is used. (I know you can manually get the string from its address.) Perhaps the JavaScript could instead pass an object (pointer) which AutoHotkey can use with Invoke().

I'm not aware of any object that provides access to the (java)script's global variables, though the document itself provides access to all of its HTML elements.

Edit: Actually, global variables seem to be accessible via the window object. I have yet to test this from AutoHotkey, though.
Edit2: Confirmed: variable set in JavaScript
Code:
var myFoo = "myFoo";
can be retrieved via
Code:
COM_Invoke(pWin, "myFoo")
where pWin is a window object.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 11th, 2007, 10:46 am 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
Quote:
I plan to eventually try coding an object implementing IDispatch (in AutoHotkey)

You mean with several RegisterCallbacks and manualy fililng the VTable struct with returned addresses ?

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 11th, 2007, 11:11 am 
Offline

Joined: March 16th, 2005, 10:33 pm
Posts: 969
Location: Frisia
Great stuff! 8)

_________________
Image mirror 1mirror 2mirror 3ahk4.me • PM or Image


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 11th, 2007, 1:16 pm 
Offline

Joined: October 17th, 2006, 4:15 pm
Posts: 7503
Location: Australia
majkinetor wrote:
Quote:
I plan to eventually try coding an object implementing IDispatch (in AutoHotkey)

You mean with several RegisterCallbacks and manualy fililng the VTable struct with returned addresses ?
Yes. Sean has already done this for ConnectObject(), which I used as a base for IDispatch4Callback(). ConnectObject is used to handle all events of an object, while IDispatch4Callback can be used to handle individual events. (IDispatch4Callback wraps a callback within an IDispatch interface.)

I think it's just a matter of:
  • thinking up the design
    • what will be accessible
    • how this will be defined
    • how to handle parameters (for instance, it could pass VARIANTs and let the function take care of interpreting them, or it could coerce everything into strings, but still require the function to convert pointer -> string)
  • implementing GetIDsOfNames - return ID for each named member, and
  • implementing Invoke - invoke a method/property given its ID.


Report this post
Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: October 11th, 2007, 1:30 pm 
Offline

Joined: May 24th, 2006, 2:49 pm
Posts: 4511
Location: Belgrade
I can't help much cuz my only experience with COM is from AHK :)
But it sounds like a mandatory thing to have.

_________________
Image


Report this post
Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 23 posts ]  Go to page 1, 2  Next

All times are UTC [ DST ]


Who is online

Users browsing this forum: specter333 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