DbProviderFactories registration fails in .NET 9-windows when run through C++/CLI loader DLL

Ryan Wagner 20 Reputation points
2025-04-16T00:04:13.2633333+00:00

Hi,

I am converting our c# .Net 4.8 applications to .Net9-windows. This is my primary goal. The problem is that I am getting this error "--->Exception: ArgumentNullException

:Value cannot be null. (Parameter 'factory')--->Stack:

at System.Data.Common.DbProviderFactories.RegisterFactory(String providerInvariantName, DbProviderFactory factory)

".

I know from researching the error and asking ChatGPT that we need to register "System.data.oledb.dll" for .Net 9-windows. In are older .Net 4.8 version "System.data.oledb.dll" was automatically registered.

For background, when our c++ application called "Client.exe" runs, it loads a c++/cli dll that we created and this c++/cli dll called "interop.dll" (now targets .Net9-windows) loads another dll called "share.dll" that we created that now targets .Net 9-windows.

The problem is that inside share.dll we need to register system.data.oledb.dll by calling line "DbProviderFactories.RegisterFactory("System.Data.OleDb", OleDbFactory.Instance);", and this line fails as "OleDbFactory.Instance" is null. I can get "OleDbFactory.Instance" to not be null only when running another exe call called tools.exe which is c# .Net 9-windows and does not use interop.dll (c++/cli) of course. The tools.exe depends on and loads share.dll. tools.exe has a " static void Main()" function and "DbProviderFactories.RegisterFactory("System.Data.OleDb", OleDbFactory.Instance);" is called at start of main function and succeeds.

Another application we have that relies on share.dll but does not use c++/cli (c# application .Net 9-windows) need a "ModuleInitializer" function like the one below to load in all the other assemblies that this application needed (but this solution added to share.dll does not help client.exe). This is called before the share.dll's entry point and the system.data.oledb.dll gets registered successfully when "DbProviderFactories.RegisterFactory("System.Data.OleDb", OleDbFactory.Instance);" is called.

using System;

using System.Data.Common;

using System.Data.OleDb;

using System.IO;

using System.Reflection;

using System.Runtime.CompilerServices;

internal static class StartupResolver

{

// This function will run automatically before any other code, even if Main() is never hit.

// It ensures AppDomain.CurrentDomain.AssemblyResolve is active before anything triggers a FileNotFoundException.

[ModuleInitializer]

public static void Initialize()

{

string addinsPath = Path.Combine(AppContext.BaseDirectory, "Addins");

AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>

{

var requestedAssembly = new AssemblyName(args.Name);

string dllPath = Path.Combine(addinsPath, requestedAssembly.Name + ".dll");

if (File.Exists(dllPath))

{

return Assembly.LoadFrom(dllPath);

}

return null;

};

}

}

The client.exe problem in more detail:

I am calling the "DbProviderFactories.RegisterFactory("System.Data.OleDb", OleDbFactory.Instance);" line from a static constructor in share.dll called "static ApplicationServices()". I have tried other places too and OleDbFactory.Instance is always null. There is no main() function in share.dll that gets called.

The interop.dll loads the share.dll by calling this function

void LoadShare()

{

        System::Reflection::Assembly^ al = System::Reflection::Assembly::LoadFrom(_T("Share.dll"));

	System::Type^ t = al->GetType("AddInServices", false);

	t->InvokeMember("Initialize", BindingFlags::InvokeMethod | BindingFlags::Public |                                               BindingFlags::Static, nullptr, nullptr, nullptr);		

}

I am currently trying to register the system.data.oledb.dll in a function I created called "InitializeOleDb" and the callstack and function are below:

here is a simplified callstack.

Share.dll!ApplicationServices.InitializeOleDb() 

Share.dll!ApplicationServices.ApplicationServices()

[Native to Managed Transition]	

[Managed to Native Transition]	

Share.dll!ApplicationServices.AddService(System.Type serviceType, object serviceInstance)

Share.dll!Interop.CVSessionManager.RegisterSession(object session = {Interop.MCVSession}

Share.dll!Interop.CVSessionManager.RegisterSession(object session = {Interop.MCVSession})

Interop.dll!RegisterDefaultSession() 

Interop.dll!InitModuleFactory(CModuleFactory* pFactory)

[Native to Managed Transition]	

Client.exe!CModuleFactory::LoadLibraryW() 

Client.exe!CModuleFactory::CModuleFactory... 

Client.exe!CGlobalModuleFactory::EnumerateClasses()...

Client.exe!ClientApp::InnerInitInstance() 	

Client.exe!ClientApp::InitInstance() 

public static class ApplicationServices

{

public static void **InitializeOleDb()**

{

    try

    {

        **Assembly loadedAssembly = Assembly.Load("System.Data.OleDb"); // Assembly is loaded successfully.**

        if (loadedAssembly != null)

        {

            **Console.WriteLine("System.Data.OleDb Assembly loaded."); // this gets called**

        }

        else

        {

            Console.WriteLine("System.Data.OleDb Assembly load failed.");

        }

        try

        {                    

           **// OleDbFactory.Instance is null at this point, register factory fails!**

            **DbProviderFactories.RegisterFactory("System.Data.OleDb", OleDbFactory.Instance);**

            Console.WriteLine("OleDbFactory registered successfully.");

        }

        catch (Exception factoryEx)

        {

            Console.WriteLine($"OleDbFactory instantiation error: {factoryEx.Message}");

        }

    }

    catch (Exception ex)

    {

        Console.WriteLine($"Error registering OleDb: {ex.Message}");

    }

}

static ApplicationServices()

{

    **InitializeOleDb();**        

}

}

Question: Does anyone know where I should be calling InitializeOleDb()? and what I need to do to get system.data.oledb.dll to register in share.dll and OleDbFactory.Instance to not be null?

Thanks,

Developer technologies | .NET | .NET CLI
0 comments No comments
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.