ExcelDnaPack.exe that packs a C++/CLI library - excel

I used ExcelDNA to create an Excel addin, packed into a single xll using ExcelDnaPack.exe. It works beautifully, until I add a reference to a dll created with a C++/CLI project (called CPPLibrary.dll)
When I pack the CPPLibrary.dll the addin complains at runtime that it cannot find the dll. If I leave CPPLibrary.dll unpacked and just copy it in the same directory as the -packed.xll everything works.
Is there a way to successfully pack a C++/CLI project into an ExcelDNa addin?
Thanks.

I ended up embedding the c++/cli as a resource, and in realtime when the excel dna addin gets loaded I extract the embedded dll to a file in disk and do a Assembly.LoadFrom(extracted path)
Like this:
private static void ExtractAndLoadCPPLibrary()
{
var executingAssembly = Assembly.GetExecutingAssembly();
string resource_path = "mydnaassembly.Embedded.CPPLibrary.dll";
var version = executingAssembly.GetName().Version.ToString().Replace(".","_");
var output_Path = Path.Combine(Path.GetTempPath(), "CPPLibrary_" + version + ".dll");
if (!File.Exists(output_Path))
{
using (Stream cpplibrary_input = executingAssembly.GetManifestResourceStream(resource_path))
using (var cpplibrary_output = File.Create(output_Path))
{
cpplibrary_input.CopyTo(cpplibrary_output);
}
}
Assembly assembly = Assembly.LoadFrom(output_Path);
}

Related

Resolving a TypeReference to a TypeDefinition in Mono.Cecil fails with Assembly Resolution Error

I'm trying to get a Mono.Cecil TypeDefinition from a .NET type and not having any luck.
I'm using code like this:
var type = typeof(MarkdownMonster.AppConfiguration);
var a = AssemblyDefinition.ReadAssembly(type.Assembly.Location);
var tr = a.MainModule.Import(type); // this seems to work
var td = tr.Resolve(); // fails
but it fails with an assembly resolution error:
GetConfigurationPropertiesTest [0:29.990] Failed: Mono.Cecil.AssemblyResolutionException : Failed to resolve assembly: 'MarkdownMonster, Version=1.18.11.0, Culture=neutral, PublicKeyToken=null'
Mono.Cecil.AssemblyResolutionException : Failed to resolve assembly: 'MarkdownMonster, Version=1.18.11.0, Culture=neutral, PublicKeyToken=null'
at Mono.Cecil.BaseAssemblyResolver.Resolve(AssemblyNameReference name, ReaderParameters parameters)
at Mono.Cecil.DefaultAssemblyResolver.Resolve(AssemblyNameReference name)
at Mono.Cecil.MetadataResolver.Resolve(TypeReference type)
at Mono.Cecil.TypeReference.Resolve()
at Westwind.TypeImporter.TypeParser.ParseObject(Type type, Boolean dontParseMembers)
The assembly is obviously there, since the TypeReference import seems to work and produces a valid TypeReference.
The assembly in question is an EXE, and just for kicks I renamed it to a DLL but that had no effect.
After a bit of back and forth experimenting I found one (ugly) solution is to create a custom type resolver and basically forcing a hard type reference into it. It seems Mono.Cecil is able to resolve transient dependencies once it's found the main assembly, but not the top level reference.
To make this work I basically pass in the already resolved assembly reference. In my case I know the only reference I will need to return will be the top level reference so I hard code this. A more realistic example will have to use AssemblyDefinition.ReadAssembly() to read an assembly off disk or from a stream.
Here's is the code to create the AssemblyResolver:
public class MonoAssemblyResolver : IAssemblyResolver
{
public AssemblyDefinition AssemblyDefinition;
public AssemblyDefinition Resolve(AssemblyNameReference name)
{
return AssemblyDefinition;
}
public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
{
return AssemblyDefinition;
}
public void Dispose()
{
AssemblyDefinition = null;
}
}
Note the cheat to pass in an already resolved assembly reference.
To resolve I can now use the following code:
var a = AssemblyDefinition.ReadAssembly(type.Assembly.Location,
new ReaderParameters() { AssemblyResolver = resolver });
// assign the resolvedr
var resolver = new MonoAssemblyResolver();
resolver.AssemblyDefinition = a;
var tr = a.MainModule.Import(type: type);
var td = tr.Resolve(); // works now
This is crazy hacky, but can be adapted to be more generic (in my case not needed).
Still it would be much nicer if Mono.Cecil could automatically resolve the assembly - I don't understand why it's not finding the assembly in the first place since it lives in the current bin folder and the TypeReference can find it.
This is how it's normally done:
var assembly = #"c:\myassembly.dll";
var resolver = new DefaultAssemblyResolver();
// add .NET runtime dir for the sake of security
foreach (var dir in Directory.GetDirectories(RuntimeEnvironment.GetRuntimeDirectory(), "*", SearchOption.AllDirectories))
{
resolver.AddSearchDirectory(dir);
}
// add the assembly's directory
resolver.AddSearchDirectory(Path.GetDirectoryName(assembly));
var mod = AssemblyDefinition.ReadAssembly(assembly, new ReaderParameters { AssemblyResolver = resolver }).MainModule;
Regards

CMS.Ecommerce.IAddressExtensions obsolete

I'm porting some code over from a Kentico 8.2 build to a Kentico 11 website. One of the snippets that I am migrating makes use of a CMS.Ecommerce.IAddressExtensions extension method public static string GetStateCode(this IAddress address).
When I moved this over to my Kentico 11 build, Visual Studio finds that this extension method no longer exists: IAddress does not contain a definition for GetStateCode and no extension method could be found.. I poked around the Object Viewer for the CMS.Ecommerce DLL, and sure enough, no IAddressExtensions class is present.
Is there a workaround to look up the state code for a given address in Kentico 11?
As you found out, Kentico has removed the IAddressExtensions class when upgrading from version 10 to 11. Here is the page for the API change.
Based on my understanding, you will instead need to use the StateInfoProvider as below.
IAddress someAddress = /* snip */;
var stateInfo = StateInfoProvider.GetStateInfo(someAddress.AddressStateID);
var stateCode = stateInfo.StateCode;
You can then take this same logic and move it into your own extension class.
public static class IAddressExtensions
{
public static string GetStateCode(this IAddress address)
{
var stateInfo = StateInfoProvider.GetStateInfo(address.AddressStateID);
return stateInfo.StateCode;
}
}

Revit Api Load Command - Auto Reload

I'm working with the revit api, and one of its problems is that it locks the .dll once the command's run. You have to exit revit before the command can be rebuilt, very time consuming.
After some research, I came across this post on GitHub, that streams the command .dll into memory, thus hiding it from Revit. Letting you rebuild the VS project as much as you like.
The AutoReload Class impliments the revit IExteneralCommand Class which is the link into the Revit Program.
But the AutoReload class hides the actual source DLL from revit. So revit can't lock the DLL and lets one rebuilt the source file.
Only problem is I cant figure out how to implement it, and have revit execute the command. I guess my C# general knowledge is still too limited.
I created an entry in the RevitAddin.addin manifest that points to the AutoReload Method command, but nothing happens.
I've tried to follow all the comments in the posted code, but nothing seems to work; and no luck finding a contact for the developer.
Found at: https://gist.github.com/6084730.git
using System;
namespace Mine
{
// helper class
public class PluginData
{
public DateTime _creation_time;
public Autodesk.Revit.UI.IExternalCommand _instance;
public PluginData(Autodesk.Revit.UI.IExternalCommand instance)
{
_instance = instance;
}
}
//
// Base class for auto-reloading external commands that reside in other dll's
// (that Revit never knows about, and therefore cannot lock)
//
public class AutoReload : Autodesk.Revit.UI.IExternalCommand
{
// keep a static dictionary of loaded modules (so the data persists between calls to Execute)
static System.Collections.Generic.Dictionary<string, PluginData> _dictionary;
String _path; // to the dll
String _class_full_name;
public AutoReload(String path, String class_full_name)
{
if (_dictionary == null)
{
_dictionary = new System.Collections.Generic.Dictionary<string, PluginData>();
}
if (!_dictionary.ContainsKey(class_full_name))
{
PluginData data = new PluginData(null);
_dictionary.Add(class_full_name, data);
}
_path = path;
_class_full_name = class_full_name;
}
public Autodesk.Revit.UI.Result Execute(
Autodesk.Revit.UI.ExternalCommandData commandData,
ref string message,
Autodesk.Revit.DB.ElementSet elements)
{
PluginData data = _dictionary[_class_full_name];
DateTime creation_time = new System.IO.FileInfo(_path).LastWriteTime;
if (creation_time.CompareTo(data._creation_time) > 0)
{
// dll file has been modified, or this is the first time we execute this command.
data._creation_time = creation_time;
byte[] assembly_bytes = System.IO.File.ReadAllBytes(_path);
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(assembly_bytes);
foreach (Type type in assembly.GetTypes())
{
if (type.IsClass && type.FullName == _class_full_name)
{
data._instance = Activator.CreateInstance(type) as Autodesk.Revit.UI.IExternalCommand;
break;
}
}
}
// now actually call the command
return data._instance.Execute(commandData, ref message, elements);
}
}
//
// Derive a class from AutoReload for every auto-reloadable command. Hardcode the path
// to the dll and the full name of the IExternalCommand class in the constructor of the base class.
//
[Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
[Autodesk.Revit.Attributes.Regeneration(Autodesk.Revit.Attributes.RegenerationOption.Manual)]
public class AutoReloadExample : AutoReload
{
public AutoReloadExample()
: base("C:\\revit2014plugins\\ExampleCommand.dll", "Mine.ExampleCommand")
{
}
}
}
There is an easier approach: Add-in Manager
Go to Revit Developer Center and download the Revit SDK, unzip/install it, the check at \Revit 2016 SDK\Add-In Manager folder. With this tool you can load/reload DLLs without having to modify your code.
There is also some additional information at this blog post.
this is how you can use the above code:
Create a new VS class project; name it anything (eg. AutoLoad)
Copy&Paste the above code in-between the namespace region
reference revitapi.dll & revitapiui.dll
Scroll down to AutoReloadExample class and replace the path to point
your dll
Replace "Mine.ExampleCommand" with your plugins namespace.mainclass
Build the solution
Create an .addin manifest to point this new loader (eg.
AutoLoad.dll)
your .addin should include "FullClassName" AutoLoad.AutoReloadExample
This method uses reflection to create an instance of your plugin and prevent Revit to lock your dll file! You can add more of your commands just by adding new classes like AutoReloadExample and point them with seperate .addin files.
Cheers

Memory leak with dynamic code in COM Interop

For compatibility reasons between out legacy ASP code and new ASP.NET code, we have a bunch of .NET COM object that expose some of our .NET utilities to the ASP. In some cases we need to work with another COM objects inside of our .NET COM wrappers. To allow high flexibility and avoid PIA dependencies, we decided to use dynamic code to work with those COM objects.
Simplified C# COM object:
using System;
using System.Text;
using System.Runtime.InteropServices;
namespace TestCom
{
[ComVisible(true)]
[Guid("6DC92920-8C3C-4C81-A615-BD0E3A332024")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface ITestComObject
{
[DispId(1)]
string MyMethod(dynamic dictionary);
}
[ComVisible(true)]
[Guid("F52A463E-F03B-4703-860C-E86CDD6D04E3")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("TestCom.TestComObject")]
public class TestComObject : ITestComObject
{
string ITestComObject.MyMethod(dynamic dictionary)
{
StringBuilder sb = new StringBuilder();
if (dictionary != null)
{
foreach (object key in dictionary)
{
object p = dictionary[key];
if (p != null)
{
sb.AppendFormat("{0}={1}{2}", key, p, Environment.NewLine);
}
}
}
return sb.ToString();
}
}
}
Test ASP Page:
<%# Language=VBScript %>
<%
Dim testObj, params
Set testObj = Server.CreateObject("TestCom.TestComObject")
Set params = Server.CreateObject("Scripting.Dictionary")
params("lang") = "en-US"
params("num") = 42
Response.Write testObj.MyMethod(params)
Set testObj = Nothing
Set params = Nothing
%>
In normal case, the dynamic code would compile just once and subsequent calls would reuse it. However in our case it seems that the dynamic code is compiled on every single call. When i attach a memory profiler to IIS process i can clearly see additional objects from Microsoft.CSharp.RuntimeBinder.Semantics namespace appearing in gen2. This effectively causes a memory leak in our IIS process.
Any ideas how to fix this dynamic code compilation issue? Please note that rewriting all code to use PIA and COM interfaces is not always an option in our case.
I suggest you put any code which is susceptible to memory leak as a separate process - parent process communicate with such leaky processses via socket for instance. Then either freshly kick starts these leaky process on every call, or restarts them sometime during the evening!

Trying to experiment with some Resharper Open APIs

I was trying to read a C# source file and parse it using Resharper. I wanted to get the list of namespaces used in the file but I had an exception in this line.
ICSharpFile file = CSharpParserUtil.Parse(sCode);
Exception Details:
A first chance exception of type 'System.InvalidOperationException' occurred in
JetBrains.Platform.ReSharper.Shell.dll
The thread 0x1020 has exited with code 0 (0x0).
The thread 0x14c0 has exited with code 0 (0x0).
static void Main()
{
String sCode = File.ReadAllText(#"D:\ResharperTries\TestFile.cs");
try
{
ICSharpFile file = CSharpParserUtil.Parse(sCode);
IList<ICSharpNamespaceDeclaration> x = file.NamespaceDeclarations;
foreach (ICSharpNamespaceDeclaration value in x)
{
Console.WriteLine(value.ContainingNamespace.ShortName);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
Need some help regarding this issue.
Thanks
It is possible to do this with the parsers that are part of DXCore and CodeRush.
In version 10.2 we released stand-alone parser libraries for C# and VB (part of the freely downloadable DXCore), so referencing the parser libraries will make it very easy to do what you’re looking for.
If you need help with this, simply email support#devexpress.com with any questions.
Here is sample of code, which can be used to fill ListBox with namespaces, used in some file:
string filePath = #"InsertFilePathHere";
CSharp30Parser parser = new CSharp30Parser();
SourceFile fileNode = parser.ParseFile(filePath) as SourceFile;
if (fileNode == null || fileNode.UsingList == null)
return;
lbUsedNamespaces.Items.Clear();
for (int i = 0; i < fileNode.UsingList.Count; i++)
{
string strUsing = fileNode.UsingList.GetKey(i) as String;
if (String.IsNullOrEmpty(strUsing))
continue;
lbUsedNamespaces.Items.Add(strUsing);
}
Currently, it's impossible to use ReSharper API without Visual Stdio as in your example with console app.
You need to write R# plugin and it will be loaded into R# in Visual Studio.
Take a look at http://resharperpowertoys.codeplex.com/

Resources