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

C++/CLI libraries may fail to load due to ijwhost.dll not being on the search path #38231

Open
elinor-fung opened this issue Jun 22, 2020 · 11 comments
Milestone

Comments

@elinor-fung
Copy link
Member

To support C++/CLI libraries in .NET Core, ijwhost was created as a shim for finding and loading the runtime. All C++/CLI libraries are linked to this shim, such that ijwhost.dll is found/loaded when the C++/CLI library is loaded.

By default, Windows' DLL search will look for dependencies of a DLL as if they were loaded with just the module name. This means that, depending on how a host application loads the C++/CLI library, the C++/CLI library may fail to load due to ijwhost.dll not being found. In order for the load to work one of the following needs to be true:

  • ijwhost.dll is on the search path
  • the host applications specifies either LOAD_WITH_ALTERED_SEARCH_PATH or LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR flags for LoadLibrary such that dependencies are searched for in the same directory as the C++/CLI library

C++/CLI libraries cannot dictate how their hosts load them and authors should not need to be concerned with how the ijwhost shim is found and loaded. The runtime/tooling should make the usage of ijwhost hidden to the user such that it just works without user intervention.

In .NET Framework, the equivalent shim (mscoree/mscoreei) is system-wide, so this was not an issue.

Potential options:

  • Make ijwhost a static library that gets linked into the C++/CLI library
  • ?

@jkoritzinsky @AaronRobinsonMSFT @vitek-karas

@ghost
Copy link

ghost commented Jun 22, 2020

Tagging subscribers to this area: @vitek-karas, @swaroop-sridhar
Notify danmosemsft if you want to be subscribed.

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added the untriaged New issue has not been triaged by the area owner label Jun 22, 2020
@jkoritzinsky
Copy link
Member

Would it be possible to ask the C++/CLI project system to include an assembly manifest like the one in your workaround for #37972 in either their templates or automatically as part of the build?

@agocke agocke removed the untriaged New issue has not been triaged by the area owner label Jul 6, 2020
@agocke agocke added this to the Future milestone Jul 6, 2020
@Ninds
Copy link

Ninds commented Jul 24, 2020

Hello,
I am observing the same issue when building Excel XLL's against .NET 5 (preview) with a modified XLW. XLW has a hook to add the containing directory to the PATH :

xlw::PathUpdater::PathUpdater()
{
    MEMORY_BASIC_INFORMATION theInfo ;
    HMODULE theHandle = NULL;
    char theDLLPathChar [MAX_PATH + 1] = "";
    DWORD dwRet = 0;
    std::string originalPathValue(StringUtilities::getEnvironmentVariable("PATH"));
    bool ok(!originalPathValue.empty());

    dwRet = static_cast<DWORD>(VirtualQuery (((LPCVOID)this), &theInfo,(static_cast<DWORD> (sizeof (MEMORY_BASIC_INFORMATION)))));
    if (dwRet)
    {
        theHandle = ((HMODULE) (theInfo.AllocationBase));
        GetModuleFileName (theHandle, theDLLPathChar , MAX_PATH);
        xlw::XlfServices.StatusBar = theDLLPathChar;
    }
    else
    {
        ok = false;
        std::cerr << XLW__HERE__ <<" Could not attain path of DLL" << std::endl;
    }
    if(ok)
    {
        std::string theDLLPath(theDLLPathChar);
        std::string newPathValue(originalPathValue);
        std::string::size_type pos = theDLLPath.find_last_of("\\");
        newPathValue+= ";"+theDLLPath.substr(0,pos);

        if (!SetEnvironmentVariable("Path", newPathValue.c_str()))
        {
            std::cerr << XLW__HERE__ << " SetEnvironmentVariable failed to set PATH" << std::endl;
            ok = false;
        }
        else
        {
            std::cerr << XLW__HERE__ << " PATH set successfully " << std::endl;
        }
    }
    if(!ok)
    {
        std::cerr << XLW__HERE__ << " Warning: Unable to initialise PATH to directory of library " << std::endl;
    }
} 

This is executed quite early on when the XLL is loaded and works for most other dependant dlls that need to be found. It does not work for ijwhost.dll however.

For some reason it's too late. Setting the PATH variable explicitly and externally makes everything work as expected.

For .NET Core 3.1 we don't even get that far :

1>C:\Program Files\dotnet\sdk\5.0.100-preview.5.20279.10\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.targets(565,5): error NETSDK1114: Unable to find a .NET Core IJW host. The .NET Core IJW host is only available on .NET Core 3.1 or higher when targeting Windows.

@AaronRobinsonMSFT
Copy link
Member

/cc @agocke

amaitland added a commit to cefsharp/CefSharp that referenced this issue Aug 14, 2020
Add assembly manifest for VC++ projects to prevent loading issues of ijwhost.dll

dotnet/runtime#38231
dotnet/runtime#37972 (comment)

Not sure exactly how to test this works as expected

Reference
https://docs.microsoft.com/en-us/windows/win32/sbscs/assembly-manifests

#3197
@mikeoliphant
Copy link
Contributor

This is still an issue for me with .NET 5.0 and VS 16.8.2.

I resolved it by adding a linker manifest to handle "Ijwhost.dll", but it took me hours of troubleshooting and scouring the internet to figure that out. It would be great if it could just be handled automatically - ideally with "Ijwhost.dll" going away - either being statically linked or through some other mechanism.

This is part of a larger problem of C++/CLI being unfriendly for plugin architectures. If your dll is being loaded by a host, the default assembly load path isn't where your plugin is, so you have to implement an AssemblyLoadContext resolve handler to load assemblies. It works - but it is an extra step that takes time to figure out and implement.

This is stuff that should "just work", particularly with .NET Core becoming the new mainstream.

@vitek-karas
Copy link
Member

@mikeoliphant could you please describe in a bit more details this:

If your dll is being loaded by a host, the default assembly load path isn't where your plugin is

I don't think I follow what is the problem here. Is it that loading plugins requires a resolver (regardless of C++/CLI being used or not)? I don't see how C++/CLI makes this different.

@mikeoliphant
Copy link
Contributor

I don't think I follow what is the problem here. Is it that loading plugins requires a resolver (regardless of C++/CLI being used or not)? I don't see how C++/CLI makes this different.

I hadn't thought about that, but it makes sense that the path issue would still exist in a purely managed context. In C++/CLI the situation is exacerbated by the issue with "Ijwhost.dll".

@datduyng
Copy link

I am facing this same issue. Our .net6 core web server runnning on Window nano server 1809 is taking dependency on a c++ library. when running the NanoServerApiScanner.exe it says that ijwhost.dll is missing

image

When searching for the ijwhost.dll file there are many instance of this dll found

image

Any help would be greatly appreciated

@Ruffnik
Copy link

Ruffnik commented Mar 26, 2022

I got this solved by including the ijwhost.dll in the linker manifest

@bj-rn
Copy link

bj-rn commented Mar 22, 2024

I resolved it by adding a linker manifest to handle "Ijwhost.dll", but it took me hours of troubleshooting and scouring the internet to figure that out.

@mikeoliphant do you have that process documented somewhere?

@mikeoliphant
Copy link
Contributor

I resolved it by adding a linker manifest to handle "Ijwhost.dll", but it took me hours of troubleshooting and scouring the internet to figure that out.

@mikeoliphant do you have that process documented somewhere?

LinkManifest

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

No branches or pull requests