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

.Net 8 upgrade results in missing destructor call of ActiveXControl from interop assembly generated with AxImp.exe #12056

Open
KL-Sven opened this issue Sep 5, 2024 · 1 comment
Assignees
Milestone

Comments

@KL-Sven
Copy link

KL-Sven commented Sep 5, 2024

.NET version

net8.0-windows

Did it work in .NET Framework?

Yes

Did it work in any of the earlier releases of .NET Core or .NET 5+?

All versions before .Net 8

Issue description

I have an ActiveX control (OCX) that is integrated into a .NET application. To create the .NET interop assemblies, I use AxImp.exe, which generates a class that inherits from System.Windows.Forms.AxHost. In all versions of .NET prior to 8.0, the destructor of the OCX would be invoked upon calling the Dispose() method on the AxHost instance. However, with the introduction of .NET 8, the destructor is no longer called.

Specifically, in .NET 7, invoking Dispose() would lead to a call to Marshal.FinalReleaseComObject(_instance) within the method ReleaseAxControl() of class AxHost, which effectively triggered the OCX's destructor. But with .NET 8, the code has been changed to call Marshal.ReleaseComObject(_instance) instead, and as a result, the destructor of the OCX is not being executed anymore. This change in behavior is causing issues with resource management in the application.

Steps to reproduce

  1. Create a New MFC ActiveX Control Project:

    • Launch Visual Studio 2022.
    • Create a new project using the MFC ActiveX Control template.
    • Name the project MFCActiveXControl.
  2. Modify the Control's Constructor and Destructor:

    • Open the MFCActiveXControlCtrl.cpp file.
    • Update the constructor and destructor to include console output for logging purposes:
    #include <iostream>
    
    CMFCActiveXControlCtrl::CMFCActiveXControlCtrl()
    {
        std::cout << "constructor called";
    
        InitializeIIDs(&IID_DMFCActiveXControl, &IID_DMFCActiveXControlEvents);
        // TODO: Initialize your control's instance data here.
    }
    
    CMFCActiveXControlCtrl::~CMFCActiveXControlCtrl()
    {
        std::cout << "destructor called";
        // TODO: Cleanup your control's instance data here.
    }
  3. Compile the Project:

    • Set the build configuration to 'Release'.
    • Compile the project to generate the MFCActiveXControl.ocx file.
  4. Register the Compiled OCX:

    • Open PowerShell as an administrator.
    • Execute the following command to register the OCX file:
    . "$Env:WINDIR\SysWOW64\regsvr32.exe" MFCActiveXControl.ocx
    
  5. Generate .NET Interop Assemblies:

    • Use AxImp.exe to create the .NET interop assemblies from the OCX file:
    . "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\AxImp.exe" MFCActiveXControl.ocx
    
  6. Add a .Net 7 C# Console Application:

    • Name the application ConsoleClient.
    • Configure the project file with the following settings:
    <TargetFramework>net7.0-windows</TargetFramework>
    <PlatformTarget>x86</PlatformTarget>
    <UseWindowsForms>true</UseWindowsForms>
    
  7. Add References to the Generated DLLs:

    • Reference both MFCActiveXControlLib.dll and AxMFCActiveXControlLib.dll in your ConsoleClient project.
  8. Update the Main Program File:

    • Replace the contents of Program.cs with the following code:
     using System;
     using AxMFCActiveXControlLib;
     
     namespace ConsoleClient
     {
         internal class Program
         {
             [STAThread]
             static void Main(string[] args)
             {
                 var ocx = new AxMFCActiveXControl();
                 ocx.CreateControl();
                 ocx.Dispose();
             }
         }
     }
  9. Compile and Run the C# Application:

    • Build the ConsoleClient application.
    • Run the application and observe the console output: "Constructor called." followed by "Destructor called."
  10. Update to .NET 8:

    • Change the target framework to .NET 8 by updating the project file:
    <TargetFramework>net8.0-windows</TargetFramework>
    
    • Rebuild the ConsoleClient application.
  11. Re-run the Application:

    • Run the updated ConsoleClient application and observe that the console output displays only "Constructor called." This indicates that the destructor is not being invoked as expected.
@KL-Sven KL-Sven added the untriaged The team needs to look at this issue in the next triage label Sep 5, 2024
@merriemcgaw merriemcgaw removed the untriaged The team needs to look at this issue in the next triage label Sep 5, 2024
@merriemcgaw merriemcgaw added this to the .NET 10.0 milestone Sep 5, 2024
@JeremyKuhne
Copy link
Member

In general it is terrible form to keep calling release in a loop. @lonitra can you check what the ref counts are in ReleaseAxControl are in 8 and 9?

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

4 participants