I have two apps. They share common features and addons. To be compatible, both are using same namespace structure. The apps loads addons as cs and compiles them to dll.
Same addons as cs works in both apps - loaded from the source and compiled to dll. On the next run, the apps load the dll files only.
I want to allow distribution of addons as dll only without the source code. The problem is that if a dll is compiled form the first app, it cannot be loaded from the second.
On the image below, I have "Adaptable MACD" compiled form each app separately.
When I try to load the first, from the second app, I receive the following error message:
"Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information."
This is my code:
public void LoadDllIndicator(string dllPath, out string errorMessages)
{
errorMessages = string.Empty;
Assembly assembly = Assembly.LoadFrom(dllPath);
try
{
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{
if (!typeof (IIndicator).IsAssignableFrom(type))
continue;
var newIndicator = Activator.CreateInstance(type) as Indicator;
if (newIndicator == null)
{
errorMessages = "Cannot load: " + dllPath;
return;
}
newIndicator.Initialize(SlotTypes.NotDefined);
newIndicator.CustomIndicator = true;
newIndicator.LoaddedFromDll = true;
IntegrateIndicator(dllPath, out errorMessages, newIndicator);
}
}
catch (Exception exception)
{
errorMessages = "ERROR: Loading '" + Path.GetFileName(dllPath) + "': " + exception.Message;
if (exception.InnerException != null && !string.IsNullOrEmpty(exception.InnerException.Message))
errorMessages += " " + exception.InnerException.Message;
}
}
How to make possible, dll compiled form one of the apps to work in the other?
Related
Published and installed program using LinqToExcel throws an "Exception has been thrown by the target of an invocation" exception. Inner exeption is not displayed but on my computer if I run exe file separately w/o another source files there is Could not load file or assembly linqtoexcel. but it is only on my PC when I tried to run it separately. But I beleave it is similar?
Both PCs are Win10, Access DB Engine 64bits or 64bits Offices are installed.
public void ImportNewData(String file)
{
ExcelConnector excel = new ExcelConnector(file);
foreach(var result in excel.ReadNewData())
{
this.loopsList.Add((Loop) result);
}
}
public IEnumerable ReadNewData() {
try
{
var query = from a in this.ExcelConnection.Worksheet < Loop > ("Data")
select a;
return query;
}
catch (Exception exeption)
{
MessageBox.Show(exeption.Message + "\n" + exeption.InnerException.Message + "\n" + exeption.InnerException.Source, "Warning");
return null;
}
}
I expect it will run on any Windows machine :)
I unchecked this check box and now it works also with 64bit Office.
We need to run sequentially database updates. Each database update will be its own DLL, and needs to retain a dependency on a specific version of our Domain DLL (the current one at the time the update was written). For example:
Cool.Program.Update0001.dll is dependent on Cool.Program.Domain.dll version 1.0.1
Cool.Program.Update0002.dll is dependent on Cool.Program.Domain.dll version 1.0.3
Cool.Program.Update0003.dll is dependent on Cool.Program.Domain.dll version 1.1.6
My plan is to store these DLLs & their dependency DLLs in sub folders as follows:
%APP%\Cool\Program\Updates\0001\Cool.Program.Update0001.dll
%APP%\Cool\Program\Updates\0001\Cool.Program.Domain.dll (version 1.0.1)
%APP%\Cool\Program\Updates\0002\Cool.Program.Update0002.dll
%APP%\Cool\Program\Updates\0002\Cool.Program.Domain.dll (version 1.0.3)
%APP%\Cool\Program\Updates\0003\Cool.Program.Update0003.dll
%APP%\Cool\Program\Updates\0003\Cool.Program.Domain.dll (version 1.1.6)
Then have my main program load & call each Update DLL in sequence, dynamically, using reflection:
var path = System.IO.Path.GetDirectoryName(typeof(this).Assembly.Location) + "\\Updates\\" + versionNumber + "\\Cool.Program.Update" + versionNumber + ".dll";
var assembly = System.Reflection.Assembly.LoadFile(path);
var type = assembly.GetTypes().First(x => x.Name == "Updater");
var method = type.GetMethod("DoYourThing");
var result = method.Invoke(null, dbConnection) as string;
Unfortunately, having tested this design and interrogating the version numbers, I find that each UpdateXXXX.dll is working with the LATEST Domain.dll, even though an earlier one is stored in its own subfolder. I assume that they are resolving their dependencies through the GAC, or, defaulting to the dependency already loaded in the main program. (BTW I see in Visual Studio that it is not possible to force "Specific version" for a project reference and a quick google-fu suggests this isn't straightforward.)
My question:
How can I force the Update assembly to resolve its dependency on the Domain dll to its local folder?
Or:
Can I explicitly inject a dependency for a dynamically loaded assembly?
EDIT: I've found an answer, see below.
I'll answer my own question as I have completed it in the mean time.
This code does the trick:
// versionNumber = "0001", "0002" etc
var basePath = Path.GetDirectoryName(typeof(this).Assembly.Location) + "\\Updates\\" + versionNumber + "\\";
var updateAssemblyPath = Path.Combine(basePath, "Cool.Program.Update" + versionNumber + ".dll");
var setup = AppDomain.CurrentDomain.SetupInformation;
setup.ApplicationBase = basePath;
var newDomain = AppDomain.CreateDomain("Updater for " + versionNumber, null, setup);
var obj = (ICanDoSomething)newDomain.CreateInstanceFromAndUnwrap(updateAssemblyPath, "TestDynamicAssemblyLoading.Update"+ versionNumber + ".Class1");
var result = obj.TellMeYourVersionsAndDependencyVersions();
MessageBox.Show(result);
It assumes that the class being used implements a known interface (eg. "ICanDoSomething") and inherits from MarshalByRefObject:
public class Class1 : MarshalByRefObject, ICanDoSomething
{
public string TellMeYourVersionsAndDependencyVersions()
{
return
typeof(Class1).Assembly.GetName().Version + "\n\n" +
"My reference to Domain\n=================\n " + SomeModel.TellMeYourVersionsAndDependencyVersions() + "\n\n" +
"My reference to Common\n=================\n " + SomeUtility.TellMeYourVersion();
}
}
In C#, I am able to compile VB and C# Code, using the code below, but I have no way of compiling C/C++ code. Is there any way of doing this?
C# Compiler:
public void Compile(string ToCompile)
{
string Result = null;
string errors = null;
Microsoft.CSharp.CSharpCodeProvider codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
System.CodeDom.Compiler.ICodeCompiler icc = codeProvider.CreateCompiler();
string Output = #"mypath";
System.CodeDom.Compiler.CompilerParameters parameters = new System.CodeDom.Compiler.CompilerParameters();
parameters.GenerateExecutable = true;
parameters.OutputAssembly = Output;
System.CodeDom.Compiler.CompilerResults results = icc.CompileAssemblyFromSource(parameters, ToCompile);
if (ReturnErrors == true)
{
if (results.Errors.Count > 0)
{
foreach (System.CodeDom.Compiler.CompilerError CompErr in results.Errors)
{
errors +=
"Line number " + CompErr.Line +
", Error Number: " + CompErr.ErrorNumber +
", '" + CompErr.ErrorText + ";" +
Environment.NewLine + Environment.NewLine;
}
Result += "Errors have been found in your code: " + Environment.NewLine + errors;
}
else
{
Result += "Success!";
System.Diagnostics.Process.Start(Output);
}
}
And to create a VB compiler, I simply replace Microsoft.CSharp.CSharpCodeProvider codeProvider = new Microsoft.CSharp.CSharpCodeProvider(); with Microsoft.VisualBasic.VBCodeProvider codeProvider = new Microsoft.VisualBasic.VBCodeProvider();
You can compile c++/cli code, not native c++ as mentioned above.
You can archive c++/cli compilation with CppCodeProvider (http://msdn.microsoft.com/en-us/library/microsoft.visualc.cppcodeprovider(v=vs.85).aspx) class using it like in your example.
I'm assuming you've got a chunk of source, say containing a function with a known prototype, which you want to compile, and run within your currently running application.
In order to do this in native (not managed) C++, you'd need to do the following:
Dynamically store your code into a boilerplate dll source project (i.e. everything written with a gap for the function's code, which you'd insert)
Spawn a C++ compiler (which the end user would have to have pre-installed) to output a dll
Build up a C++/Cli wrapper that wraps the c++ dll that you built above, and compile that too (see Redwan's answer)
Dynamically load your wrapper Dll, and call the function.
If you can work with just managed c++/CLI, then Redwan's answer should be adequate on its own.
I have written a plugin system inspired from NotePad.NET, this plugin system read all DLL File in given folder and load them at runtime if they match my Interface using reflection. code is below
foreach (string file in System.IO.Directory.GetFiles(dir + "\\plugins\\", "*.dll", System.IO.SearchOption.AllDirectories))
if (file.EndsWith(".dll"))
{
Assembly dll = Assembly.LoadFrom(file);
foreach (Type t in dll.GetTypes())
{
try
{
log.WriteLine("Trying to match " + t.BaseType.FullName + " with " + typeof(Acoustical.PluginBase.FileFormatBase).FullName + " and " + typeof(Acoustical.PluginBase.ReportBase).FullName);
if (t.BaseType.FullName == typeof(FileFormatBase).FullName)
{
log.WriteLine(" we compare for " + t.BaseType.FullName);
try
{
fileformatPlugin.Add((dynamic)Activator.CreateInstance(t));
}
catch (Exception ex)
{
log.WriteLine("Error in loading File Plugin ::" + ex.Message + "\r\n" + ex.StackTrace);
}
continue;
}
else if (t.BaseType.FullName == typeof(ReportBase).FullName)
{
log.WriteLine(" we compare for " + t.BaseType.FullName);
reportPlugin.Add((dynamic)Activator.CreateInstance(t));
continue;
}
}
catch (Exception ex) {
log.WriteLine("Error in loading Plugin ::" + ex.Message + "\r\n" + ex.StackTrace);
}
}
dll = null;
}
Above code shows the part where Iterate over all found files in folder and than load them.
The above code works for me if I use the Windows Form or WCF interface application, it works on Windows service but doesn't guarantee that. It 60-70% of time on recompile doesn't load the plugin, then I after 10-15 attempts it sometime load plugins or sometime load only 1-2 plugins.
as you see I put Try catch on almost all lines to trace where the error is coming but no error is showing up. Since it is Windows SErvice it is not possible to debug when this code is written on ONStartup event.
I did see "We compare for" line in log, but when we try to see fileformatplugin got any element it's count remain 0 and error line is not there in log.
Any advice?
The problem I am facing has as follows:
I have developed a portable class library to encapsulate a service connection. Inside this class library there is a Resources.resw file containing strings. These strings are called only by methods of the class library (for example to override ToString() methods).
As I said this is a portable class library. If I reference it as a dll, or even as a project inside another solution, it gets built and compiles correctly. Then I make a call using a method of this library within my application, say
ClientFacadeConnector connector = new ClientFacadeConnector();
ICollection<SearchResult> results = null;
string message = string.Empty;
if (maxResults != -1) //Search with max Results
{
try
{
if (!contextQuery.Trim().Equals(string.Empty))
{
results = await connector.GetConnected().SearchAsync(contextQuery, query, maxResults);
message = "Search with ContextQuery " + contextQuery + ", Query " + query + ", max results " + maxResults.ToString();
}
else
{
results = await connector.GetConnected().SearchAsync(query, maxResults, true);
message = "...using normal Query search, Query " + query + ", max results " + maxResults.ToString();
}
}
catch (IQserException ex)
{
message = ex.Message;
}
}
if (results != null)
{
ICollection<LocalSearchResult> contentResults = new List<LocalSearchResult>();
foreach (SearchResult s in results)
{
var q = s.ToString();
var contentItem = await connector.GetConnected().GetContentAsync(s.ContentId);
LocalSearchResult lContent = new LocalSearchResult(contentItem);
lContent.Score = s.Score;
lContent.Relevance = s.Relevance;
lContent.MarkFullText(query);
contentResults.Add(lContent);
}
At the point where I call s.ToString() method, I get an error "Resource Map not found".
To explain where this comes from:
public static class AppResources
{
private static ResourceLoader resourceLoader;
static AppResources()
{
// Load local file Resources.resw by default
resourceLoader = new ResourceLoader();
}
public static string GetResources(string key)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException("key");
return resourceLoader.GetString(key);
}
}
and inside the overridden ToString() method there is code that looks as follows:
public override string ToString()
{
StringBuilder buf = new StringBuilder(AppResources.GetResources("InstrSearchResultContent"));
if (ContentId != -1)
{
buf.Append(AppResources.GetResources("StringContent") + " ID:" + ContentId.ToString() + " | ");
}
else
{
buf.Append(AppResources.GetResources("StringNo") + AppResources.GetResources("StringContent") + "ID" + " | ");
}
...
The resource file is called resources.resw and is the default resw file that ResourceLoader calls if no other is called.
Strangely enough, if I copy the resource file inside the client application locally, it is referenced correctly by all calls to the class library resource file and everything works.
This class library is supposed to be an SDK when finished. Do I need to distribute the resource file separately?
Such a problem I have never experienced with normal Class libraries and resx files. Resw is giving me the creeps..
It looks like you have to specify the name of the resource map when you create the ResourceLoader, like this:
resourceLoader = new ResourceLoader("Assembly/ResourceFile");
For example, if your class library was called "Company.Lib.dll", and your resource file was "Resources.resw", you would use:
resourceLoader = new ResourceLoader("Company.Lib/Resources");
This doesn't seem to be documented fully on MSDN - it suggests that you can just specify the name of your resource file, but it might be that that only works for resource files that are in the Windows Store application project. It was this page that showed me that, for libraries, you need to specify the assembly name as well.
I also had similar issue even with repeating all steps from How to load string resources.
The problem was that my Resources.resw file was empty. When I added some fake string to it all started working as expected.
I had a similar issue which i resolved by changing the Build Action of the resw file to PRIResource in the properties. I had renamed an existing resx to resw but the documentation doesn't mention that you also have to change the build action.
Accepted answer posted by #Rory MacLeod may no longer be true. I tried and VS warned that ResourceLoader(String) is deprecated. The following worked in my case:
var loader = ResourceLoader.GetForCurrentView();
string localName = loader.GetString("someKey");
I faced a similar issue when developing a UWP app with a class library.
So I have a /Strings/en-Us/Resource.resw file in my library.
ResourceLoader.GetForCurrentView().GetString("someKey");
gives an exception
new ResourceLoader("Company.Lib/Resources").GetString("someKey");
gives me a deprecation warning but works.
My solution which does not give a warning is this:
ResourceLoader.GetForViewIndependentUse("AssemblyNamespace/Resources").GetString("someKey");