I am creating a plugin for a product that loads plugin DLLs using Assembly.Load(byte[]). This is all very well and good, but it means that I have no conventional means of loading the debugging symbols to step through my code.
The crazy thing is, several months ago I was having the exact same issue and solved it - and boy was I proud of myself! So I know it can be done, I've just forgotten how!
I have a few vague memories of things I might have tried, but I can't tease the details out of my head:
.NET Reflector
Probably wrong though because I distinctly remember stepping through the original .cs file
Using IIS Express rather than Cassini
But this strikes me as a weird solution - the assembly is loaded from a byte-array, so the framework knows nothing about where the DLL came from or what an appropriate PDB might look like if it saw one. This problem should exist in any environment.
Loading the symbols manually through the "Modules" window
Tried this; I get "The symbol file xxxxx.pdb does not match the module" - because, of course, we're loading from a byte-array, not the DLL itself.
If your assembly is strongly named, you can put the assembly in the GAC. Strongly named assemblies are always loaded from the GAC, even if it is loaded via Assembly.Load(byte[]). Then just put your in symbols in C:\Windows\symbols\dll or where ever is convenient. I do this all the time to debug our own product's plugin DLLs which are loaded by another application in a similar manner.
You can use gacutil to install it in the GAC. Remember to remove it when you're done debugging or you might end up running tests against an old version you GAC'd and forgot about.
Related
0) How to debug unityContainer?
Im working on a legacy project on the firm, and all projects are loaded using dependency injection using unityContainer. I need to make improvements on the presentation layer, but I cannot debug the code, only the main project, witch loaded all modules.
The code used for loading modules(projects):
unityContainer.RegisterType("FrontEndModule", new InjectionMember[0]);
On the module, i register all project types like this:
unityContainer.RegisterType< IAboutPage, AboutPage>();
And then I run the main form:
Application.Run((Form) unityContainer.Resolve< IMainPage >() );
1) So there is any way to debug the code of the loaded projects?
2) Do I need to make any change to be able to debug?
I've tried to run the form directly, but then there is a lot of injections needed to run. Maybe I would be able to use another IoC framework that permit me to debug the code of loaded projects.
Thanks.
There is nothing special about Unity when you debug code.
To be able to steps through the code you need
PDB files matching to DLLs that you are using (often it means "rebuild locally" unless PDB published along with DLLs)
source code, preferably matching the version of the source used to build DLLs (VS will allow to use mismatched sources, but you'll likely get very poor experience when stepping through does not match what actually happening since line numbers in PDB no longer align with actual text source)
you may need to disable "Tools->Options->Debug->My code only" setting to allow stepping through/breakpoints in code outside of solution (that's depending on VS mood :) )
If you can't get PDBs you still can see exceptions and call-stacks (as this information is part of DLL metainfo).
Switching to another DI framework will not have any impact on that.
My .NET application can load multiple versions of the same assembly into memory. Assemblies are not signed but every time new copy of assembly is complied and loaded it automatically gets a new assembly version (in minor part). I do not have any issues with types since instantiation ob objects is under by control, so I know from which assembly the object is created.
This was working ever since VS 2003, but with the latest VS 2015 the debugging of this scenario is broken. All works fine while only single version of assembly is loaded into memory but whenever second version is being loaded all Locals/Watch windows become empty. And trying to evaluate any expression in QuickWatch gives a compiler exception "error CS1704: An assembly with the same simple name 'MyAssembly' has already been imported. Try removing one of the references (e.g. 'MyAssembly.dll') or sign them to enable side-by-side."
Here are screenshots of the same application with attached debuggers from VS2013 and VS2015 (when two assemblies are loaded):
VS2013 Debugger:
VS2015 Debugger:
And the selected parts from loaded assemblies list:
So this makes debugging with VS 2015 almost impossible.
Since originally this is a compliler error (which to my belief is used under the cover of VS 2015 debugger) internet search is not very useful. Here is the only link related to Debugger problem that I was able to find:
Visual Studio Debugger Failing to inspect variables. The difference to my case is that there having two assemblies in memory was a mistake while in my case this is an intent.
So now I am thinking on my options.
Of course, ideally I would like to have some patch to VS 2015 that will fix the issue. But being realistic I am not sure this will happen.
Signing assemblies (as compiler suggests) is not an option since assemblies are generated on clients' machines and it is not possible to provide them with a key for signing.
I could try to play with AppDomains to see if debugger could handle the case when assemblies are loaded to different domains. But even if it could, this would be quite a huge (and unplanned) change to my appliction.
So may be anyone could suggest some more ideas? Thnaks.
I've found a workaround for this problem. There is an option in Visual Studio "Debug - Options - General - Use the legacy C# and VB expression evaluator", enabling it restores previous debugger behaviour.
This option is described in VS 2015 Known Issues for different debugger issue but also works for described case.
I also filed a bug to Microsoft. I do not believe they will do anything about it, but here is the link just in case.
Assuming this is an option in your situation, you could try to rename the class or namespace before loading the assembly.
There are libraries that can modify assembly files: http://www.codeproject.com/Articles/20565/Assembly-Manipulation-and-C-VB-NET-Code-Injection
This of course assumes that you are loading the assembly and types at runtime.
I have a WPF application whose output is a.exe. This application is dependent on an external b.dll (whose source code I have access to).
Requirements:
The output should only be a.exe which should contain the dll. I don't want to provide my users with a separate dll (if it can be avoided)
I should be able to obfuscate the code. (I don't want anyone to be able to modify it).
Approaches tried:
I embedded b.dll inside a.exe, it worked. But I was not able to obfuscate the exe as it gave an error that it was unable to find b.dll.
I obfuscated a.exe and b.dll but it did not work. It was unable to find b.dll.
Alternate approach :
Is there any way that I can perhaps add the spruce code of b.dll to my project and have the dll be compiled to the exe itself rather than a separate dll.
Is it possible to make this alternate approach work or are there any other ways ?
If nothing works, I know that I can compile a and b separately, obfuscate a and provide b as a separate file (what I'm trying to avoid).
Apologies for the formatting issues, if any, I'm using the android app. Let me know if you need any details.
I have had great success with Eazfuscator.Net in the past.
http://www.gapotchenko.com/eazfuscator.net
To run it from the command line enter the following command:
Eazfuscator.Net.exe -n a.exe b.dll
It will combine the two files into a single exe. The main program will be able to access the dll.
You can even set up Visual Studio so that the command line above runs as a post compile event.
Assembly embedding may seem quite confusing, so here is how it's usually done:
The dependencies are obfuscated if needed.
The target assembly is obfuscated. At this point, the obfuscator is also instructed to embed certain dependencies as a part of obfuscation process.
As a result, the embedded assemblies are stored as a resource of the target assembly.
In order to load dependencies at runtime, obfuscators usually install a handler for AppDomain.AssemblyResolve event that is raised by CLR when it fails to resolve an assembly automatically.
The handler extracts and loads an embedded assembly from the resource.
That's it. A good obfuscation tool allows achieving that quite easily. I don't see why it wouldn't work in the case with WPF application. If there are problems, I would recommend contacting product support.
Another option is assembly merging. Unlike embedded, the merged assemblies become an inseparable part of the target assembly code. For this reason, the assembly merging often helps to achieve a better obfuscation coverage and application startup time comparing to embedding. Although it may look a better option, merging may sometimes break the application functionality.
Here's the scenario, I've extended MvcWebRazorHostFactory so I can do a little bit of extra view magic at build time. The exact magic doesn't matter.
The registration in my ~\Views\Web.config looks like so:
<host factoryType="StackExchange.MyNamespace.MyFactory, StackExchange.MyNamespace" />
There's to the appropriate assembly in the project, a copy of the assembly in a \lib folder, and I've confirmed that it's copied to \bin as expected. Furthermore, the actual view magic does happen when views are built so ASP.NET itself is actually finding everything.
What doesn't work in Visual Studio (2012)'s Razor Syntax Highlighting.
Above is a snapshot of how the syntax highlighting fails. As a rule, anything not in a namespace directly #using'd cannot be found (and thus gets the red squiggly underline) and the #model directive doesn't works. This points to some trouble getting at our automatically included namespaces in the Web.config.
The tooltip for the error on #model is "The name 'model' does not exist in the current context".
Through trial and error I've narrowed the root cause to the above host config section, although we have some other tweaks around Razor (a custom pageBaseType for example) it's that one line that breaks everything.
Things I've tried thus far:
Strong naming the assembly
Installing the assembly in the GAC
This page hints at this being necessary.
When strongly named my ~\Views\Web.config ended up looking like so:
<host factoryType="StackExchange.MyNamespace.MyFactory, StackExchange.MyNamespace, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b61d663b67b05bd2" />
I checked the Version, Culture, and PublicKeyToken against the GAC using "gacutil -l".
I suppose one other possible point of failure is the GAC itself, as there are ton of .NET versions on my box. Both the assembly and the web site are built with .NET 4.5 (both referencing MVC 4, Razor 2, and so on). The path to the gacutil I've been using while debugging is Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\gacutil.exe.
Occasionally during my winnowing to a root cause I'd get an error message to the effect "StackExchange.MyNamespace.MyFactory could not be found" on the first line of a view. The underline would be in the "Other Error" color (Purple in VS2012 Dark Theme). I could not reliably reproduce that, so I can't get a screenshot.
So my question is, does anyone know how to get Visual Studio (2012 again) to properly handle Razor Views with a custom host factory?
My current best guess at a fix is doing some really hacky Web.config replacements so I have one config while editing and another while debugging. Really not a fan of that idea though.
It seems like this should work, and that Visual Studio is just having troubling finding something it needs. I just don't know what that is.
Update after some more experimentation.
I've got more developers to reproduce that it's the <host pageFactoryType="...">, including in a vanilla MVC4 application (the above examples are, somewhat obviously, from the StackOverflow solution; which might have had some cruft).
We have found a work around though, it's to buy a ReSharper license. Not a great (or cheap) work around, but a work around none the less.
With ReSharper installed everything works with one caveat. If you have a custom <pages pageBaseType="..."> registration the type must be in the same project, or you don't get intellisense for #this.Model reference (the #model directive works though).
More updates.
Looks like a no-op HostFactory (one that extends MvcRazorHostFactory but doesn't override any methods or contain any other code) works fine if you strong name it and install it in the GAC. I believe I was using the wrong GAC when testing that earlier, but (with some outside prodding) was able to get it working using the explicitly x64 gacutil.
Now the trouble is figuring out what's wrong with my particular host factory; more details coming as they're discovered.
After some discussion with Microsoft, we found the root cause of this problem.
As usual, it was my code.
The root error was an MVC3 reference in the custom factoryType, despite the project (and everything around it) being MVC4.
To sum everything up, to get Visual Studio IntelliSense working in .cshtml files with a custom <host factoryType>:
You must strong name your assembly
You must install the assembly in the GAC
We still debug and deploy against a local copy, but VS will look in the GAC
You must have all the right references*
Be aware that you'll need to restart Visual Studio after installing into the GAC, simply reloading the solution is not (usually) sufficient. We've also found that some (but not all) ReSharper installations will need their cache's cleared, to be on the safe side clear the cache.
One minor trip up when adding a reference to an assembly that resides in the GAC is the Visual Studio won't copy it to your output directory by default, this may work for you but caused problems for us. You can change this in the properties of the reference.
Also, Visual Studio will give you a number error messages (the "Other Error" purple underline I was unable to reliably reproduce earlier) if your custom factory throws any exceptions; except perhaps in it's constructor (that seemed a little inconsistent).
Where you get no help is the case we were in, where the type itself has serious issues. It was just a coincidence that things worked at runtime (the interfaces we're playing with didn't change between MVC3 and MVC4, and weak naming did the rest).
*If you're like us and have MVC beta 1 through 4 installed, be aware of the PEBKAC in this step.
So I recently updated my software and with the new version I supply a new dll-file, lets call it My.dll. Now, the old version works just fine on every computer I have tried.
The problems began with the new version. Specifically, so far on at least one computer, it states that "Could not load file or assembly My.dll". This even happens when I have dropped a copy of the software on a network drive and run the software directly from there. It works on every other computer but one, which still gives the exact same error where other computers work fine.
The dll in question is even in the same directory as the executable, so I'm really quite bummed here. I tried to google around a bit as well, but all the issues I found were related to ASP.NET specifically. Any ideas on how to go about finding the problem would be much appreciated.
It is possible that the computer in question has a DLL added to it's Global assembly cache. This would take priority over the DLL in the same folder.
More information about the GAC: http://msdn.microsoft.com/en-us/library/yf1d93sz(v=VS.100).aspx
Is there an old copy of the DLL lying around? Perhaps with a different name? I had a similar issue when I changed the name of a dll. Internally, the namespaces were the same.
In my case, an older version of the DLL was still there. .NET got confused with two assemblies in the bin directory having the exact same namespaces and classes, couldn't decide on which to load, and threw an exception.
Removing the older version of the dll solved the issue.
Use the Assembly binding log viewer and set it to log failures. This will give you some clues as to why it is not loading.
You could take a look at the error log using the Assembly Binding Log Viewer. First you have to turn on logging.
--Right Click on Project
--Goto Properties->Build tab
--Change Platform and Platform Target to Any CPU, Save and run