Skip to content

Commit 5394db1

Browse files
authored
v2.0.0-beta.3
1 parent fea0810 commit 5394db1

17 files changed

Lines changed: 685 additions & 415 deletions

NetWebView2Lib.au3

Lines changed: 40 additions & 39 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -63,45 +63,51 @@ This project is provided "as-is". You are free to use, modify, and distribute it
6363
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" width="100%">
6464
</p>
6565

66-
## 🚀 What's New in v2.0.0-beta.1 - Multi-Instance & Stability Focus
66+
## 🚀 What's New in v2.0.0-beta.3 - AcceleratorKey Detail & Refactoring
6767

68-
This major beta release re-engineers the core event architecture to support professional multi-instance applications and solves long-standing COM deadlock issues with pre-emptive key blocking.
68+
This update focuses on deep keyboard interception and code modularity.
6969

7070
### ⚡ Key Features & Enhancements
7171

72-
#### **1. Sender-Aware Event Architecture**
72+
#### **1. PhysicalKeyStatus Expansion**
73+
The `OnAcceleratorKeyPressed` event now provides 1:1 access to the underlying Windows keyboard state.
74+
- **New Properties**: `RepeatCount`, `ScanCode`, `IsExtendedKey`, `IsMenuKeyDown`, `WasKeyDown`, and `IsKeyReleased`.
75+
- **Use Case**: Detect held keys, distinguish between left/right Alt/Ctrl, and implement complex hotkey logic without Win32 API calls.
7376

74-
The entire event system has been refactored for multi-instance reliability. Every event fired from the C# core now provides a "Sender" context.
77+
#### **2. Performance & Modularity**
78+
- **Standalone Argument Logic**: Argument wrappers have been moved to dedicated files (e.g., `WebView2AcceleratorKeyPressedEventArgs.cs`).
79+
- **Lean Core**: Reduced `WebViewManager.cs` complexity by outsourcing event data structures.
7580

76-
- **Instance Isolation**: AutoIt scripts can now easily distinguish which WebView2 instance triggered an event using the `parentHandle` parameter.
77-
- **Sealed Contexts**: The communication bridge is now sealed to its parent manager, preventing message cross-talk in complex multi-window setups.
78-
79-
#### **2. Pre-emptive Key Blocking (Zero Deadlocks)**
81+
---
8082

81-
We have abandoned the legacy synchronous polling for accelerator keys in favor of a proactive, list-based approach.
83+
## 🚀 What's New in v2.0.0-beta.2 - COM Versioning & Handle Alignment
8284

83-
- **`BlockedVirtualKeys`**: Define a list of VK codes (like F5, F12) to be blocked directly by the C# engine.
84-
- **Performance**: Eliminates the 600ms sync-wait loops, resulting in instant response times and 0% risk of COM re-entrancy deadlocks.
85-
- **OnAcceleratorKeyPressed**: Still available for monitoring, but the "Handled" logic is now pre-emptively managed by the property list.
85+
This update introduces the ability to query the DLL version directly and aligns window handles with AutoIt's native format.
8686

87-
#### **3. Native Win32 Integration**
87+
### ⚡ Key Features & Enhancements
8888

89-
- **`BrowserWindowHandle`**: A new property that exposes the internal `HWND` of the browser control. This allows for advanced parent-child window relationship management and direct Win32 message interception via DLL calls.
89+
#### **1. Advanced Handle Formatting (`[HANDLE:0x...]`)**
90+
All window handles returned by the library (via properties or events) are now formatted as strings compatible with AutoIt's Advanced Window Descriptions.
91+
- **Direct Compatibility**: Handles like `[HANDLE:0x00010203]` can be passed directly to `WinExists`, `WinSetTitle`, etc., without `HWnd()` conversion.
9092

91-
#### **4. Internal Messaging Overlay**
93+
#### **2. COM Version Exposure**
94+
You can now access the `.version` property on all primary COM objects.
95+
- **`Manager.version`**, **`Bridge.version`**, **`Parser.version`**.
9296

93-
- **Improved Stability**: A new internal messaging system ensures that background state updates never interfere with the primary UI event loop, ensuring a smoother user experience during heavy navigation sequences.
97+
#### **3. Stabilized Infrastructure**
98+
- **GUID Collision Resolution**: Fixed internal interface IDs that caused registration issues in v2.0.0-beta.1.
99+
- **`ParentWindowHandle`**: New property to retrieve the handle passed during initialization.
94100

95101

96102
<p align="center">
97103
<img src="https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png" width="100%">
98104
</p>
99105

100-
## 📖 NetWebView2Lib Version 2.0.0-beta.1 (Quick Reference)
106+
## 📖 NetWebView2Lib Version 2.0.0-beta.3 (Quick Reference)
101107

102108
### NetWebView2Lib (ProgId: NetWebView2.Manager)
103109

104-
#### === Properties ===
110+
#### ===Properties===
105111

106112
##### AreDevToolsEnabled
107113
Determines whether the user is able to use the context menu or keyboard shortcuts to open the DevTools window.
@@ -188,14 +194,18 @@ Control visibility of the browser's default error pages (e.g., connection lost).
188194
`object.IsBuiltInErrorPageEnabled = Value`
189195

190196
##### BrowserWindowHandle
191-
Returns the internal window handle (HWND) of the WebView2 control. This is the child window attached to the parent GUI.
197+
Returns the internal window handle (HWND) of the WebView2 control. [Format: `[HANDLE:0x...]`]
192198
`object.BrowserWindowHandle`
193199

200+
##### ParentWindowHandle
201+
Returns the parent window handle provided during initialization. [Format: `[HANDLE:0x...]`]
202+
`object.ParentWindowHandle`
203+
194204
##### BlockedVirtualKeys
195205
A comma-separated list of Virtual Key codes to be blocked synchronously (e.g., "116,123").
196206
`object.BlockedVirtualKeys = "116,123"`
197207

198-
#### === Method ===
208+
#### ===Method===
199209

200210
##### Initialize
201211
Initializes the WebView2 control within a parent window.
@@ -354,6 +364,7 @@ Enables or disables the Web Message communication system.
354364
##### SetStatusBarEnabled
355365
Enables or disables the browser status bar.
356366
`object.SetStatusBarEnabled(Enabled As Boolean)`
367+
357368

358369
##### CapturePreview
359370
Captures a screenshot of the current view to a file.
@@ -489,7 +500,7 @@ Captures page data using Chrome DevTools Protocol. Can return MHTML or other CDP
489500
`object.CaptureSnapshot(CdpParameters As String)`
490501

491502
##### SetDownloadPath
492-
Sets a global default folder or file path for all browser downloads. If a directory is provided, the filename is automatically appended by the library.
503+
Sets a global default folder or file path for all browser downloads. If a directory is provided, the filename is automatically appended by the library. Create the folder if it doesn't exist
493504
`object.SetDownloadPath(Path As String)`
494505

495506
##### CancelDownloads
@@ -504,7 +515,7 @@ Cancels active downloads. If `uri` is empty or omitted, cancels all active downl
504515
Captures the current page as a PDF and returns the content as a Base64-encoded string.
505516
`object.PrintToPdfStream()`
506517

507-
#### === Events ===
518+
#### ===Events===
508519

509520
##### OnMessageReceived
510521
Fired when a message or notification is sent from the library to AutoIt.
@@ -564,14 +575,21 @@ Fired when an accelerator key is pressed. Allows blocking browser shortcuts.
564575
*Args properties:
565576
VirtualKey (uint): The VK code of the key.
566577
KeyEventKind (int): Type of key event (Down, Up, etc.).
567-
Handled (bool): Set to `True` to stop the browser from processing the key.*
568-
578+
Handled (bool): Set to `True` to stop the browser from processing the key.
579+
RepeatCount (uint): The number of times the key has repeated.
580+
ScanCode (uint): Hardware scan code.
581+
IsExtendedKey (bool): True if it's an extended key (e.g., right Alt).
582+
IsMenuKeyDown (bool): True if Alt is pressed.
583+
WasKeyDown (bool): True if the key was already down.
584+
IsKeyReleased (bool): True if the event is a key up.
585+
KeyEventLParam (int): Gets the LPARAM value that accompanied the window message*
586+
569587

570588
---
571589

572590
### JsonParser (ProgId: NetJson.Parser)
573591

574-
#### === Methods ===
592+
#### ===Methods===
575593

576594
##### Parse
577595
Parses a JSON string. Automatically detects if it's an Object or an Array.

bin/NetWebView2Lib.dll

6.5 KB
Binary file not shown.

bin/NetWebView2Lib.tlb

3.89 KB
Binary file not shown.

examples/001-BasicDemo.au3

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ Func Main()
3333

3434
Local $sProfileDirectory = @ScriptDir & "\NetWebView2Lib-UserDataFolder"
3535
_NetWebView2_Initialize($oWebV2M, $hGUI, $sProfileDirectory, 0, 0, 0, 0, True, True, True, 1.2, "0x2B2B2B")
36+
If @error Then Return SetError(@error, @extended, $oWebV2M)
37+
3638
__Example_Log(@ScriptLineNumber, "After: _NetWebView2_Initialize()")
3739

3840
; navigate to HTML string - full fill the object with your own offline content - without downloading any content

examples/006-DownloadDemo.au3

Lines changed: 111 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#AutoIt3Wrapper_UseX64=y
22
#include <GUIConstantsEx.au3>
33
#include <WindowsConstants.au3>
4-
4+
#include <Misc.au3>
55
#include "..\NetWebView2Lib.au3"
66

77
; 006-DownloadDemo.au3
@@ -35,15 +35,20 @@ Func _Example()
3535

3636
#EndRegion ; GUI CREATION
3737

38+
; BlockedVirtualKeys
39+
; A comma-separated list of Virtual Key codes to be blocked synchronously (e.g., "116,123").
40+
; `object.BlockedVirtualKeys = "116,123"`
41+
42+
; Block F5 (116) and F12 (123) AcceleratorKey!
43+
$oWebV2M.BlockedVirtualKeys = "116,123"
44+
3845
; Silent Download Setting
3946
$oWebV2M.IsDownloadUIEnabled = False
40-
; Set default Download Path
41-
DirCreate(@ScriptDir & "\Downloads_Test")
47+
48+
; Set default Download Path (Create the folder if it doesn't exist)
4249
$oWebV2M.SetDownloadPath(@ScriptDir & "\Downloads_Test")
4350

4451
; navigate to the page
45-
;~ _NetWebView2_Navigate($oWebV2M, "https://www.libreoffice.org/download/download-libreoffice", $NETWEBVIEW2_MESSAGE__TITLE_CHANGED)
46-
4752
_NetWebView2_Navigate($oWebV2M, "https://www.libreoffice.org/donate/dl/win-x86_64/25.8.4/en-US/LibreOffice_25.8.4_Win_x86-64.msi", $NETWEBVIEW2_MESSAGE__NAV_STARTING)
4853

4954
#Region ; GUI Loop
@@ -61,7 +66,8 @@ Func _Example()
6166
_NetWebView2_CleanUp($oWebV2M, $oJSBridge)
6267
EndFunc ;==>_Example
6368

64-
Func __UserEventHandler__OnDownloadStateChanged($oWebV2M, $hGUI, $sState, $sURL, $iTotal_Bytes, $iReceived_Bytes)
69+
; Advise using 'Volatile' for Event Handlers to ensure the WebView2 COM thread can interrupt the main script safely.
70+
Volatile Func __UserEventHandler__OnDownloadStateChanged($oWebV2M, $hGUI, $sState, $sURL, $iTotal_Bytes, $iReceived_Bytes)
6571
#forceref $oWebV2M
6672

6773
$hGUI = HWnd("0x" & Hex($hGUI, 16))
@@ -99,21 +105,108 @@ Func __UserEventHandler__OnDownloadStateChanged($oWebV2M, $hGUI, $sState, $sURL,
99105
EndSwitch
100106
EndFunc ;==>__UserEventHandler__OnDownloadStateChanged
101107

102-
Func __UserEventHandler__OnAcceleratorKeyPressed($oWebV2M, $hGUI, $oArgs)
103-
$hGUI = HWnd("0x" & Hex($hGUI, 16))
104-
Local Const $sArgsList = '[Handled=' & $oArgs.Handled & '; KeyEventKind=' & $oArgs.KeyEventKind & '; KeyEventLParam=' & $oArgs.KeyEventLParam & '; VirtualKey=' & $oArgs.VirtualKey & ']'
105-
Local Const $s_Prefix = "[USER:EVENT: OnAcceleratorKeyPressed]: GUI:" & $hGUI & " ARGS: " & ((IsObj($oArgs)) ? ($sArgsList) : ('ERRROR'))
108+
; Advise using 'Volatile' for Event Handlers to ensure the WebView2 COM thread can interrupt the main script safely.
109+
Volatile Func __UserEventHandler__OnAcceleratorKeyPressed($oWebV2M, $hGUI, $oArgs)
110+
Local Const $sArgsList = '[VirtualKey=' & $oArgs.VirtualKey & _ ; The VK code of the key.
111+
'; KeyEventKind=' & $oArgs.KeyEventKind & _ ; Type of key event (Down, Up, etc.).
112+
'; Handled=' & $oArgs.Handled & _ ; Set to `True` to stop the browser from processing the key.
113+
'; RepeatCount=' & $oArgs.RepeatCount & _ ; The number of times the key has repeated.
114+
'; ScanCode=' & $oArgs.ScanCode & _ ; Hardware scan code.
115+
'; IsExtendedKey=' & $oArgs.IsExtendedKey & _ ; True if it's an extended key (e.g., right Alt).
116+
'; IsMenuKeyDown=' & $oArgs.IsMenuKeyDown & _ ; True if Alt is pressed.
117+
'; WasKeyDown=' & $oArgs.WasKeyDown & _ ; True if the key was already down.
118+
'; IsKeyReleased=' & $oArgs.IsKeyReleased & _ ; True if the event is a key up.
119+
'; KeyEventLParam=' & $oArgs.KeyEventLParam & ']' ; Gets the LPARAM value that accompanied the window message.
120+
121+
Local Const $s_Prefix = "[USER:EVENT: OnAcceleratorKeyPressed]:: GUI:" & $hGUI & " ARGS: " & ((IsObj($oArgs)) ? ($sArgsList) : ('ERROR'))
122+
123+
; Example of checking specific keys
124+
Switch $oArgs.VirtualKey
125+
Case Int($VK_F7)
126+
ConsoleWrite("! Blocked: F7 caret browsing." & @CRLF)
127+
$oArgs.Handled = True
128+
129+
Case Int($VK_N)
130+
; Check if Ctrl is pressed (using _IsPressed for Ctrl as IsMenuKeyDown specifically targets Alt)
131+
; Use this to block shortcuts like Ctrl+N (New Window)
132+
If _IsPressed("11") Then
133+
ConsoleWrite("! Blocked: Ctrl+N shortcut." & @CRLF)
134+
$oArgs.Handled = True
135+
EndIf
136+
137+
Case Int($VK_ESCAPE) ; ESC 27 1b 033 Escape
138+
; Cancels active downloads. If `uri` is empty or omitted, cancels all active downloads.
139+
$oWebV2M.CancelDownloads()
140+
141+
EndSwitch
142+
143+
__NetWebView2_Log(@ScriptLineNumber, $s_Prefix, 0)
144+
145+
$oArgs = 0 ; Explicitly release the COM reference inside the volatile scope
146+
EndFunc ;==>__UserEventHandler__OnAcceleratorKeyPressed
147+
148+
; Advise using 'Volatile' for Event Handlers to ensure the WebView2 COM thread can interrupt the main script safely.
149+
Volatile Func __UserEventHandler__OnAcceleratorKeyPressed00($oWebV2M, $hGUI, $oArgs)
150+
Local Const $sArgsList = '[VirtualKey=' & $oArgs.VirtualKey & _ ; The VK code of the key.
151+
'; KeyEventKind=' & $oArgs.KeyEventKind & _ ; Type of key event (Down, Up, etc.).
152+
'; Handled=' & $oArgs.Handled & _ ; Set to `True` to stop the browser from processing the key.
153+
'; RepeatCount=' & $oArgs.RepeatCount & _ ; The number of times the key has repeated.
154+
'; ScanCode=' & $oArgs.ScanCode & _ ; Hardware scan code.
155+
'; IsExtendedKey=' & $oArgs.IsExtendedKey & _ ; True if it's an extended key (e.g., right Alt).
156+
'; IsMenuKeyDown=' & $oArgs.IsMenuKeyDown & _ ; True if Alt is pressed.
157+
'; WasKeyDown=' & $oArgs.WasKeyDown & _ ; True if the key was already down.
158+
'; IsKeyReleased=' & $oArgs.IsKeyReleased & _ ; True if the event is a key up.
159+
'; KeyEventLParam=' & $oArgs.KeyEventLParam & ']' ; Gets the LPARAM value that accompanied the window message.
160+
161+
162+
Local Const $s_Prefix = "[USER:EVENT: OnAcceleratorKeyPressed]:: GUI:" & $hGUI & " ARGS: " & ((IsObj($oArgs)) ? ($sArgsList) : ('ERRROR'))
163+
164+
;~ public uint VirtualKey { get; }
165+
;~ public int KeyEventLParam { get; }
166+
;~ public int KeyEventKind { get; }
167+
;~
168+
;~ public uint RepeatCount { get; }
169+
;~ public uint ScanCode { get; }
170+
;~ public bool IsExtendedKey { get; }
171+
;~ public bool IsMenuKeyDown { get; }
172+
;~ public bool WasKeyDown { get; }
173+
;~ public bool IsKeyReleased { get; }
106174

107175
;~ https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2acceleratorkeypressedeventargs?view=webview2-dotnet-1.0.705.50
108-
ConsoleWrite($oArgs.Handled & @CRLF) ; Indicates whether the AcceleratorKeyPressed event is handled by host.
109-
ConsoleWrite($oArgs.KeyEventKind & @CRLF) ; Gets the key event kind that caused the event to run
110-
ConsoleWrite($oArgs.KeyEventLParam & @CRLF) ; Gets the LPARAM value that accompanied the window message.
176+
;~ ConsoleWrite($oArgs.Handled & @CRLF) ; Indicates whether the AcceleratorKeyPressed event is handled by host.
177+
;~ ConsoleWrite($oArgs.KeyEventKind & @CRLF) ; Gets the key event kind that caused the event to run
178+
;~ ConsoleWrite($oArgs.KeyEventLParam & @CRLF) ; Gets the LPARAM value that accompanied the window message.
111179
;~ ConsoleWrite('>> PhysicalKeyStatus=' & $oArgs.PhysicalKeyStatus & @CRLF) ; Gets a CoreWebView2PhysicalKeyStatus representing the information passed in the LPARAM of the window message. ==> ; https://learn.microsoft.com/en-us/dotnet/api/microsoft.web.webview2.core.corewebview2physicalkeystatus?view=webview2-dotnet-1.0.705.50
112-
ConsoleWrite($oArgs.VirtualKey & @CRLF) ; Gets the Win32 virtual key code of the key that was pressed or released.
180+
;~ ConsoleWrite($oArgs.VirtualKey & @CRLF) ; Gets the Win32 virtual key code of the key that was pressed or released.
181+
182+
;~ If $oArgs.VirtualKey = 27 Then ; ESC 27 1b 033 Escape, next character is not echoed ; https://www.autoitscript.com/autoit3/docs/appendix/ascii.htm
183+
;~ $oWebV2M.CancelDownloads($_sURLDownload_InProgress)
184+
;~ EndIf
113185

114-
If $oArgs.VirtualKey = 27 Then ; ESC 27 1b 033 Escape, next character is not echoed ; https://www.autoitscript.com/autoit3/docs/appendix/ascii.htm
115-
$oWebV2M.CancelDownloads($_sURLDownload_InProgress)
116-
EndIf
186+
; Example of checking specific keys
187+
Switch $oArgs.VirtualKey
188+
Case Int($VK_F7)
189+
ConsoleWrite("! Blocked: F7 caret browsing." & @CRLF)
190+
$oArgs.Handled = True
191+
192+
Case Int($VK_N)
193+
; Έλεγχος αν είναι πατημένο το Ctrl (χρησιμοποιώντας το IsMenuKeyDown αν αφορά Alt ή WinAPI για Ctrl)
194+
; Αν θέλουμε να μπλοκάρουμε το Ctrl+N (New Window)
195+
If _IsPressed("11") Then ; Χρειάζεται το Misc.au3 για την _IsPressed
196+
ConsoleWrite("! Blocked: Ctrl+N shortcut." & @CRLF)
197+
$oArgs.Handled = True
198+
199+
EndIf
200+
201+
Case Int($VK_ESCAPE) ; ESC 27 1b 033 Escape, next character is not echoed ; https://www.autoitscript.com/autoit3/docs/appendix/ascii.htm
202+
; Cancels active downloads. If `uri` is empty or omitted, cancels all active downloads.
203+
$oWebV2M.CancelDownloads()
204+
205+
EndSwitch
206+
207+
__NetWebView2_Log(@ScriptLineNumber, $s_Prefix, 0)
208+
$oArgs = 0 ; Explicitly release the COM reference inside the volatile scopeEndFunc
117209

118-
__NetWebView2_Log(@ScriptLineNumber, (StringLen($s_Prefix) > 150 ? StringLeft($s_Prefix, 150) & "..." : $s_Prefix), 1)
119210
EndFunc ;==>__UserEventHandler__OnAcceleratorKeyPressed
211+
212+

examples/007-HTTP_StatusCodeTracking.au3

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
;~ #AutoIt3Wrapper_UseX64=y
12
#include <GUIConstantsEx.au3>
23
#include <WindowsConstants.au3>
34
#include "..\NetWebView2Lib.au3"
5+
#include <WinAPISysWin.au3>
46

57
; Global objects
68
Global $oMyError = ObjEvent("AutoIt.Error", "_ErrFunc") ; COM Error Handler
@@ -9,6 +11,7 @@ _Example_HTTP_Tracking()
911

1012
Func _Example_HTTP_Tracking()
1113
Local $hGUI = GUICreate("WebView2 HTTP Status Tracker", 1000, 600)
14+
ConsoleWrite("$hGUI=" & $hGUI & @CRLF)
1215

1316
; Initialize WebView2 Manager and register events
1417
Local $oWebV2M = _NetWebView2_CreateManager("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.0.0", "WebEvents_")
@@ -49,6 +52,21 @@ EndFunc ;==>_Example_HTTP_Tracking
4952
#Region ; === EVENT HANDLERS ===
5053
; Handles native WebView2 events
5154
Func WebEvents_OnMessageReceived($oWebV2M, $hGUI, $sMsg)
55+
56+
; { part of the $hGUI handle explanation
57+
; with the new Advanced Handle Formatting logic [HANDLE:0x...]
58+
ConsoleWrite("$hGUI=" & $hGUI & @CRLF)
59+
ConsoleWrite("WinExists=" & WinExists($hGUI) & @CRLF)
60+
ConsoleWrite("WinGetTitle=" & WinGetTitle($hGUI) & @CRLF)
61+
ConsoleWrite("! _WinAPI_GetClientWidth($hGUI)=" & _WinAPI_GetClientWidth($hGUI) & @CRLF) ; not working
62+
63+
Local $hWnd = WinGetHandle($hGUI)
64+
ConsoleWrite("$hWnd=" & $hWnd & @CRLF)
65+
ConsoleWrite("WinExists($hWnd)=" & WinExists($hWnd) & @CRLF)
66+
ConsoleWrite("- _WinAPI_GetClientWidth($hWnd)=" & _WinAPI_GetClientWidth($hWnd) & @CRLF) ; working
67+
; End part of the $hGUI handle explanation }
68+
69+
5270
ConsoleWrite(">>> [WebEvents]: " & (StringLen($sMsg) > 150 ? StringLeft($sMsg, 150) & "..." : $sMsg) & @CRLF)
5371
Local $iSplitPos = StringInStr($sMsg, "|")
5472
Local $sCommand = $iSplitPos ? StringStripWS(StringLeft($sMsg, $iSplitPos - 1), 3) : $sMsg

examples/folder.jpg

6.03 KB
Loading

0 commit comments

Comments
 (0)