Search code examples
c++.netwinformsc++-cliclr

Embedding localized satellite dll into exe application


I have a C++ CLR/CLI project, I wonder how to embed a localized satellite dll into my exe application, I found similar solutions but it's for C# projects which is pretty different from my project structure. Is it possible to embed it directly into the binary? By the way I'm getting issues with namespaces, it seems my custom namespace is not linked to my localized resource file.


Solution

  • I've been searching for hours to find a solution for a C++ CLR/CLI project which is pretty different comparing with C# projects which apparently comes with Build Action and Custom Tool Namespace all these options we don't have in a CLR/CLI project, it's really important, especially if we have changed Namespaces so we gotta use Resource Logical Name instead. Here's my answer how to solve Namespace issues, this also works for localized resource files linked to satellite dlls.

    After your localized satellite dll is generated, include that in your project as Compiled Managed Resource you can set that by opening its file property and setting the Item Type. In projects such as C# you won't find that but something similar like "Embedded Resource". Anyways this is intended to C++ CLR/CLI projects only. If you have changed namespaces, don't forget to set Resource Logical Name of the respective resource file.

    Next step is to do some code in order to embed that dll into our exe application, here's a good one for that:

    Since C++ CLR/CLI doesn't support lambda expressions we have to do this way:

    private: System::Reflection::Assembly^  currentDomainAssemblyResolve(System::Object^  sender, System::ResolveEventArgs^  args) {
    
            System::Reflection::AssemblyName^  assemblyName = gcnew System::Reflection::AssemblyName(args->Name);
    
            System::String^  resourceName = assemblyName->Name + ".dll";
    
            System::IO::Stream^  stream = System::Reflection::Assembly::GetExecutingAssembly()->GetManifestResourceStream(resourceName);
    
            array<Byte>^  assemblyData = gcnew array<Byte>((unsigned long) stream->Length);
            try {
                stream->Read(assemblyData, 0, assemblyData->Length);
            } finally {
                if (stream != nullptr) delete stream;
            }
    
            return System::Reflection::Assembly::Load(assemblyData);
        }
    

    Usage:

    //Put it in your constructor before InitializeComponent()
        MyClass(void) {
                    AppDomain::CurrentDomain->AssemblyResolve += gcnew System::ResolveEventHandler(this, &MyNameSpace::MyClass::currentDomainAssemblyResolve);
                    InitializeComponent();
                }
    

    So now it's no longer necessary satellite dlls to load your localized resources.