Interface implementation - QueryInterface never called

Get help for the alpha version of AutoHotkey v2 here. Please state the v2 version you are working with in the title when making a new topic.
User avatar
Posts: 1166
Joined: 06 Oct 2015, 21:39

Interface implementation - QueryInterface never called

14 Aug 2020, 07:54

I'm by no means even close to a novice when dealing with interfaces but it seems odd that QueryInterface is never called for any of the three interfaces that I define to use it in the following code. AddRef and Release both get called though. Is this normal? I think these interfaces have to be built this way because they don't have a CLSID so I can't just use ComObjCreate.

Code: Select all

; #Requires AutoHotkey v2.0-a112
#SingleInstance Force

; DllCall("User32.dll\SetProcessDpiAwarenessContext", "Ptr", 0xFFFFFFFC) ; not sure if this is needed.  Works fine without it.

main :=
main.OnEvent("Close", (main) => main.Hide())
main.Show("w1000 h800")
HotKey("F3", (*) => main.Show("w1000 h800"))

; IWebView2CreateWebView2EnvironmentCompletedHandler.
vtbl := BufferAlloc(4*A_PtrSize)  ;
for Each, Method in ["QueryInterface", "AddRef", "Release", "Invoke"]
    NumPut("UPtr", CallbackCreate(Method), vtbl, (A_Index-1)*A_PtrSize)

ptr := DllCall("Kernel32.dll\GlobalAlloc", "UInt", 0, "Ptr", A_PtrSize+4, "UPtr")
NumPut("UPtr", vtbl.ptr, ptr)
NumPut("UInt", 1, ptr, A_PtrSize)

; ICoreWebView2CreateCoreWebView2ControllerCompletedHandler.
vtbl2 := BufferAlloc(4*A_PtrSize)
for Each, Method in ["QueryInterface", "AddRef", "Release", "Invoke_2"]
    NumPut("UPtr", CallbackCreate(Method), vtbl2, (A_Index-1)*A_PtrSize)
ptr2 := DllCall("Kernel32.dll\GlobalAlloc", "UInt", 0, "Ptr", A_PtrSize+4, "UPtr")
NumPut("UPtr", vtbl2.ptr, ptr2)
NumPut("UInt", 1, ptr2, A_PtrSize)

; IWebView2NavigationCompletedEventHandler.
vtbl3 := BufferAlloc(4*A_PtrSize)
for Each, Method in ["QueryInterface", "AddRef", "Release", "Invoke_3"]
    NumPut("UPtr", CallbackCreate(Method), vtbl3, (A_Index-1)*A_PtrSize)
ptr3 := DllCall("Kernel32.dll\GlobalAlloc", "UInt", 0, "Ptr", A_PtrSize+4, "UPtr")
NumPut("UPtr", vtbl3.ptr, ptr3)
NumPut("UInt", 1, ptr3, A_PtrSize)

if R := DllCall(A_ScriptDir . "\WebView2Loader.dll\CreateCoreWebView2Environment", "Ptr", ptr, "UInt") {
    MsgBox("ERROR " . Format("{:08X}",R))

QueryInterface(this, riid, ppvObject) {
    local IID_IUnknown := BufferAlloc(16)
    local ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler := BufferAlloc(16)

    DllCall("Ole32.dll\CLSIDFromString", "Str", "{00000000-0000-0000-C000-000000000046}", "Ptr", IID_IUnknown)
    DllCall("Ole32.dll\CLSIDFromString", "Str", "{86EF6808-3C3F-4C6F-975E-8CE0B98F70BA}", "Ptr", ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler)

    if DllCall("Ole32.dll\IsEqualGUID", "Ptr", riid, "Ptr", IID_IUnknown)
    || DllCall("Ole32.dll\IsEqualGUID", "Ptr", riid, "Ptr", ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler)
        NumPut("UPtr", this, ppvObject)
        return 0 ; S_OK.

    NumPut("UPtr", 0, ppvObject)
    return 0x80004002 ; E_NOINTERFACE.
} ;

AddRef(this) {
    local RefCount := NumGet(this,A_PtrSize,"UInt") + 1
    ; ToolTip("add count = " . RefCount)
    NumPut("UInt", RefCount, this, A_PtrSize)
    return RefCount
} ;

Release(this) {
    local RefCount := NumGet(this,A_PtrSize,"UInt")
    if (RefCount > 0)
        NumPut("UInt", --RefCount, this, A_PtrSize)
        ; ToolTip("released count = " . RefCount)
        if (RefCount == 0)
            DllCall("Kernel32.dll\GlobalFree", "Ptr", this)
    return RefCount
} ;

Invoke(this, HRESULT, ICoreWebView2Environment) {
    global main, ptr2
    ; ICoreWebView2Environment::CreateCoreWebView2Controller Method.
    ComCall(3, ICoreWebView2Environment, "Ptr", main.hwnd, "Ptr", ptr2)

    return 0  ; S_OK.

Invoke_2(this, HRESULT, IWebView2WebViewController) {
    global main, ptr3
    ObjAddRef(IWebView2WebViewController) ; This was key to retain a reference to the Controller
    webViewController := IWebView2WebViewController
    main.OnEvent("Size", Func("mainSize").bind(IWebView2WebViewController))
    ; Resize WebView to fit the bounds of the parent window.
    ; IWebView2WebViewController::put_Bounds Method.
    DllCall("User32.dll\GetClientRect", "Ptr", main.hWnd, "Ptr", RECT := BufferAlloc(16))
    ComCall(6, IWebView2WebViewController, "Ptr", RECT)

    ; IWebView2WebViewController::get_CoreWebView2
    ComCall(25, IWebView2WebViewController, "Ptr*", webView := 0)
    ; ICoreWebView2::add_navigationstarting
    ComCall(7, webview, "Ptr", ptr3, "Ptr", token := BufferAlloc(8))
    ; ICoreWebView2::Navigate
    ; ComCall(5, webview, "Str", "")
    ComCall(5, webview, "Str", "file:///" A_ScriptDir "/index.html")

    return 0  ; S_OK.

Invoke_3(this, IWebView2WebView, IWebView2NavigationStartingEventArgs) {
    ComCall(4, IWebView2NavigationStartingEventArgs, "Int*", isUserInitiated := 0)
    ComCall(5, IWebView2NavigationStartingEventArgs, "Int*", isRedirected := 0)
    if (isUserInitiated && !isRedirected) {
        ComCall(3, IWebView2NavigationStartingEventArgs, "Str*", uri := "")
        if (MsgBox("You are about to navigate to: " . uri . "`n`nDo you want to continue?", "Navigation warning", "YN Icon!") = "No") {
            ; IWebView2NavigationStartingEventArgs::put_Cancel
            ComCall(8, IWebView2NavigationStartingEventArgs, "Int", true)
            ; ICoreWebView2::NavigateToString
            ComCall(6, IWebView2WebView, "Str", "<h1>Navigation Canceled</h1><p>You chose to cancel navigation to the following URL: " . uri . "</p>")
    return 0 ; S_OK

mainSize(controller, win, minMax, *) {
    ; for performance reasons, you should make the webview2 not visible when the parent window is minimized/hidden.
    ComCall(3, controller, "Int*", isVisible := 0)
    if (minMax == -1 && isVisible) {
        ComCall(4, controller, "Int", false)
    else if (minMax == 0 && !isVisible) {
        ComCall(4, controller, "Int", true)

class IUnknown {
    __New() {
Posts: 7085
Joined: 30 Sep 2013, 04:07
GitHub: Lexikos

Re: Interface implementation - QueryInterface never called

14 Aug 2020, 21:40

The documentation says "The caller implements this interface", so yes, you must implement the interface. If you created an instance of a predefined class with ComObjCreate, any interface pointers you receive from it would have already been implemented by that class, not by "the caller". And what would it do with whatever value the WebView2 passes to it?

When or why would you expect QueryInterface to be called? There's no reason for a function to query for an interface that it already has. If it was declared as IUnknown*, it would have to QueryInterface to get IWhateverHandler*.
for Each, Method
Maybe you're already aware, but in v2 Each, is completely redundant for Array.
User avatar
Posts: 1166
Joined: 06 Oct 2015, 21:39

Re: Interface implementation - QueryInterface never called

15 Aug 2020, 08:25

I see! Thank you for helping clear that up for me. It seems logical now and I should have inferred that.

The for-loop was remnants from code by @Flipeador of a much earlier v2 version.

Return to “AutoHotkey v2 Help”

Who is online

Users browsing this forum: No registered users and 1 guest