2

As stated here im trying to embed the dlls in the exe application in order to just distribute one exe, but when i try to run my application on a xp machine with full .NET 4 installed it just crashes with no error, im placing the following code on the main method

[STAThread]
        static void Main()
        {
            AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
            {
                String resourceName = "AssemblyLoadingAndReflection." + new AssemblyName(args.Name).Name + ".dll";

                using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
                {
                    Byte[] assemblyData = new Byte[stream.Length];
                    stream.Read(assemblyData, 0, assemblyData.Length);
                    return Assembly.Load(assemblyData);
                }
            };

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new frmrPrincipal());
        }

i have a folder named dlls where im placing

Functions.Shared.dll
Alex.UI.dll
Alex.Controls.dll

and im setting its Build Action to "Embedded Resource".

if i remove that piece of code and set the dlls to be included by click once installer it works fine. btw im Using .NET Framework 4 Full profile and VS2010 SP1

dalexsoto
  • 3,412
  • 1
  • 27
  • 43
  • There is no point whatsoever in merging DLLs when you use ClickOnce. It doesn't mind multiple DLLs. Rather the opposite, less to download for an update. – Hans Passant Mar 21 '11 at 18:17
  • well I won't be using ClickOnce to distribute my application, I want just to distribute the exe file – dalexsoto Mar 21 '11 at 18:20
  • Okay, call it setup.exe, like the one that a Setup project creates. Also takes care of installing .NET if necessary, creating a shortcut to help your user getting it started, file associations, etcetera. – Hans Passant Mar 21 '11 at 18:23
  • Ok Ok you got me on that one :) your answer is dead-on but anyways whats wrong in the code?? any ideas? – dalexsoto Mar 21 '11 at 18:26

4 Answers4

3

The jitter goes kaboom when it tries to jit the Main() method. AssemblyResolve isn't registered yet, chicken and egg problem. You can only use types in Main that are available, so you'll have to stay away from frmrPrincipal. Using a little helper method can solve that problem, but now you'll have to suppress inlining with [MethodImpl]. You are also shooting your foot by not allowing ngen.exe to do its job.

using System.Runtime.CompilerServices;
...
    [STAThread]
    static void Main()
    {
        AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
        {
            // etc..
        }
        AvoidJitterBombing();
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private static void AvoidJitterBombing() {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new frmrPrincipal());
    }
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • i need more explanation on how to fix it, a code example or a better guidance needed, this is my firs encounter with reflection :( – dalexsoto Mar 21 '11 at 22:58
  • I'm trying to talk you out of it. Not doing a very good job of it, obviously. At least find out what wonderful things ngen.exe can do for your program. It googles well. – Hans Passant Mar 21 '11 at 23:03
  • Well i understand what sgen.exe does, (thanks for the tip) but is there any way to make this code work?? or the author of CLR via C# its just wrong? – dalexsoto Mar 22 '11 at 01:26
  • Okay, I have to give up when Richter's name gets dragged through the mud. Post updated. – Hans Passant Mar 22 '11 at 02:12
  • Ok, it worked but before i accept your answer, please tell me if im understanding correctly whats going on, as you are disabling inlining sgen wont be optimizing the code and whatever code that derives from frmrPrincipal will hurt on my application performance? – dalexsoto Mar 22 '11 at 04:14
  • You called it "sgen" twice now, it is Ngen.exe. You are missing out on the option of having the code pre-compiled. – Hans Passant Mar 22 '11 at 04:20
  • im very sorry as im working with the XML Serializer too im kinda dizzy right now (long working weekend) ok thanks a lot for your patience – dalexsoto Mar 22 '11 at 04:25
  • I guess I will stick to ILMerge – dalexsoto Mar 22 '11 at 06:31
0

I suspect you are trying to load a dll with unmanaged code. For managed dlls merely reading and loading the assembly in memory would do or you can load from a certain location without even reading. For mixed mode assemblies, I had success only by writing the bytes to a file and loading from its location.

class Program
{
    [STAThread]
    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);
        };

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new frmrPrincipal());
    }
}

where Program is the class name. To avoid chicken and egg problem, you have to ensure you declare the handler before accessing assembly and that you do not access the assembly members (or instantiate anything that has to deal with the assembly) inside the loading (assembly resolving) part. Also take care to ensure GetMyApplicationSpecificPath() is not any temp directory since temp files could be attempted to get erased by other programs or by yourself (not that it will get deleted while your program is accessing the dll, but at least its a nuisance. AppData is good location). Also note that you have to write the bytes each time, you cant just load from location just 'cos the dll already resides there.

nawfal
  • 70,104
  • 56
  • 326
  • 368
0

I played with this recently, starting from this or a very similar piece of code, and I think the construction of the resourceName is wrong. Try putting a breakpoint at the beginning of the resolve method, and then calling GetManifestResourceNames() on the executing assembly to see what name it is actually giving your embedded resources.

I suspect it's probably "dlls.Functions.Shared" and so forth instead of "AssemblyLoadingAndReflection.Functions.Shared"

Paul Phillips
  • 6,093
  • 24
  • 34
0

You might take a look at .NETZ, we used for WinForms application deployment along with ClickOnce.

Kalman Speier
  • 1,937
  • 13
  • 18
  • You should [download](http://madebits.com/netz/download.php) the source and compile with .NET Framework 4.0, because the binaries on theier download page has been compiled with .NET 1.x and 2.0 only. – Kalman Speier Mar 23 '11 at 09:24