Buona sera, in qusti tempi difficili, rimanendo a casa per molte ore mi sono potuto riavvicinare al tipo di programmazione che a me piace... saluto tutti e vengo al 3d
...comprendo che l'oggetto sicuramente apparirà criptico e mi scuso in anticipo nel caso non avessi centrato pienamente il sub forum più adatto per gestire il problema in quanto gli argomenti trattati spaziano tra HTML, javascript e in gran parte VBA.
Sto cercando di automatizzare la lettura di alcune pagine web in modo da poter scrivere i dati letti all'interno di tabelle web "<table>", in altrettante tabelle di access.
L'automazione avviene mediante l'uso di un oggetto Internet Explorer in una routine VBA che mi legge il codice HTML delle pagine WEB per poi processarlo.
La struttura DOM delle pagine che leggo ha degli <iframe> annidati (per chi non lo sapesse l'<iframe> mostra al suo interno un'altra pagina web) nei quali riesco a districarmi senza fatica nel leggere e processare il codice HTML.
...e tutto si è svolto sempre in maniera egregia finché non mi sono trovato davanti una situazione particolare che mi sta mettendo in difficolta.
Cercherò di descriverla:
Nel browser Internet Explorer ho una pagina Web aperta che al suo interno ha un pulsante che mi apre una nuova finestra di tipo modale, che al suo interno ha un <iframe> che ne visualizza un'altra ancora (… che bellezza…).
Riesco a leggere sia il codice HTML della pagina principale che quello della modale ma non riesco ad accedere a quello della pagina visualizzata nell' <iframe> ed è proprio quello che mi servirebbe... la situazione è diversa da quella di routine nella quale mi districo bene in quanto normalmente non ho mai a che fare con delle finestre di tipo modale che, a quanto pare, sono rognose.
Se qualche anima pia è desiderosa di aiutarmi fornisco di seguito gli elementi per simulare lo scenario (ma fate una pausa prima :-) )
Codice HTML della pagina "index html" da salvare sul proprio desktop;
<head>
<script type="text/javascript">
function UpdateFields (newFore, newSur) {
var forename = document.getElementById ("forename");
var surname = document.getElementById ("surname");
forename.value = newFore;
surname.value = newSur;
}
function ShowModal () {
var forename = document.getElementById ("forename");
var surname = document.getElementById ("surname");
var sharedObject = {};
sharedObject.forename = forename.value;
sharedObject.surname = surname.value;
if (window.showModalDialog) {
var retValue = showModalDialog ("modal.htm", sharedObject, "dialogHeight=600px; dialogWidth=200px;edge=sunken;help=no;resizable=yes;status=no;scroll:no");
if (retValue) {
UpdateFields (retValue.forename, retValue.surname);
}
}
else {
// for similar functionality in Opera, but it's not modal!
var modal = window.open ("modal.htm", null, "dialogHeight=600px;dialogWidth=px;edge=sunken;help=no;resizable=yes;status=no;scroll:no", null);
modal.dialogArguments = sharedObject;
}
}
</script>
</head>
<body>
Forename: <input type="text" id="forename" value="Alan"/><br/>
Surname: <input type="text" id="surname" value="Smith"/>
<br/><br/>
<button onclick="ShowModal ()">Edit the fields with a modal dialog!</button>
</body>
Codice HTML della pagina "modal.htm" da salvare sul proprio desktop;
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Modal dialog sample</title>
<script type="text/javascript">
function Init () {
var sharedObject = window.dialogArguments;
var forename = document.getElementById ("forename");
var surname = document.getElementById ("surname");
forename.value = sharedObject.forename;
surname.value = sharedObject.surname;
}
function OnOK () {
var forename = document.getElementById ("forename");
var surname = document.getElementById ("surname");
if (window.showModalDialog) {
var sharedObject = {};
sharedObject.forename = forename.value;
sharedObject.surname = surname.value;
window.returnValue = sharedObject;
}
else {
// if not modal, we cannot use the returnValue property, we need to update the opener window
window.opener.UpdateFields (forename.value, surname.value);
}
window.close ();
}
function OnCancel () {
window.close ();
}
</script>
<body onload="Init ();">
Forename: <input type="text" id="forename"/><br/>
Surname: <input type="text" id="surname"/>
<br/><br/>
<button onclick="OnOK ()">OK</button> <button onclick="OnCancel ()">Cancel</button>
<iframe id="nomeID" name="nomeID" src="index2.html" scrolling="no" align="left" MARGINHEIGHT="0" MARGINWIDTH="0" frameborder="0" width="100%" height="100%"></iframe>
</body>
</html>
Codice HTML della pagina "index2.html" da salvare sul proprio desktop (in realtà questa può essere una qualsiasi pagina.. è ininfluente);
<html>
<script language="javascript">
function openwindow()
{
retval=window.showModalDialog("pagina.htm")
document.getElementById('text1').value=retval
}
</script>
<body>
<form name=frm>
<input name=text1 type=text>
<input type=button onclick="javascript:openwindow()" value="Open window..">
</form>
</body>
</html>
Codice VBA da scrivere nel modulo di una form access nella quale posizionerete un pulante chiamato 'Comando_0'
Option Explicit
' Requires: VBA project reference to "Microsoft HTML Object Library"
'https://stackoverflow.com/questions/54318737/automating-the-html-document-in-an-ie-web-dialog-window
Private Const SMTO_ABORTIFHUNG = &H2
Private Const GW_CHILD = 5
Private Const GW_HWNDNEXT = 2
Private Type UUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(0 To 7) As Byte
End Type
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
(ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" _
(ByVal hWnd As Long) As Long
Private Declare Function GetWindow Lib "user32" _
(ByVal hWnd As Long, ByVal wCmd As Long) As Long
Private Declare Function IsWindowVisible Lib "user32" _
(ByVal hWnd As Long) As Boolean
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" _
(ByVal hWnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Private Declare Function RegisterWindowMessage Lib "user32" _
Alias "RegisterWindowMessageA" (ByVal lpString As String) As Long
Private Declare Function SendMessageTimeout Lib "user32" _
Alias "SendMessageTimeoutA" ( _
ByVal hWnd As Long, _
ByVal msg As Long, _
ByVal wParam As Long, _
lParam As Any, _
ByVal fuFlags As Long, _
ByVal uTimeout As Long, _
lpdwResult As Long) As Long
Private Declare Function ObjectFromLresult Lib "oleacc" ( _
ByVal lResult As Long, _
riid As UUID, _
ByVal wParam As Long, _
ppvObject As Any) As Long
Sub DialogDemo()
Const DLG_TITLE = "Modal dialog sample" '<< the dialog title
Dim Doc As IHTMLDocument
Set Doc = GetIEDialogDocument(DLG_TITLE)
If Not Doc Is Nothing Then
Debug.Print Doc.body.innerHTML
Else
MsgBox "Dialog Window '" & DLG_TITLE & "' was not found!", vbOKOnly + vbExclamation
End If
End Sub
'Given an IE dialog window title, find the window and return a reference
' to the embedded HTML document object
Function GetIEDialogDocument(dialogTitle As String) As IHTMLDocument
Dim lhWndP As Long, lhWndC As Long, Doc As IHTMLDocument
'find the IE dialog window given its title
If GetHandleFromPartialCaption(lhWndP, dialogTitle) Then
Debug.Print "Found dialog window - " & dialogTitle & "(" & TheClassName(lhWndP) & ")"
lhWndC = GetWindow(lhWndP, GW_CHILD) 'Find Child
If lhWndC > 0 Then
If TheClassName(lhWndC) = "Internet Explorer_Server" Then
Debug.Print "HWND PARENT: ", lhWndP
Debug.Print "HWND CHILD: ", lhWndC
Set Doc = IEDOMFromhWnd(lhWndC)
End If
End If
Else
Debug.Print "Window '" & dialogTitle & "' not found!"
End If
Set GetIEDialogDocument = Doc
End Function
' IEDOMFromhWnd
' Returns the IHTMLDocument interface from a WebBrowser window
' hWnd - Window handle of the control
Function IEDOMFromhWnd(ByVal hWnd As Long) As IHTMLDocument
Dim IID_IHTMLDocument As UUID
Dim hWndChild As Long
Dim lRes As Long
Dim lMsg As Long
Dim hr As Long
If hWnd <> 0 Then
lMsg = RegisterWindowMessage("WM_HTML_GETOBJECT") ' Register the message
SendMessageTimeout hWnd, lMsg, 0, 0, SMTO_ABORTIFHUNG, 1000, lRes ' Get the object pointer
If lRes Then
With IID_IHTMLDocument ' Initialize the interface ID
.Data1 = &H626FC520
.Data2 = &HA41E
.Data3 = &H11CF
.Data4(0) = &HA7
.Data4(1) = &H31
.Data4(2) = &H0
.Data4(3) = &HA0
.Data4(4) = &HC9
.Data4(5) = &H8
.Data4(6) = &H26
.Data4(7) = &H37
End With
' Get the object from lRes (note - returns the object via the last parameter)
hr = ObjectFromLresult(lRes, IID_IHTMLDocument, 0, IEDOMFromhWnd)
End If
End If 'hWnd<>0
End Function
'utilty function for getting the classname given a window handle
Function TheClassName(lhWnd As Long)
Dim strText As String, lngRet As Long
strText = String$(100, Chr$(0))
lngRet = GetClassName(lhWnd, strText, 100)
TheClassName = Left$(strText, lngRet)
End Function
Private Function GetHandleFromPartialCaption(ByRef lWnd As Long, _
ByVal sCaption As String) As Boolean
Dim lhWndP As Long, sStr As String
GetHandleFromPartialCaption = False
lhWndP = FindWindow(vbNullString, vbNullString) 'PARENT WINDOW
Do While lhWndP <> 0
sStr = String(GetWindowTextLength(lhWndP) + 1, Chr$(0))
GetWindowText lhWndP, sStr, Len(sStr)
sStr = Left$(sStr, Len(sStr) - 1)
If Len(sStr) > 2 Then
If UCase(sStr) Like "*ARG*" Then Debug.Print sStr
End If
If InStr(1, sStr, sCaption) > 0 Then
GetHandleFromPartialCaption = True
lWnd = lhWndP
Exit Do
End If
lhWndP = GetWindow(lhWndP, GW_HWNDNEXT)
Loop
End Function
Private Sub Comando0_Click()
DialogDemo
End Sub
all'inizio del modulo della form ho scritto i riferimenti web dove ho preso il codice e i riferimenti necessari
Procedimento di simulazione:
1. Creare i file "index.html", "modal.htm" e "index2.html" (va bene anche il notepad purché quando salvate con nome scriviate "nome.html" compresi i doppi apici.
2. tasto destro su index.html e scegliere apri con -> internet explorer (E' importante aprire con IE perchè è con questo browser che ho problemi e non posso usarne altri);
3. se richiesto dalla pagina, abilitare il contenuto activex;
4. cliccare sul pulsante "edit the filed...." e si aprira la finestra modale incriminata che a video (maledetta lei!!!) mostra correttamente anche il contenuto dell'iframe.
5. non chiudere internet explorer e, dopo aver aperto access, premere il pulsante Command_0 della form di access.
output del debug che ottengo quando premo il pulsante Command_0
Window 'Modal dialog sample' not found!
Found dialog window - Modal dialog sample(Internet Explorer_TridentDlgFrame)
HWND PARENT: 3934384
HWND CHILD: 2689912
Forename: <input id="forename" type="text"><br>
Surname: <input id="surname" type="text">
<br><br>
<button onclick="OnOK ()">OK</button> <button onclick="OnCancel ()">Cancel</button>
<iframe name="frameWinModale_contenitore" width="100%" height="100%" align="center" id="frameWinModale_contenitore" src="index2.html" frameborder="0" marginwidth="0" marginheight="0" scrolling="yes" bgcolor="#F0F8FF">
</iframe>
come potete notare la parte finale dell'output è quella dell'<iframe> in cui si evince il riferimento alla pagina index.html2 ma non ne mostra il contenuto quindi ho pensato che se riuscissi a passare la finestra modale per intero in una nuova istanza di internet explorer forse riuscirei a districarmi nuovamente bene in quanto in teoria non dovrebbe più essere di tipo modale.
Nel debug sono mostrati i HWND della finestra IE parent e HWND di quella modale quindi se esistesse un modo di sfruttare il 'numero magico' per far passare la finestra modale in una nuova istanza IE forse risolvereì...
ho provato con 'SetWindowPos' e "showWindow' ma non ho cavato un ragno dal buco... qualcuni riesce a illuminarmi ?... un piccolo suggerimento?