Combining multiple assemblies into a single EXE for a WPF application - wpf

I followed this tutorial to combine a few DLL's into my EXE.
http://www.digitallycreated.net/Blog/61/combining-multiple-assemblies-into-a-single-exe-for-a-wpf-application
The way I understand this works is:
- it starts by telling the compiler to embed (as embedded resources) each and every DLL that have their Local Copy set to True.
That part is working fine. It apparently doesn't "add" them as resources to my project (figure 1 in the tutorial kind of says otherwise), but I can tell that the size of my EXE is correct.
FYI, my program uses WPFtoolkit, in my case, that's 2 DLL's:
system.windows.controls.datavisualization.toolkit.dll
WPFToolkit.dll
Then, I set the Build Action of my App.xaml to Page, and made a program.cs file which I added to my project.
this is my project.cs:
using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D;
using System.Windows.Media.TextFormatting;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Shell;
using System.IO;
using System.Reflection;
using System.Globalization;
namespace Swf_perium {
public class Program {
//[System.Diagnostics.DebuggerNonUserCodeAttribute()]
//[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]
[STAThreadAttribute]
public static void Main() {
Swft_perium.App app = new Swf_perium.App();
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
app.InitializeComponent();
app.Run();
}
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
AssemblyName assemblyName = new AssemblyName(args.Name);
string path = assemblyName.Name + ".dll";
Console.WriteLine(path);
if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
{
path = String.Format(#"{0}\{1}", assemblyName.CultureInfo, path);
}
using (Stream stream = executingAssembly.GetManifestResourceStream(path))
{
if (stream == null)
return null;
byte[] assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
return Assembly.Load(assemblyRawBytes);
}
}
}
}
After I build the project, I run it off VS2013, no problem, since both DLL's have their local copy set to true. If I go in my debug folder, take both DLL's out and run the EXE off windows explorer, then the program instantly crashes because it can find the DLL's.
What this tutorial should allow me to do is being able to run that EXE by itself without the DLL's, so yeah, it doesn't work.
I added a console writeline of the path that are being read by the OnResolveAssembly method of my program.cs. And here's what I get:
4 times the same path:
"Swf_perium.resources.dll"
Obviously, when it gets to the Stream, it's null and the method then returns null.
I am trying to understand where these paths are coming from? I don't understand why 4? And why this path?
Has anyone ever tried this technique? Comments on the blog show pretty good success rate.
Does anyone have an idea?
I made several mistakes to get to this stage, but at this point I don't see what I am doing wrong.
Thanks
Steve
EDIT: following HB's guidance, here's what I did:
I took the MSBuild target "mod" out.
Set both references' copy local to FALSE.
Added both DLL as embedded resources manually. They're both into the "Resources" directory at the root of the project.
I set App.xaml build action back to "ApplicationDefinition".
And I excluded my program.cs out of the project.
and added this code to App.xaml.cs:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Reflection;
namespace Swf_perium
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public App()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
var execAssembly = Assembly.GetExecutingAssembly();
string resourceName = execAssembly.FullName.Split(',').First() + ".Resources." + new AssemblyName(args.Name).Name + ".dll";
Console.WriteLine(resourceName);
using (var stream = execAssembly.GetManifestResourceStream(resourceName))
{
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
}
}
now, the console prints out of the 2 DLL's filename, but not the other.. I am guessing that's why it's still not working..
that's where I'm at.
edit:
The DLL that doesn't show is not called by my code directly. it's a dependence from the first DLL. I took that second DLL out of references and resources.. If I set copy local to true for the first DLL (which my program actually uses), building the project generates both DLL at the root - in this case with both dlls generated the program works, funny thing is if I delete that second DLL, the program still works. So the problem isn't that second DLL but the first one.
the error I have (which I've had all along no matter what technique I use) is that my XAML is calling that namespace and can't find it!
edit:
Ok, well it still doesn't work. I've brought my program.cs back into the solution, set it as the entry point. And added the code suggested by HB into it.
I made sure that the assemblyresolve is done on the first line of the main so that's it's done before any wpf is done. I even added a 5s sleep just to make sure that the dll was loaded before any wpf happens. Still no go.
Either the dependence to the second DLL is what's causing a problem (?) or maybe the way I import the namespace in my XAML is incorrect. Do I need to specify that this namespace is embedded? and where it's located - i.e. its path?.
thanks

Perhaps look at Costura where it will do all the hard work of embedding assemblies for you.

Don't know your project structure but i usually add a directory for the assemblies to the root of the project and then add the dlls to that directory as embedded resource. I also then turn off the local copy of the references to make sure that it works.
Here is the code i use in my App.xaml.cs:
static App()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
var execAssembly = Assembly.GetExecutingAssembly();
string resourceName = execAssembly.FullName.Split(',').First() + ".ReferencedAssemblies." +
new AssemblyName(args.Name).Name + ".dll";
using (var stream = execAssembly.GetManifestResourceStream(resourceName))
{
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
Simply replace the ".ReferencedAssemblies." string according to the directory you placed the dlls in.
(Using the static constructor of the class makes sure that the event is hooked up before any code that potentially accesses referenced assemblies is executed, in your code i would move the hook to the first line of Main, that may already solve your problem.)

Related

LocalReport in full .net class library referenced in .net core project

I'm trying to build a service to export a rdlc Localreport from one of my actions in my webapi. Webapi is built on .net core 3.1
I know that reportviewer is not compatible with .net core, so to try and mitigate that I've got a class library project added to my project based on .net framework 4.7.2. Added the reference to the class library to my .net core webapi, so far so good. I'm able to call methods from my class library, no problem.
Now try adding LocalReport to a class in my class library....
using Microsoft.Reporting.WebForms;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Reports
{
public class RenderAction
{
public void GetOrderReport()
{
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
string p = Path.GetDirectoryName(path);
string reportPath = Path.Combine(p, "Reports","Order.rdlc");
if (!File.Exists(reportPath)) { return; }
var report = new LocalReport();
}
}
}
intellisense prompted to install Microsoft.Reporting.Viewer so I did so... When calling my function in runtime, I get the following error when creating the new instance of localreport:
System.MissingMethodException
HResult=0x80131513
Message=Method not found: 'Void System.AppDomainSetup.set_ActivationArguments(System.Runtime.Hosting.ActivationArguments)'.
Source=Microsoft.ReportViewer.Common
StackTrace:
at Microsoft.Reporting.ReportRuntimeSetupHandler.InitAppDomainPool(Evidence sandboxEvidence, PolicyManager policyManager)
at Microsoft.Reporting.LocalService..ctor(ILocalCatalog catalog, Evidence sandboxEvidence, PolicyManager policyManager)
at Microsoft.Reporting.ControlService..ctor(ILocalCatalog catalog)
at Microsoft.Reporting.WebForms.LocalReport..ctor()
at Myproject.Reports.RenderAction.GetOrderReport() in C:\Users\RudiGroenewald\source\repos\Myproject-Api-Common\Myproject_Api_Common\Myproject_Reports\RenderAction.cs:line 24
at Myproject.Api.Common.Controllers.ReportsController.Get() in C:\Users\RudiGroenewald\Source\Repos\Myproject-Api-Common\Myproject_Api_Common\Myproject_Api_Common\Controllers\ReportsController.cs:line 22
at Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(Object target, Object[] parameters)
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.SyncObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<<InvokeActionMethodAsync>g__Logged|12_1>d.MoveNext()
It seems like some sort of dll version mismatch or something... a bit stumped really.
Is it just not possible to get this working? My alternative is to have a full .net webapi, just for reportwriting, which I prefer not to do. Any thoughts on what I'm doing wrong?

Serializing anonymous types in Silverlight with json.net

I'm having some problems serializing an anonymous type only on the Silverlight platform. I have code on .net 4.0 and .netcf that works fine.
This line right here
Newtonsoft.Json.JsonConvert.SerializeObject(new { Something = "yup" });
throws an aptly named guy, JsonSerializationException:
Error getting value from 'Something' on '<>f__AnonymousType0`1[System.String]'.
I tried 4.0r1 and 4.0r2 - Am I doing something wrong or am I taking crazy pills?
The problem is that anonymous types are defined as internal classes by the compiler. JSON.NET relies on reflection to work, and in Silverlight reflection across assembly borders work only for public types (when used by partially trusted assemblies such as this one).
I think DataContractJsonSerializer as mentioned in the previous answer is the way to go in this case, since it's part of the framework and should have extra privileges.
Another thing to try is use dictionaries or ExpandoObject's instead of anonymous types, but YMMV.
Answer is simple;) Add [assembly: InternalsVisibleTo("Newtonsoft.Json")] to AssemblyInfo.cs and voila... I have exactly the same problem and this attribute solved my serialization/deserialization problem.
AssemblyInfo.cs
[assembly: InternalsVisibleTo("Newtonsoft.Json")]
Is there a specific reason why you want to use Json.NET? If not, you might want to try the built-in serializer (in the System.Runtime.Serialization namespace). I have to admit, I have never tried it with anonymous types, so I am not sure if this will be useful to you. Anyway, here is the class I use for serialization/deserialization:
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Json;
namespace GLS.Gui.Helper
{
public static class SerializationHelper
{
public static string SerializeToJsonString(object objectToSerialize)
{
using (MemoryStream ms = new MemoryStream())
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(objectToSerialize.GetType());
serializer.WriteObject(ms, objectToSerialize);
ms.Position = 0;
using (StreamReader reader = new StreamReader(ms))
{
return reader.ReadToEnd();
}
}
}
public static T Deserialize<T>(string jsonString)
{
using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonString)))
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
return (T)serializer.ReadObject(ms);
}
}
}
}
Maybe have a look at this http://whydoidoit.com/silverlight-serializer/ as I have used this to serialize many objects in Silverlight, although I cant remember if i did anonymous types with it.
To supplement the other answers with another workaround, note that the reflection (and so the serialization of anonymous types) will succeed when running with elevated trust.

Serializable attribute in silverlight 4

So do we or do we not have a Serializable attribute in silverlight 4? I have some confusing responses on the internet. When I try to use it in my code, i get a namespace error. These are my includes
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Runtime.Serialization;
I have the assemblies System ,System.Runtime.Serialization added to my project.
A follow up question is, if it is not available in Silverlight how do I correctly serialize a singleton? Since I was planning to use the example given here http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializable.aspx
Thanks
That's a .NET attribute which you can't use in Silverlight, but you can use DataContract to serialize.
For stand-alone (non-WCF) serialization/deserialization, there are three components which can be used:
System.Runtime.Serialization.DataContractSerializer (from System.Runtime.Serialization.dll)
System.Runtime.Serialization.Json.DataContractJsonSerializer (from System.ServiceModel.Web.dll)
System.Xml.Serialization.XmlSerializer (from System.Xml.Serialization.dll)
A simple example using the DataContractSerializer:
string SerializeWithDCS(object obj)
{
if (obj == null) throw new ArgumentNullException("obj");
DataContractSerializer dcs = new DataContractSerializer(obj.GetType());
MemoryStream ms = new MemoryStream();
dcs.WriteObject(ms, obj);
return Encoding.UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Position);
}
Example from this thread: http://forums.silverlight.net/forums/p/23161/82135.aspx

Reactive framework and WPF conflicts?

I do not have much experiece of WPF, and I consider it as an easier way to markup/coding UI in .net.
I have install the lastest Rx release, and play it using Console application without any issue, when I used it in a simple WPF application, it seems that 'Observable' is not liked by WPF...
I have added both reference to:
System.CoreEx
System.Reactive
when type in the button click's event handler, the VS intelligent happily pickup the Observable class and its static members, however when compile it, the 'Observable' becomes unknown to the context. Also the intelligence is gone for the class...
If I remove the above two reference, and add them back in again, the intelligence picked it up... when compile, same situation happens again...
I also installed the silverlight version of rx, not still not luck, please advice and help.
private void btnStart_Click(object sender, RoutedEventArgs e)
{
CheckBox[] chbServers = new CheckBox[] { chbMx1, chbMx2, chbMx3, chbMx4 };
ListBox[] lbxFiles = new ListBox[] { listBox1, listBox2, listBox3, listBox4 };
for (int i = 0; i < chbServers.Length; ++i)
{
if (chbServers[i].IsChecked == true)
{
string baseDir = string.Format(#"c:\maildrop\mx{0}", i + 1);
if (Directory.Exists(baseDir))
{
DirectoryInfo di = new DirectoryInfo(baseDir);
FileInfo[] fis = di.GetFiles("*.eml", SearchOption.AllDirectories);
//Observable.GenerateWithTime(
// 0,
// index => index <= fis.Length,
// index => index + 1,
// index => fis[index].Name,
// _ => TimeSpan.FromMilliseconds(300)
// )
// .ObserveOnDispatcher()
// .Subscribe(name =>
// {
// // delete the file
// lbxFiles[i].Items.Remove(name);
// });
}
}
}
}
The commented code is the piece which cause problem...
using System;
using System.Collections.Generic;
using System.Disposables;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Data.SqlClient;
all namespace refereced are as above...
WPF applications use the .NET 4 Client Profile by default, which was not supported by Rx until the latest release. Changing your project settings to use the full .NET 4 framework will allow you to compile.

Debugging silverlight in a WPF app

I am developing a WPF app that contains a webbrowser control that loads a silverlight application. I would like to be able to launch the app from visual studio (F5) and have the debugger attach to the silverlight code. However, I've not had any luck with this.
The best I can currently do is to launch the app without attaching, then once it is up and running, attach to the process manually with silverlight as the specified type of code to debug, and this works. (When I cause the web browser control to load the silverlight app, it will hit breakpoints in my silverlight code). I've written some macros to automate this launching/attaching somewhat, but it still isn't the best.
I've tried specifying the WPF app as the external program to run when launching/debugging the silverlight app, but Visual Studio attaches to the process wanting to debug the managed .NET code.
Any ideas? Ideally, I would really like to attach to the process and debug both the managed .NET and the silverlight code, but I don't think this is possible. I'd really like to automatically be attached to the silverlight code at launch so that I can easily debug all issues with the silverlight app, including those that occur on load.
Thanks for your ideas Brandorf and fatty. Brandorf's almost gets me to where I wanted to go, but does require that my SL app be capable of running on its own. I really want to have only the one app, which is both wpf and silverlight, with the SL side being debugged.
A long time after I asked this question (I forgot I had asked it here), I actually pieced together a solution that I'm really happy with. I use visual studio automation within the WPF/.NET side of my app, to find all running instances of visual studio, figure out which one produced my exe (since it typically sits in a folder below the vcproj/sln folder), and then use visual studio automation to have that VS attach to the app, debugging silverlight code. After this is done, I then load my silverlight content.
It works really well. You end up with an app that goes and finds a debugger to attach to itself every time it runs (so you probably want this code only in a debug build, or somehow able to be turned off). So you just launch the app with ctrl-F5 (launch without debugging) from visual studio whenever you want to debug the silverlight side.
Here's my code:
#if DEBUG
using System;
using System.Collections.Generic;
using System.Collections;
using System.Runtime.InteropServices;
using System.IO;
namespace Launcher
{
//The core methods in this class to find all running instances of VS are
//taken/inspired from
//http://www.codeproject.com/KB/cs/automatingvisualstudio.aspx
class DebuggingAutomation
{
[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(int reserved,
out UCOMIRunningObjectTable prot);
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(int reserved,
out UCOMIBindCtx ppbc);
///<summary>
///Get a snapshot of the running object table (ROT).
///</summary>
///<returns>
///A hashtable mapping the name of the object
///in the ROT to the corresponding object
///</returns>
private static Hashtable GetRunningObjectTable()
{
Hashtable result = new Hashtable();
int numFetched;
UCOMIRunningObjectTable runningObjectTable;
UCOMIEnumMoniker monikerEnumerator;
UCOMIMoniker[] monikers = new UCOMIMoniker[1];
GetRunningObjectTable(0, out runningObjectTable);
runningObjectTable.EnumRunning(out monikerEnumerator);
monikerEnumerator.Reset();
while (monikerEnumerator.Next(1, monikers, out numFetched) == 0)
{
UCOMIBindCtx ctx;
CreateBindCtx(0, out ctx);
string runningObjectName;
monikers[0].GetDisplayName(ctx, null, out runningObjectName);
object runningObjectVal;
runningObjectTable.GetObject(monikers[0], out runningObjectVal);
result[runningObjectName] = runningObjectVal;
}
return result;
}
/// <summary>
/// Get a table of the currently running instances of the Visual Studio .NET IDE.
/// </summary>
/// <param name="openSolutionsOnly">
/// Only return instances that have opened a solution
/// </param>
/// <returns>
/// A list of the ides (as DTE objects) present in
/// in the running object table to the corresponding DTE object
/// </returns>
private static List<EnvDTE.DTE> GetIDEInstances(bool openSolutionsOnly)
{
var runningIDEInstances = new List<EnvDTE.DTE>();
Hashtable runningObjects = GetRunningObjectTable();
IDictionaryEnumerator rotEnumerator = runningObjects.GetEnumerator();
while (rotEnumerator.MoveNext())
{
string candidateName = (string)rotEnumerator.Key;
if (!candidateName.StartsWith("!VisualStudio.DTE"))
continue;
EnvDTE.DTE ide = rotEnumerator.Value as EnvDTE.DTE;
if (ide == null)
continue;
if (openSolutionsOnly)
{
try
{
string solutionFile = ide.Solution.FullName;
if (!String.IsNullOrEmpty(solutionFile))
{
runningIDEInstances.Add(ide);
}
}
catch { }
}
else
{
runningIDEInstances.Add(ide);
}
}
return runningIDEInstances;
}
internal static void AttachDebuggerIfPossible()
{
if (System.Diagnostics.Debugger.IsAttached)
{
//Probably debugging host (Desktop .NET side), so don't try to attach to silverlight side
return;
}
var ides = GetIDEInstances(true);
var fullPathToAssembly = System.Reflection.Assembly.GetExecutingAssembly().Location;
var potentials = new List<EnvDTE.DTE>();
foreach (var ide in ides)
{
var solutionPath = ide.Solution.FullName;
var topLevelSolutionDir = Path.GetDirectoryName(solutionPath);
var assemblyName = fullPathToAssembly;
if (assemblyName.StartsWith(topLevelSolutionDir, StringComparison.OrdinalIgnoreCase))
{
potentials.Add(ide);
}
}
EnvDTE.DTE chosenIde = null;
//If you have multiple ides open that can match your exe, you can come up with a scheme to pick a particular one
//(eg, put a file like solution.sln.pickme next to the solution whose ide you want to debug). If this is not a
//concern, just pick the first match.
if (potentials.Count > 0)
{
chosenIde = potentials[0];
}
var dbg = chosenIde != null ? (EnvDTE80.Debugger2)chosenIde.Debugger : null;
if (dbg != null)
{
var trans = dbg.Transports.Item("Default");
var proc = (EnvDTE80.Process2)dbg.GetProcesses(trans, System.Environment.MachineName).Item(Path.GetFileName(fullPathToAssembly));
var engines = new EnvDTE80.Engine[1];
engines[0] = trans.Engines.Item("Silverlight");
proc.Attach2(engines);
}
}
}
}
#endif
It's a bit of a shot in the dark, but assuming your silverlight app is capable of running on its own, you can, under your solution settings, set visual studio to start both apps together, and you should be attached to both of them.
If you can't add the Silverlight project to your solution (which will start debugging automatically), you might be able to make use of this tip. It will load both projects at the same time
http://saraford.net/2008/07/28/did-you-know-you-can-start-debugging-multiple-projects-268/

Resources