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();
}
}
Related
I have a program that dynamically runs user entered C# code, that part works fine and the results are outputted to a location given by the user (or a system default).
My users are asking that I output the results to an "output window" so they can view the results without having to go to file.
I am trying to reference the program the dynamically created C# code is being generated in so I can output the results, but I am getting a file not found error when trying to add the reference for the program that it is being run in.
Here is my code:
private static Assembly CompileSourceCodeDom(string sourceCode)
{
CodeDomProvider cpd = new CSharpCodeProvider();
var cp = new CompilerParameters();
LogConsoleMessage(Assembly.GetExecutingAssembly().CodeBase + "\n");
cp.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().CodeBase);
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Windows.Forms.dll");
cp.ReferencedAssemblies.Add("System.Core.dll");
cp.GenerateExecutable = false;
CompilerResults cr = cpd.CompileAssemblyFromSource(cp, sourceCode);
cr.Errors.Cast<CompilerError>().ToList().ForEach(error =>
LogConsoleMessage(error.ErrorText + " Line #: " + error.Line + " Column:
" + error.Column + "\n"));
if (cr.Errors.Count == 0)
{
return cr.CompiledAssembly;
}
else
{
return null;
}
}
This is the line where the error is being displayed
cr.Errors.Cast<ComplierErrors>().ToList().ForEach(error =>
LogConsoleMessage(error.ErrorText + " Line #: " + error.Line + " Column:
" + error.Column + "\n")); is where the error message is coming out
Here is the exact error message. I checked the folder and it does exists.
Metadata file 'file:///C:/Users/[my user]/source/repos/test/test/bin/Debug/test.exe' could not be found Line #: 0 Column: 0
Does this have to do with the fact that the program is currently running? Because I cannot seem to find a way to reference this code. Does anyone have any suggestions about a different way to output the results to a visible location?
Thanks in advance!
EDIT
Assembly.GetExecutingAssembly().CodeBase
was changed to
Assembly.GetExecutingAssembly().Location
Assembly.CodeBase returns the assembly location formatted as an URI like file://... which the compiler is apparently not able to parse.
Instead, try using the "pure" local or UNC path of your assembly. To get the local or UNC path of the assembly, replace CodeBase with the Location property:
LogConsoleMessage(Assembly.GetExecutingAssembly().Location + "\n");
cp.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
I'm having trouble statically linking Mono using mkbundle in Windows. In my attempts to figure out what's going on I hit a wall. When you pass the static flag to mkbundle in windows it looks for the file monosgen-2.0-static.lib in the mono directory. This directory is defined by the line the below:
string monoPath = GetEnv("MONOPREFIX", #"C:\Program Files (x86)\Mono");
The contents of this directory after installing mono 5.1.1 is:
First I noticed the file naming convention is different from that that mkbundle is looking for (monosgen-2.0 should be mono-2.0-sgen). I can change this just fine, however I suspect - given the file name - that the mono-2.0-sgen.lib file shown in the screenshot isn't statically compiled, as when I try to run my bundled application it first can't find the sgen dll, and then when it can it can't find others.
At this point I'm wondering if mkbundle officially works on Windows, and if it does am I doing something fundamentally wrong? I have seen older post asking for help setting mkbundle in Windows and have posted questions regarding this myself. Most point to using mingw instead of cl.exe. Should I be using this instead?
The source for this snippet is shown below. You can find the entire source code here https://github.com/mono/mono/blob/master/mcs/tools/mkbundle/mkbundle.cs.
if (style == "windows")
{
Func<string, string> quote = (pp) => { return "\"" + pp + "\""; };
string compiler = GetEnv("CC", "cl.exe");
string winsdkPath = GetEnv("WINSDK", #"C:\Program Files (x86)\Windows Kits\8.1");
string vsPath = GetEnv("VSINCLUDE", #"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC");
string monoPath = GetEnv("MONOPREFIX", #"C:\Program Files (x86)\Mono");
string[] includes = new string[] {winsdkPath + #"\Include\um", winsdkPath + #"\Include\shared", vsPath + #"\include", monoPath + #"\include\mono-2.0", "." };
// string[] libs = new string[] { winsdkPath + #"\Lib\winv6.3\um\x86" , vsPath + #"\lib" };
var linkLibraries = new string[] { "kernel32.lib",
"version.lib",
"Ws2_32.lib",
"Mswsock.lib",
"Psapi.lib",
"shell32.lib",
"OleAut32.lib",
"ole32.lib",
"winmm.lib",
"user32.lib",
"libvcruntime.lib",
"advapi32.lib",
"OLDNAMES.lib",
"libucrt.lib" };
string glue_obj = "mkbundle_glue.obj";
string monoLib;
if (static_link)
monoLib = LocateFile (monoPath + #"\lib\monosgen-2.0-static.lib");
else {
Console.WriteLine ("WARNING: Dynamically linking the Mono runtime on Windows is not a tested option.");
monoLib = LocateFile (monoPath + #"\lib\monosgen-2.0.lib");
LocateFile (monoPath + #"\lib\monosgen-2.0.dll"); // in this case, the .lib is just the import library, and the .dll is also needed
}
var compilerArgs = new List<string>();
compilerArgs.Add("/MT");
foreach (string include in includes)
compilerArgs.Add(String.Format ("/I {0}", quote (include)));
if (!nomain || custom_main != null) {
compilerArgs.Add(quote(temp_c));
compilerArgs.Add(quote(temp_o));
if (custom_main != null)
compilerArgs.Add(quote(custom_main));
compilerArgs.Add(quote(monoLib));
compilerArgs.Add("/link");
compilerArgs.Add("/NODEFAULTLIB");
compilerArgs.Add("/SUBSYSTEM:windows");
compilerArgs.Add("/ENTRY:mainCRTStartup");
compilerArgs.AddRange(linkLibraries);
compilerArgs.Add("/out:"+ output);
string cl_cmd = String.Format("{0} {1}", compiler, String.Join(" ", compilerArgs.ToArray()));
Execute (cl_cmd);
}
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?
I have been playing around with TFS for a while because I need to upload/check in specific files and folders into various locations on the TFServer after they have been created and placed locally. I am mapping the workspace and everything and I get a bunch of changes from PendingChanges but not the ones I want and not where I want. The problem is really annoying because for every check in process I only want to work on a specific folder or file in a specific location that is already mapped. I am gonna paste part of the code here for reference.
using (TfsTeamProjectCollection collection = new TfsTeamProjectCollection(serverUri, _cred))
{
VersionControlServer versionControl = (VersionControlServer)collection.GetService(typeof(VersionControlServer));
string machineName = Environment.MachineName;
string currentUserName = Environment.UserName;
Workspace myWorkspace = versionControl.GetWorkspace(machineName, currentUserName);
// tried this from Stack but didn't work.
//PendingChange[] changes = myWorkspace.GetPendingChanges().Where(x => x.LocalOrServerFolder.Contains(localPath)).ToArray();
PendingChange[] changes = myWorkspace.GetPendingChanges();
if (changes.Length != 0)
{
foreach (PendingChange c in changes)
{
rt.Text += " path: " + c.LocalItem + ", change: " + PendingChange.GetLocalizedStringForChangeType(c.ChangeType) + "\n";
}
}
else
{
rt.Text += "This didn't work.";
}
}
Basically I want to throw a folder or file at my TFS class and let it check if the file or folder is up to date or present on the TFS and act accordingly. I hope I have described the problem thoroughly.
I think that my answer will not response fully you question, but if you want to get pending changes for a specific folder, you can use something like this..
PendingChange[] changes = myWorkspace.GetPendingChanges(localPath, RecursionType.Full, false);
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");