Edge WebGadget - Set BrowserArguments, UserDataFolder (Windows)

Share your advanced PureBasic knowledge/code with the community.
breeze4me
Enthusiast
Enthusiast
Posts: 527
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Edge WebGadget - Set BrowserArguments, UserDataFolder (Windows)

Post by breeze4me »

Setting the arguments (optional switches) at a WebGadget launch is simple, as they can be specified via a single environment variable.

Code: Select all

SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", ~"--inprivate --disk-cache-dir=\"" + GetTemporaryDirectory() + "WebView2_Cache" + ~"\"")

OpenWindow(0, 0, 0, 1000, 600, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
StringGadget(1, 210, 4, 500, 24, "https://www.purebasic.com")
ButtonGadget(2, 710, 4, 50, 24, "Go")
WebGadget(3, 0, 30, 1000, 560, "https://whatismyipaddress.com", #PB_Web_Edge)

Repeat
  e = WaitWindowEvent()
  
  If e = #PB_Event_Gadget
    Select EventGadget()
      Case 2
        SetGadgetText(3, Trim(GetGadgetText(1)))
        
    EndSelect
  EndIf
  
Until e = #PB_Event_CloseWindow
However, you need an API hook to specify the user data folder.
Below is an example using MS's Detours library.
The link below is static library files that have been compiled for your convenience.
https://www.dropbox.com/scl/fi/q8nppbah ... mhvex&dl=0

The WebView2Loader.dll file can be found in the path "\build\native\x64\" and "\build\native\x86\" when you download and unzip the nuget package below.
It's an optional file. The code will be run without it.
https://www.nuget.org/packages/Microsoft.Web.WebView2

Code: Select all

; https://github.com/webview/webview

;// The minimum WebView2 API version we need regardless of the SDK release
;// actually used. The number comes from the SDK release version,
;// e.g. 1.0.1150.38. To be safe the SDK should have a number that is greater
;// than or equal to this number. The Edge browser webview client must
;// have a number greater than or equal to this number.


EnableExplicit

CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
  #Lib_x86x64 = "86"
CompilerElse
  #Lib_x86x64 = "64"
CompilerEndIf

Import "Detours.lib"
  DetourTransactionBegin.l()
  DetourTransactionCommit.l()
  DetourUpdateThread.l(hThread)
  DetourAttach.l(*ppPointer, pDetour)
  DetourDetach.l(*ppPointer, pDetour)
EndImport


; https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2365.46

; STDAPI CreateCoreWebView2EnvironmentWithOptions(PCWSTR browserExecutableFolder, PCWSTR userDataFolder, ICoreWebView2EnvironmentOptions* environmentOptions, ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* environmentCreatedHandler);
; STDAPI GetAvailableCoreWebView2BrowserVersionString(PCWSTR browserExecutableFolder, LPWSTR* versionInfo);
; STDAPI CompareBrowserVersions(PCWSTR version1, PCWSTR version2, int* result);

; https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2365.46#createcorewebview2environmentwithoptions
; CreateCoreWebView2EnvironmentWithOptions(PCWSTR browserExecutableFolder, PCWSTR userDataFolder, ICoreWebView2EnvironmentOptions * environmentOptions, ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler * environmentCreatedHandler)
Prototype.l CreateCoreWebView2EnvironmentWithOptions(*browserExecutableFolder, *userDataFolder, *environmentOptions, *createdEnvironmentCallback)
Global CreateCoreWebView2EnvironmentWithOptions__.CreateCoreWebView2EnvironmentWithOptions

; https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/webview2-idl?view=webview2-1.0.2365.46#createwebviewenvironmentwithoptionsinternal
; CreateWebViewEnvironmentWithOptionsInternal(bool checkRunningInstance, int runtimeType, PCWSTR userDataFolder, IUnknown * environmentOptions, ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler * webViewEnvironmentCreatedHandler)
Prototype.l CreateWebViewEnvironmentWithOptionsInternal(checkRunningInstance.l, runtimeType.l, *userDataFolder, *environmentOptions, *webViewEnvironmentCreatedHandler)
Global CreateWebViewEnvironmentWithOptionsInternal__.CreateWebViewEnvironmentWithOptionsInternal

Procedure.l My_CreateWebViewEnvironmentWithOptionsInternal(checkRunningInstance.l, runtimeType.l, *userDataFolder, *environmentOptions.ICoreWebView2EnvironmentOptions, *webViewEnvironmentCreatedHandler)
  Protected lResult.l
  
  ; Read the value you set in the environment variable below.
  Protected sUserDataFolder.s = GetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER")
  
  Debug "----------------------------------------"
  Debug #PB_Compiler_Procedure
  
  If sUserDataFolder
    
    ; In this example, it is only used to show the "UserDataFolder", which is already specified.
    If *userDataFolder
      Debug "Previous UserDataFolder: " + PeekS(*userDataFolder)
    EndIf
    
    ; Specify the new "UserDataFolder" that you obtained above.
    *userDataFolder = @sUserDataFolder
    Debug "Set new UserDataFolder: " + sUserDataFolder
  EndIf
  
  Debug "----------------------------------------"
  
  ; Call the original unhooked function.
  lResult = CreateWebViewEnvironmentWithOptionsInternal__(checkRunningInstance, runtimeType, *userDataFolder, *environmentOptions, *webViewEnvironmentCreatedHandler)
  
  ProcedureReturn lResult
EndProcedure

Procedure.l My_CreateCoreWebView2EnvironmentWithOptions(*browserExecutableFolder, *userDataFolder, *environmentOptions.ICoreWebView2EnvironmentOptions, *createdEnvironmentCallback)
  Protected lResult.l
  Protected sUserDataFolder.s = GetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER")
  
  Debug "----------------------------------------"
  Debug #PB_Compiler_Procedure
  
  If sUserDataFolder
    If *userDataFolder
      Debug "Previous UserDataFolder: " + PeekS(*userDataFolder)
    EndIf
    *userDataFolder = @sUserDataFolder
    Debug "Set new UserDataFolder: " + sUserDataFolder
  EndIf
  
  Debug "----------------------------------------"
  
  lResult = CreateCoreWebView2EnvironmentWithOptions__(*browserExecutableFolder, *userDataFolder, *environmentOptions, *createdEnvironmentCallback)
  
  ProcedureReturn lResult
EndProcedure

Procedure.s GetWebView2Path()
  Protected sResult.s
	Protected sSubkey.s = "SOFTWARE\Microsoft\EdgeUpdate\ClientState\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}"
	Protected hkey, *m, lDataBytes.l
	
	If RegOpenKeyEx_(#HKEY_LOCAL_MACHINE, sSubkey, 0, #KEY_READ | #KEY_WOW64_32KEY, @hkey) = #ERROR_SUCCESS
	  If RegQueryValueEx_(hkey, "EBWebView", 0, 0, *m, @lDataBytes) =	#ERROR_SUCCESS
	    If lDataBytes > 0
	      *m = AllocateMemory(lDataBytes)
	      If *m
	        If RegQueryValueEx_(hkey, "EBWebView", 0, 0, *m, @lDataBytes) =	#ERROR_SUCCESS
	          sResult = PeekS(*m)
	        EndIf
	        FreeMemory(*m)
	      EndIf
	    EndIf
	  EndIf
		RegCloseKey_(hkey)
	EndIf 
	
	ProcedureReturn sResult
EndProcedure


Define Lib_WV2, sPath.s

; First, we look in the registry to see if WebView2 is installed on the system.
sPath = GetWebView2Path()
If sPath
  
  ; If the "WebView2Loader.dll" file exists in the application folder,
  ; the WebGadget is created using the CreateCoreWebView2EnvironmentWithOptions function inside the "WebView2Loader.dll" file,
  ; otherwise, the WebGadget is created using the CreateWebViewEnvironmentWithOptionsInternal function inside the "EmbeddedBrowserWebView.dll" file at the path obtained above.
  
  ; We'll intercept the call to one of the two functions used to create the WebGadget.
  
  Lib_WV2 = OpenLibrary(#PB_Any, "WebView2Loader.dll")
  If Lib_WV2
    Debug "WebView2Loader.dll open."
    CreateCoreWebView2EnvironmentWithOptions__ = GetFunction(Lib_WV2, "CreateCoreWebView2EnvironmentWithOptions")
  Else
    Lib_WV2 = OpenLibrary(#PB_Any, sPath + "\EBWebView\x" + #Lib_x86x64 + "\EmbeddedBrowserWebView.dll")
    If Lib_WV2
      Debug "EmbeddedBrowserWebView.dll open."
      CreateWebViewEnvironmentWithOptionsInternal__ = GetFunction(Lib_WV2, "CreateWebViewEnvironmentWithOptionsInternal")
    EndIf
  EndIf
  
  ; Once you have the address of one of the two functions, try the hook.
  If CreateCoreWebView2EnvironmentWithOptions__ Or CreateWebViewEnvironmentWithOptionsInternal__
    ; https://github.com/microsoft/Detours/wiki/DetourTransactionBegin
    If DetourTransactionBegin() = #NO_ERROR
      
      ; https://github.com/microsoft/Detours/wiki/DetourUpdateThread
      If DetourUpdateThread(GetCurrentThread_()) = #NO_ERROR
        
        If CreateCoreWebView2EnvironmentWithOptions__
          ; https://github.com/microsoft/Detours/wiki/DetourAttach
          ; As a result of the function execution, the former variable stores the original function address before it was hooked.
          If DetourAttach(@CreateCoreWebView2EnvironmentWithOptions__, @My_CreateCoreWebView2EnvironmentWithOptions()) <> #NO_ERROR
            Debug "Error 0"
          EndIf
        EndIf
        
        If CreateWebViewEnvironmentWithOptionsInternal__
          If DetourAttach(@CreateWebViewEnvironmentWithOptionsInternal__, @My_CreateWebViewEnvironmentWithOptionsInternal()) <> #NO_ERROR
            Debug "Error 1"
          EndIf
        EndIf
        
        ; https://github.com/microsoft/Detours/wiki/DetourTransactionCommit
        DetourTransactionCommit()
      EndIf
    EndIf
    
  Else
    Debug "WebView2 function not found."
  EndIf
  
Else
  Debug "No WebView2 installed."
EndIf


; Unlike setting browser arguments, this one line alone won't work, so we need an API hook.
; Reflecting the path of the environment variable may be possible in a future PB update, but for now, it's not possible in 6.10 b9.
SetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER", GetTemporaryDirectory() + "WebView2_User")

SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", ~"--inprivate --disk-cache-dir=\"" + GetTemporaryDirectory() + "WebView2_Cache" + ~"\"")


OpenWindow(0, 0, 0, 1000, 600, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
StringGadget(1, 210, 4, 500, 24, "https://www.purebasic.com")
ButtonGadget(2, 710, 4, 50, 24, "Go")
WebGadget(3, 0, 30, 1000, 560, "https://whatismyipaddress.com", #PB_Web_Edge)

Define e

Repeat
  e = WaitWindowEvent()
  
  If e = #PB_Event_Gadget
    Select EventGadget()
      Case 2
        SetGadgetText(3, Trim(GetGadgetText(1)))
        
    EndSelect
  EndIf
  
Until e = #PB_Event_CloseWindow

CloseLibrary(#PB_All)

Last edited by breeze4me on Mon Mar 25, 2024 5:33 am, edited 4 times in total.
User avatar
Kiffi
Addict
Addict
Posts: 1358
Joined: Tue Mar 02, 2004 1:20 pm
Location: Amphibios 9

Re: Edge WebGadget - Set BrowserArguments, UserDataFolder (Windows)

Post by Kiffi »

breeze4me wrote: Fri Mar 22, 2024 5:10 pmSetting the arguments (optional switches) at a WebGadget launch is simple, as they can be specified via a single environment variable.
Very cool! Thanks! Image
Hygge
dige
Addict
Addict
Posts: 1254
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: Edge WebGadget - Set BrowserArguments, UserDataFolder (Windows)

Post by dige »

Thank you breeze4me for the example of setting webgadget parameters at startup.

Unfortunately, I was unable to test the second example due to a lack of intelligence and now I feel completely stupid. :cry:
Would you be so kind as to add a few more explanations? What happens to the code? How can I test the code?

Many thanks in advance!
dige
"Daddy, I'll run faster, then it is not so far..."
breeze4me
Enthusiast
Enthusiast
Posts: 527
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: Edge WebGadget - Set BrowserArguments, UserDataFolder (Windows)

Post by breeze4me »

dige wrote: Sun Mar 24, 2024 6:24 pm Thank you breeze4me for the example of setting webgadget parameters at startup.

Unfortunately, I was unable to test the second example due to a lack of intelligence and now I feel completely stupid. :cry:
Would you be so kind as to add a few more explanations? What happens to the code? How can I test the code?

Many thanks in advance!
dige
Thank you for your interest. I've added some explanations.
dige
Addict
Addict
Posts: 1254
Joined: Wed Apr 30, 2003 8:15 am
Location: Germany
Contact:

Re: Edge WebGadget - Set BrowserArguments, UserDataFolder (Windows)

Post by dige »

Thank you :D
"Daddy, I'll run faster, then it is not so far..."
Post Reply