Jak C++ Windows dll może być scalony w C# aplikacji EXE?

Mam program Windows C#, który używa C++ dll dla danych i/O. moim celem jest wdrożenie aplikacji jako pojedynczego EXE.

Jakie są kroki tworzenia takiego pliku wykonywalnego?

 32
Author: Noah, 2008-09-16

8 answers

Wdrożenie pojedynczego zestawu kodu zarządzanego i niezarządzanego Niedziela, Luty 4, 2007

Programiści. NET uwielbiają wdrażanie xcopy. I uwielbiają pojedyncze elementy montażowe. Przynajmniej zawsze czuję się trochę nieswojo, jeśli muszę użyć jakiegoś komponentu i muszę pamiętać listę plików, które również zawierają się z głównym składem tego komponentu. Więc kiedy ostatnio musiałem opracować komponent zarządzanego kodu i musiałem go powiększyć o niezarządzany kod z DLL C (thx dla Marcusa Heege za pomoc tym!), Myślałem o tym, jak ułatwić wdrożenie dwóch bibliotek DLL. Gdyby to były tylko dwa zespoły, mógłbym użyć ILmerge do spakowania ich w jednym pliku. Ale to nie działa w przypadku mieszanych komponentów kodu z zarządzanymi, jak również niezarządzanymi bibliotekami DLL.

Więc oto, co wymyśliłem dla rozwiązania:

Dołączam dowolne biblioteki DLL, które chcę wdrożyć z moim składnikiem głównym jako osadzonymi zasobami. Następnie skonfigurowałem konstruktor klasy, aby wyodrębnić te biblioteki DLL, jak poniżej. Na Klasa ctor jest wywoływana tylko raz w ramach każdego AppDomain więc jej neglible overhead, myślę.

namespace MyLib
{
    public class MyClass
    {
        static MyClass()
        {
            ResourceExtractor.ExtractResourceToFile("MyLib.ManagedService.dll", "managedservice.dll");
            ResourceExtractor.ExtractResourceToFile("MyLib.UnmanagedService.dll", "unmanagedservice.dll");
        }

        ...

W tym przykładzie umieściłem dwie biblioteki dll jako zasoby, jedna jest niezarządzaną biblioteką DLL, a druga zarządzaną biblioteką DLL (tylko w celach demonstracyjnych), aby pokazać, jak ta technika działa dla obu rodzajów kodu.

Kod do wyodrębniania bibliotek DLL do własnych plików jest prosty:

public static class ResourceExtractor
{
    public static void ExtractResourceToFile(string resourceName, string filename)
    {
        if (!System.IO.File.Exists(filename))
            using (System.IO.Stream s = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
                using (System.IO.FileStream fs = new System.IO.FileStream(filename, System.IO.FileMode.Create))
                {
                    byte[] b = new byte[s.Length];
                    s.Read(b, 0, b.Length);
                    fs.Write(b, 0, b.Length);
                }
    }
}

Praca z takim zarządzanym złożeniem kodu jest taka sama jak zwykle-prawie. Ty reference it (tutaj: ManagedService.dll) w głównym projekcie komponentów (tutaj: MyLib), ale ustaw właściwość Copy Local NA false. Dodatkowo łączysz się w złożeniu jako istniejący element i ustawiasz akcję budowania na zasób osadzony.

Dla kodu niezarządzanego (tutaj: UnmanagedService.dll) wystarczy połączyć w DLL jako istniejący element i ustawić akcję budowania do osadzonego zasobu. Aby uzyskać dostęp do jego funkcji, użyj atrybutu DllImport jak zwykle, np.

[DllImport("unmanagedservice.dll")] public extern static int Add(int a, int b);
To wszystko! Jak najszybciej tworzysz pierwszą instancję klasy za pomocą statycznego ctor wbudowane biblioteki DLL są wyodrębniane do własnych plików i są gotowe do użycia tak, jakbyś je wdrożył jako oddzielne pliki. Tak długo, jak masz prawa zapisu do katalogu execution, powinno to działać dobrze dla Ciebie. Przynajmniej dla kodu prototypowego myślę, że ten sposób wdrożenia pojedynczego zestawu jest dość wygodne. Smacznego!

Http://weblogs.asp.net/ralfw/archive/2007/02/04/single-assembly-deployment-of-managed-and-unmanaged-code.aspx

 16
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-28 21:26:32

Try boxedapp ; pozwala na załadowanie wszystkich bibliotek DLL z pamięci. Wydaje się również, że można nawet osadzić. NET runtime. Dobrze jest stworzyć naprawdę samodzielne aplikacje...

 3
Author: CharlesB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-23 09:38:22

Próbowałeś ILMerge? http://research.microsoft.com/~mbarnett / ILMerge. aspx

ILMerge to narzędzie, które może być używane do łączenia wielu zestawów. NET w jeden zestaw. Jest on dostępny bezpłatnie na stronie Narzędzia i narzędzia w centrum programistycznym Microsoft.NET Framework.

Jeśli budujesz bibliotekę c++ z flagą /clr (całość lub część C++ / CLI), to powinno działać:

ilmerge /out:Composite.exe MyMainApp.exe Utility.dll

Nie będzie działać ze zwykłym (natywny) Windows DLL jednak.

 1
Author: Raithlin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 13:59:28

Po prostu kliknij prawym przyciskiem myszy projekt w Visual Studio, wybierz Właściwości projektu -> Zasoby - > Dodaj zasób - > Dodaj istniejący plik… I dołącz poniższy kod do swojej aplikacji.xaml.cs lub odpowiednik.

public App()
{
    AppDomain.CurrentDomain.AssemblyResolve +=new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}

System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    string dllName = args.Name.Contains(',') ? args.Name.Substring(0, args.Name.IndexOf(',')) : args.Name.Replace(".dll","");

    dllName = dllName.Replace(".", "_");

    if (dllName.EndsWith("_resources")) return null;

    System.Resources.ResourceManager rm = new System.Resources.ResourceManager(GetType().Namespace + ".Properties.Resources", System.Reflection.Assembly.GetExecutingAssembly());

    byte[] bytes = (byte[])rm.GetObject(dllName);

    return System.Reflection.Assembly.Load(bytes);
}

Oto mój oryginalny wpis na blogu: http://codeblog.larsholm.net/2011/06/embed-dlls-easily-in-a-net-assembly/

 1
Author: Lars Holm Jensen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-15 18:25:39

Użyj Fody.Costura nuget

  1. otwórz swoje rozwiązanie -> projekt - > Zarządzaj pakietami Nuget
  2. Szukaj Fody.Costura
  3. Skompiluj Twój projekt.

To jest to !

Źródło: http://www.manuelmeyer.net/2016/01/net-power-tip-10-merging-assemblies/

 1
Author: automan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-30 20:32:10

Thinstall jest jednym z rozwiązań. W przypadku natywnej aplikacji windows sugerowałbym osadzenie DLL jako obiektu zasobów binarnych, a następnie rozpakowanie go w czasie wykonywania, zanim będzie to potrzebne.

 0
Author: titanae,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-16 13:40:44

Inteligentny montaż może zrobić to i więcej. Jeśli dll ma niezarządzany kod, to nie pozwoli Ci scalić biblioteki DLL do jednego zestawu, zamiast tego może osadzić wymagane zależności jako zasoby do głównego exe. Jej druga strona, nie jest wolna.

Możesz to zrobić ręcznie, osadzając dll do swoich zasobów, a następnie opierając się na Assembly AppDomain ResolveHandler. Jeśli chodzi o Mixed mode DLL, znalazłem wiele wariantów i smaków ResolveHandler podejście nie działa dla mnie (wszystkie, które czytaj dll bajtów do pamięci i odczytu z niej). Wszyscy pracowali dla zarządzanych bibliotek DLL. Oto, co dla mnie zadziałało:

static void Main()
{
    AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
    {
        string assemblyName = new AssemblyName(args.Name).Name;
        if (assemblyName.EndsWith(".resources"))
            return null;

        string dllName = assemblyName + ".dll";
        string dllFullPath = Path.Combine(GetMyApplicationSpecificPath(), dllName);

        using (Stream s = Assembly.GetEntryAssembly().GetManifestResourceStream(typeof(Program).Namespace + ".Resources." + dllName))
        {
            byte[] data = new byte[stream.Length];
            s.Read(data, 0, data.Length);

            //or just byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);

            File.WriteAllBytes(dllFullPath, data);
        }

        return Assembly.LoadFrom(dllFullPath);
    };
}

Kluczem jest zapis bajtów do pliku i ładowanie z jego lokalizacji. Aby uniknąć problemów z kurczakiem i jajkiem, musisz upewnić się, że zadeklarowałeś obsługę przed uzyskaniem dostępu do assembly i że nie uzyskasz dostępu do członków assembly (lub nie tworzysz instancji czegokolwiek, co ma do czynienia z assembly) wewnątrz części ładującej (rozwiązującej assembly). Zadbaj również o to, aby GetMyApplicationSpecificPath() nie była żadną temp katalog, Ponieważ pliki tymczasowe mogą być próbowane do usunięcia przez inne programy lub przez siebie (nie, że zostanie usunięty, gdy program ma dostęp do dll, ale przynajmniej jego uciążliwość. AppData jest dobra lokalizacja). Należy również zauważyć, że trzeba zapisać bajty za każdym razem, nie można załadować z lokalizacji tylko ' bo dll już tam mieszka.

Jeśli montaż jest w pełni niezarządzany, możesz zobaczyć ten link lub to jak załadować takie biblioteki DLL.

 0
Author: nawfal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:32:23

PostBuild z Xenocode może spakować zarówno zarządzane, jak i niezaangażowane w jeden exe.

 -2
Author: Joel Lucsy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-10 19:58:15