Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When to call AddRef and Release, and when only Release? #2133

Closed
koen-cerm opened this issue Feb 3, 2022 · 2 comments
Closed

When to call AddRef and Release, and when only Release? #2133

koen-cerm opened this issue Feb 3, 2022 · 2 comments

Comments

@koen-cerm
Copy link

Hi,

Although the sample code uses wrappers around the com objects to handle the com object lifetimes, I am interested to know when you should call both AddRef and Release, and when you should only call Release?

For example, in the WebView2APISample project, in AppWindow.cpp, storing the environment into m_webViewEnvironment will call AddRef() internally when setting, and Release when going out of scope:

HRESULT AppWindow::OnCreateEnvironmentCompleted(
    HRESULT result, ICoreWebView2Environment* environment)
{
    CHECK_FAILURE(result);
    m_webViewEnvironment = environment;

However, for example when getting content/headers from a request (in ScenarioWebViewEventMonitor.cpp) , I don't think AddRef is called, because operator& is used to retrieve a pointer to the internal member of wil::com_ptr. So I assume that AddRef is not needed, and you should only call Release for the content/headers.

std::wstring RequestToJsonString(ICoreWebView2WebResourceRequest* request)
{
    wil::com_ptr<IStream> content;
    CHECK_FAILURE(request->get_Content(&content));
    wil::com_ptr<ICoreWebView2HttpRequestHeaders> headers;
    CHECK_FAILURE(request->get_Headers(&headers));

Is my assumption correct? And if yes/no: how can I know when you should call both AddRef and Release, and when should you call only Release?
Currently I assume that AddRef and Release are both needed in a callback function when the ICore... object is an argument, and you only need to call Release when you retrieve the pointer via a get_... (ICore... **) method.

Thanks,

Koen

@david-risney
Copy link
Contributor

Generally, the rules are:

  1. When a COM object is passed from caller to callee as an input parameter to a method, the caller is expected to keep a reference on the object for the duration of the method call. The callee shouldn't need to call AddRef or Release for the synchronous duration of that method call. For example, if you're the callee implementing one of the async completed handler or event handler Invoke methods, the WebView2 code will call your Invoke method and pass in a COM object and you don't need to call AddRef or Release on it.
  2. When a COM object is passed from callee to caller as an out parameter from a method the object is provided to the caller with a reference already taken and the caller owns the reference. Which is to say, it is the caller's responsibility to call Release when they're done with the object. For example, if you call ICoreWebView2::get_Settings(ICoreWebView2Settings**), the get_Settings code will call AddRef on the ICoreWebView2Settings it hands back to you and its up to you only to call Release when you're done.
  3. When making a copy of a COM object pointer you need to call AddRef and Release. The AddRef must be called before you call Release on the original COM object pointer. If for example you have an async method call completion handler method that receives a COM object as an in parameter but you need to deal with that COM object asynchronously later, you'll need to make a copy of the COM object pointer and call AddRef during the completion handler and then Release later after you finish your async work with the object.

To cover your specific examples, the first is an example of (1) & (3). The input parameter ICoreWebView2Environment* already has an AddRef on it that's good for the duration of the method call. But we need to store a copy of that object in our member variable m_webViewEnvironment which will outlive the duration of this method call. So, there's an AddRef when we assign to m_webViewEnvironment (the smart pointer code does the AddRef) and then later a Release call on it during the smart pointer's destructor.

HRESULT AppWindow::OnCreateEnvironmentCompleted(
    HRESULT result, ICoreWebView2Environment* environment) // environment has a ref good for the duration of this call
{
    CHECK_FAILURE(result);
    m_webViewEnvironment = environment; // Since m_webViewEnvironment outlives this call it has its own AddRef/Release

Your second example shows (2). The method calls that provide a COM object as an out parameter to the caller, the method does an AddRef for the caller, and its up to the caller to call Release. You're correct that the &content will result in the underlying raw pointer not the smart pointer and so there's no AddRef call from the smart pointer. The get_Content method call does the AddRef itself when creating a copy of its COM object pointer. But the content smart pointer will perform a Release when it goes out of scope.

std::wstring RequestToJsonString(ICoreWebView2WebResourceRequest* request)
{
    wil::com_ptr<IStream> content;
    CHECK_FAILURE(request->get_Content(&content)); // get_Content gives out a COM object with a ref and the caller has to Release
    wil::com_ptr<ICoreWebView2HttpRequestHeaders> headers;
    CHECK_FAILURE(request->get_Headers(&headers)); // Same as above

@koen-cerm
Copy link
Author

Thank you for the elaborated response. It is clear to me now (and it confirms what I thought).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants