Interface implementation - QueryInterface never called

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
User avatar
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Interface implementation - QueryInterface never called

Post by kczx3 » 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
#Warn
#SingleInstance Force

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

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

; IWebView2CreateWebView2EnvironmentCompletedHandler.
; https://docs.microsoft.com/en-us/microsoft-edge/hosting/webview2/reference/ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler#interface_i_web_view2_create_web_view2_environment_completed_handler.
vtbl := BufferAlloc(4*A_PtrSize)  ; https://www.autohotkey.com/boards/viewtopic.php?t=37142#p170967
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.
; https://docs.microsoft.com/en-us/microsoft-edge/hosting/webview2/reference/ICoreWebView2CreateCoreWebView2ControllerCompletedHandler#invoke.
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.
; https://docs.microsoft.com/en-us/microsoft-edge/hosting/webview2/reference/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))
}
return

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)
    tooltip("TEST")

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

    NumPut("UPtr", 0, ppvObject)
    return 0x80004002 ; E_NOINTERFACE.
} ; https://docs.microsoft.com/en-us/windows/desktop/api/unknwn/nf-unknwn-iunknown-queryinterface%28refiid_void%29

AddRef(this) {
    local RefCount := NumGet(this,A_PtrSize,"UInt") + 1
    ; ToolTip("add count = " . RefCount)
    NumPut("UInt", RefCount, this, A_PtrSize)
    return RefCount
} ; https://docs.microsoft.com/en-us/windows/desktop/api/unknwn/nf-unknwn-iunknown-addref

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
} ; https://docs.microsoft.com/en-us/windows/desktop/api/unknwn/nf-unknwn-iunknown-release

; https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/0-9-538/icorewebview2createcorewebview2environmentcompletedhandler#invoke
Invoke(this, HRESULT, ICoreWebView2Environment) {
    global main, ptr2
    
    ; ICoreWebView2Environment::CreateCoreWebView2Controller Method.
    ; https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/0-9-538/icorewebview2environment#createcorewebview2controller
    ComCall(3, ICoreWebView2Environment, "Ptr", main.hwnd, "Ptr", ptr2)

    return 0  ; S_OK.
}

; https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/0-9-538/icorewebview2createcorewebview2controllercompletedhandler#invoke
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.
    ; https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/0-9-538/icorewebview2controller#put_bounds
    DllCall("User32.dll\GetClientRect", "Ptr", main.hWnd, "Ptr", RECT := BufferAlloc(16))
    ComCall(6, IWebView2WebViewController, "Ptr", RECT)

    ; IWebView2WebViewController::get_CoreWebView2
    ; https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/0-9-538/icorewebview2controller#get_corewebview2
    ComCall(25, IWebView2WebViewController, "Ptr*", webView := 0)
    
    ; ICoreWebView2::add_navigationstarting
    ; https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/0-9-538/icorewebview2#add_navigationstarting
    ComCall(7, webview, "Ptr", ptr3, "Ptr", token := BufferAlloc(8))
    
    ; ICoreWebView2::Navigate
    ; https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/0-9-538/icorewebview2#navigate
    ; ComCall(5, webview, "Str", "https://www.google.com/")
    ComCall(5, webview, "Str", "file:///" A_ScriptDir "/index.html")

    return 0  ; S_OK.
}

; https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/0-9-538/icorewebview2navigationstartingeventhandler#invoke
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() {
        
    }
}

lexikos
Posts: 9583
Joined: 30 Sep 2013, 04:07
Contact:

Re: Interface implementation - QueryInterface never called

Post by lexikos » 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
kczx3
Posts: 1640
Joined: 06 Oct 2015, 21:39

Re: Interface implementation - QueryInterface never called

Post by kczx3 » 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.

Post Reply

Return to “Ask for Help (v2)”