_NetWebView2_ExecuteScript() added iMode#49
Conversation
|
Please check this cases: ; example 1
ConsoleWrite($_g_oWeb.ExecuteScriptWithResult('console.log("Hello world!");') & @CRLF)
MsgBox($MB_TOPMOST, "TEST #" & @ScriptLineNumber, $_g_oWeb.ExecuteScriptWithResult('console.log("Hello world v1");'))
; example 2
ConsoleWrite("Log: " & $_g_oWeb.ExecuteScriptWithResult('var x = "Hello world"; x;') & @CRLF)
MsgBox($MB_TOPMOST, "TEST #" & @ScriptLineNumber, $_g_oWeb.ExecuteScriptWithResult('var x = "Hello world v2"; x;'))
; example 3
Local $sText = $_g_oWeb.ExecuteScriptWithResult("var x = extractPDFText(); x;")
MsgBox($MB_TOPMOST, "Text from PDF v1", $sText)
; example 4
$sText = $_g_oWeb.ExecuteScriptWithResult("extractPDFText();")
MsgBox($MB_TOPMOST, "Text from PDF v2", $sText)
I wonder why not all of them work as I'd expect. I mean, they just example 2 give some kind of result. |
|
Converted to DRAFT as it needs to talk and eventually fix - before merge. |
|
Hi @mlipok, the reason some examples return empty strings lies in how JavaScript evaluation works in WebView2 and how the results are serialized. Example 1: console.log() returns undefined $_g_oWeb.ExecuteScriptWithResult('console.log("Hello world!");')Why it 'fails': In JavaScript, the console.log() function returns undefined. ExecuteScriptAsync (and our wrapper) captures the return value of the script. Since console.log only sends data to the DevTools console and returns nothing to the engine, you get an empty result in AutoIt. Example 2: The "Last Expression" Rule (Works!) $_g_oWeb.ExecuteScriptWithResult('var x = "Hello world"; x;')Why it works: JavaScript returns the value of the last expression evaluated. Since x; is the last line, its value is passed back to C# and then to AutoIt. Why you're getting empty results. In the Static_PDF_Viewer.au3 example? The function extractPDFText() is designed as an Asynchronous Event-based function, not a synchronous return function. How it works now: When you call extractPDFText(), it processes the PDF in the background and then triggers the OnMessageReceived event in AutoIt with the prefix PDF_TEXT|. It does not return the string directly to the script caller. Why Example 3 & 4 fail: Since the JS function doesn't have a return statement (it uses postMessage instead), ExecuteScriptWithResult correctly receives 'null/undefined'. If you want to use ExecuteScriptWithResult to get the text directly, we would need to refactor the JS helper to be a synchronous return, but since PDF parsing is inherently asynchronous in pdf.js, the Event-based approach (which is already working in the example) is the stable way to go. Check your __EVENTS_Bridge_OnMessageReceived function in the example, that is where your PDF text actually arrives! ; ❌ This returns nothing because the JS function uses postMessage internally
Local $sVoid = $_g_oWeb.ExecuteScriptWithResult("extractPDFText();")
; ✅ This is where the data actually goes (check your console/logs)
Func __EVENTS_Bridge_OnMessageReceived($sMsg)
If StringInStr($sMsg, "PDF_TEXT|") Then
ConsoleWrite("Data received via Event: " & $sMsg & @CRLF)
EndIf
EndFunc |
Found some answer here: |
|
The explanations for Examples 2, 3, and 4 are clear enough. |
|
Hi @mlipok, regarding Example 1 and the WebView2 feedback link: The 'problem' is that ExecuteScriptAsync only returns the result of the expression evaluation, while the DevTools console is a separate environment that hooks into the browser's stdout. Can we make it work? Yes, but it requires 'Monkey Patching' the console object. If you really need to capture console.log output directly into an AutoIt variable via ExecuteScriptWithResult, you could inject this script during initialization: // Monkey patch console.log to buffer messages
window.console.logBuffer = [];
const originalLog = console.log;
console.log = function() {
window.console.logBuffer.push(Array.from(arguments).join(' '));
originalLog.apply(console, arguments);
};
// Then in AutoIt, to get the logs:
// $_g_oWeb.ExecuteScriptWithResult("var logs = console.logBuffer.join('\\n'); console.logBuffer = []; logs;")My Recommendation: Unless you are building a custom Debugger, it’s usually better to: |
All is clear |
|
I found it Semiotically in v1.4.2 I had made a js Debuger #include "NetWebView2Lib.au3"
Global $oWebV2M, $oBridge
_Example_Console_Redirect()
Func _Example_Console_Redirect()
Local $hGUI = GUICreate("Console Redirect Test", 400, 300)
; 1. Initialize WebView2
$oWebV2M = _NetWebView2_CreateManager()
_NetWebView2_Initialize($oWebV2M, $hGUI, @TempDir & "\NVW2_Test", 0, 0, 0, 0)
; 2. Get Bridge and Register Events
$oBridge = $oWebV2M.GetBridge()
ObjEvent($oBridge, "BridgeEvents_", "IBridgeEvents")
; 3. Prepare the Core Bridge JS (The snippet we cleaned up)
Local $sCoreJS = _Get_Core_Bridge_JS()
; 4. Inject the bridge
$oWebV2M.AddInitializationScript($sCoreJS)
GUISetState(@SW_SHOW)
; navigate to the page
_NetWebView2_Navigate($oWebV2M, "about:blank")
; 6. TEST: Execute a console.log
; Note: We don't use ExecuteScriptWithResult here because
; the data will come back through the Bridge Event!
$oWebV2M.ExecuteScript("console.log('Hello from JavaScript to AutoIt Console!');")
$oWebV2M.ExecuteScript("console.error('This is a test error message');")
While GUIGetMsg() <> -3
WEnd
_NetWebView2_CleanUp($oWebV2M, $oBridge)
EndFunc
; This function handles the incoming messages from the JS Bridge
Func BridgeEvents_OnMessageReceived($sMsg)
; Check if it's a JSON message from our Bridge
If StringLeft($sMsg, 1) = "{" Then
; For simplicity in this example, we just print the raw message
; In a real app, you would parse the JSON to get .message and .level
ConsoleWrite(">>> BRIDGE MESSAGE: " & $sMsg & @CRLF)
EndIf
EndFunc
Func _Get_Core_Bridge_JS()
Local $sJS = ""
$sJS &= "/**" & @CRLF
$sJS &= " * NetWebView2Lib Core Bridge" & @CRLF
$sJS &= " * Handles Console Hijacking and Global Error Reporting" & @CRLF
$sJS &= " */" & @CRLF
$sJS &= "" & @CRLF
$sJS &= "(function() {" & @CRLF
$sJS &= " // 1. Configuration & State" & @CRLF
$sJS &= " window.NET_BRIDGE_ENABLED = true;" & @CRLF
$sJS &= "" & @CRLF
$sJS &= " /**" & @CRLF
$sJS &= " * Centralized message dispatcher to AutoIt" & @CRLF
$sJS &= " */" & @CRLF
$sJS &= " const dispatchToAutoIt = (data) => {" & @CRLF
$sJS &= " try {" & @CRLF
$sJS &= " if (window.chrome && window.chrome.webview) {" & @CRLF
$sJS &= " window.chrome.webview.postMessage(JSON.stringify(data));" & @CRLF
$sJS &= " }" & @CRLF
$sJS &= " } catch (e) {" & @CRLF
$sJS &= " // Silent fail if bridge is not fully ready" & @CRLF
$sJS &= " }" & @CRLF
$sJS &= " };" & @CRLF
$sJS &= "" & @CRLF
$sJS &= " /**" & @CRLF
$sJS &= " * Console Hijacking Logic" & @CRLF
$sJS &= " */" & @CRLF
$sJS &= " const originalConsole = {" & @CRLF
$sJS &= " log: console.log," & @CRLF
$sJS &= " error: console.error," & @CRLF
$sJS &= " warn: console.warn," & @CRLF
$sJS &= " info: console.info" & @CRLF
$sJS &= " };" & @CRLF
$sJS &= "" & @CRLF
$sJS &= " const createWrappedConsole = (type) => {" & @CRLF
$sJS &= " return function() {" & @CRLF
$sJS &= " // Send to AutoIt" & @CRLF
$sJS &= " dispatchToAutoIt({" & @CRLF
$sJS &= " type: ""CONSOLE_LOG""," & @CRLF
$sJS &= " level: type.toUpperCase()," & @CRLF
$sJS &= " message: Array.from(arguments).map(arg => " & @CRLF
$sJS &= " (typeof arg === 'object') ? JSON.stringify(arg) : String(arg)" & @CRLF
$sJS &= " ).join(' ')," & @CRLF
$sJS &= " timestamp: new Date().toISOString()" & @CRLF
$sJS &= " });" & @CRLF
$sJS &= " // Keep original browser behavior" & @CRLF
$sJS &= " originalConsole[type].apply(console, arguments);" & @CRLF
$sJS &= " };" & @CRLF
$sJS &= " };" & @CRLF
$sJS &= "" & @CRLF
$sJS &= " // Replace standard console methods" & @CRLF
$sJS &= " console.log = createWrappedConsole('log');" & @CRLF
$sJS &= " console.error = createWrappedConsole('error');" & @CRLF
$sJS &= " console.warn = createWrappedConsole('warn');" & @CRLF
$sJS &= " console.info = createWrappedConsole('info');" & @CRLF
$sJS &= "" & @CRLF
$sJS &= " /**" & @CRLF
$sJS &= " * 2. Global Runtime Error Handler" & @CRLF
$sJS &= " */" & @CRLF
$sJS &= " window.onerror = function(message, source, lineno, colno, error) {" & @CRLF
$sJS &= " dispatchToAutoIt({" & @CRLF
$sJS &= " type: ""JS_ERROR""," & @CRLF
$sJS &= " message: message," & @CRLF
$sJS &= " source: source," & @CRLF
$sJS &= " line: lineno," & @CRLF
$sJS &= " column: colno," & @CRLF
$sJS &= " stack: error ? error.stack : """"" & @CRLF
$sJS &= " });" & @CRLF
$sJS &= " return false; // Let browser handle it as well" & @CRLF
$sJS &= " };" & @CRLF
$sJS &= "" & @CRLF
$sJS &= " // Signal that bridge is active" & @CRLF
$sJS &= " dispatchToAutoIt({ type: ""SYSTEM"", message: ""Core Bridge Injected"" });" & @CRLF
$sJS &= "})();"
Return $sJS
EndFunc |
Important
Thanks for your effort and interest 💛 in improving the project. It's very appreciated.
Description
Adding $iMode to have possibility to use different JS executions modes
🔗 Linked GitHub Issues
#45 (comment)
Closes #\45
📋 What is the current behavior?
you can only use ExecuteScript (Fire-and-Forget)
🚀 What is the new behavior?
all 3 modes are supported
ExecuteScript (Fire-and-Forget)
ExecuteScriptOnPage (Async-Void Trap)
ExecuteScriptWithResult
Type of changes
Breaking changes 🔥
How and where was this tested?
🖥️ Describe where you tested your changes
Not Related
🔬 Describe how you tested your changes
Checklist
Additional context
#45
Screenshots
none
Note to reviewers
none