Dynamically Updating a Loaded Dll - c#-4.0

I am creating two solutions for a project I am working on, one for the main server, which will ideally continuously run without need for update. The secondary solution is a DLL, where programmers will continuously change and update code. The server solution is a windows Application and the programmer's solution (the one constantly changing, AKA JupiterCode.dll) is a Class Library.
I am accessing the code via telnet, which is required, and will be entering commands there. The input string will then be executed via reflection on the DLL file ("The user entered 'smile'. Is there a command called 'smile'? Yes, ok, let's execute that code."). I have all of that working and it's going swimmingly, but I hit a snag for when I want to actually update the DLL so that there are more commands the user can enter. When I rebuild the Class Library, the server does not yet reflect the new changes. The server only reflects the new changes when I rebuild the server.
I am trying to have it so when a user enters UPDATE into the telnet server, it will update the loaded DLL with the new DLL, but when I try to do something like...
string originalDllPath = "C:\\Users\\*****\\Documents\\PseudoGameClient\\bin\\Debug\\JupiterCode.dll";
string newDllPath = "C:\\Users\\*****\\Documents\\PseudoServerStub\\bin\\Debug\\.dll";
string newDllBakPath = "C:\\Users\\*****\\Documents\\PseudoServerStub\\bin\\Debug\\JupiterCodeNewBak.dll";
string oldDllPath = "C:\\Users\\*****\\Documents\\PseudoServerStub\\bin\\Debug\\JupiterCode.dll";
string oldDllBakPath = "C:\\Users\\*****\\Documents\\PseudoServerStub\\bin\\Debug\\JupiterCodeBak.dll";
try
{
File.Copy(originalDllPath, newDllPath, true);
AppDomain.Unload(updatedCodeDomain);
AppDomain sandbox = AppDomain.CreateDomain("Sandbox");
Assembly assembly = sandbox.Load(oldDllBakPath);
upToDateCode = assembly;
File.Copy(oldDllPath, newDllBakPath, true);
File.Copy(newDllPath, oldDllPath, true);
AppDomain.Unload(sandbox);
AppDomain newCodeDomain = AppDomain.CreateDomain("NewCode");
assembly = newCodeDomain.Load(oldDllPath);
upToDateCode = assembly;
File.Copy(newDllBakPath, oldDllBakPath, true);
File.Delete(newDllPath);
File.Delete(newDllBakPath);
updatedCodeDomain = newCodeDomain;
}
catch (Exception ex)
{
}
In this method, once it gets to
Assembly assembly = sandbox.Load(oldDllBakPath);
It gives me the exception:
"Could not load file or assembly 'C:\\Users\\aderbedrosia\\Documents\\PseudoServerStub\\bin\\Debug\\JupiterCodeBak.dll' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)":"C:\\Users\\aderbedrosia\\Documents\\PseudoServerStub\\bin\\Debug\\JupiterCodeBak.dll"} System.Exception {System.IO.FileLoadException
(Note: Uptodatecode is kept as a class field because I refer to it later down to execute the commands on it, which again, is working perfectly fine, save for the inability to update.)
I have also tried:
File.Copy(originalDllPath, newDllPath, true);
Assembly assembly = Assembly.LoadFrom(oldDllBakPath);
AppDomain.Unload(updatedCodeDomain);
AppDomain sandbox = AppDomain.CreateDomain("sandbox");
sandbox.Load(assembly.GetName());
upToDateCode = null;
upToDateCode = assembly;
File.Copy(oldDllPath, newDllBakPath, true);
File.Copy(newDllPath, oldDllPath, true);
assembly = Assembly.LoadFrom(oldDllPath);
AppDomain.Unload(sandbox);
AppDomain sandbox2 = AppDomain.CreateDomain("secondarySandbox");
sandbox2.Load(assembly.GetName());
upToDateCode = assembly;
File.Copy(newDllBakPath, oldDllBakPath, true);
File.Delete(newDllPath);
File.Delete(newDllBakPath);
updatedCodeDomain = sandbox2;
and it errors out on
sandbox.Load(assembly.GetName());
with
"Could not load file or assembly 'JupiterCode, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=9a98a67e4d0b3db8' or one of its
dependencies. The system cannot find the file
specified.":"JupiterCode, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=9a98a67e4d0b3db8"
Again, to reiterate, I am able to dynamically load the assembly at runtime, but replacing the loaded DLL with a more up to date one (at runtime, without rebuilding) is proving difficult.

For anyone who is facing the same problem, I was able to use a dynamic compiler. look into CSharpCodeProvider and CSharpCodeProvider.CompileAssemblyFromFile. It was everything I needed.

Related

Visual Studio Addin not showing

I've created very simple Visual Studio Add-in, ala this article by JP Booodhoo.
http://codebetter.com/jpboodhoo/2007/09/04/macro-to-aid-bdd-test-naming-style/
The addin works in debug, so if I F5 in the add in solution, and open a solution then the addin shows in the tools. However, it doesn't show when not debugging. i.e. after I've deployed the addin, closed and re-opened my solution.
Am I missing something?
In terms of deployment, I followed the deployment steps in this article and deployed it to C:\Users[your user name]\Documents\Visual Studio 2012\Addins
Alternative to macros in Visual Studio 2012
public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
{
handled = false;
if(executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if(commandName == "KinghamExtensions.Connect.KinghamExtensions")
{
var selection = (TextSelection)(_applicationObject.ActiveDocument.Selection);
selection.SelectLine();
if (selection.Text == "") return;
var prefix = "public void ";
var index = selection.Text.IndexOf(prefix);
prefix = selection.Text.Substring(0, index) + prefix;
var description = selection.Text.Replace(prefix, String.Empty);
selection.Text = prefix + description.Replace(" ", "_").Replace("'", "_");
selection.LineDown();
selection.EndOfLine();
handled = true;
}
}
}
As I say, the code works when running the addin from vs in debug, but doesn't show in the tools menu.
Also, it doesn't show up in the keyboard options like the Git Extensions addin does meaning I can't assign a key binding.
Any thoughts?
It is hard to answer by the information you given, but at first you should check the followings:
Your AddIn should appear in the Tools>Add-in Managger...
If you set the first check box before it, than it should be loaded.
If it isn't and you get an error message, click to no, else the Studio will rename the deployed .AddIn file.
You should check if your release assembly is at the place referenced by the Assembly element like this: <Assembly>C:\Users[your user name]\Documents\Visual Studio 2012\Projects\MyAddin1\MyAddin1\bin\MyAddin1.dll</Assembly>
in the .AddIn file deployed by Visual Studio to the AddIn folder you mentioned in your question.
If it is, and the error pesrists, you should add some log to your Add-In (a Windows MessageBox will do)
and place it to the OnConnection method. The error can appear either OnConnection throws an Exception while the IDE trying to load it, or the FullClassName element in the AddIn file refers to an other name than your Connection class has.
If you get no errors and your OnConnection runs properly, then it could be an exception thrown while your code is adding your command, - if you do the same way as it is in a generated Add-In template in a try/catch block- and you need to resolve it.

WPF Designer throws error when string resources are used in code behind

I have a wpf custom control (in AssemblyA) that references a string resource from an resx file in an external assembly (AssemblyB).
public override void OnApplyTemplate()
{
try
{
base.OnApplyTemplate();
// ...
// Do Stuff
// ...
}
catch (Exception ex)
{
Logger.Error(ExceptionCodes.Ex50000, ex);
}
}
When I add the custom control (in AssemblyA) via a dll reference to a page in another project (AssemblyC) in another solution, the control fails to display in the designer. Instead, the designer displays a nice big red cross with the message
FileNotFoundException: Could not load file or assembly 'AssemblyB'.
The AssemblyB was also added as a dll reference to AssemblyC.
Removing the the references to the string resource in AssemblyA removes the error and allows the control to display correctly in the designer. Unfortunately, this is not an option as the string resources are used throughout the application for support reasons.
Creating an resx file in AssemblyA also removes the error but decentralises the resources which is not an option for on going development.
Based on the above, the designer is obviously not loading the resource assembly. Any insights would be appreciated.
To Summarise
CustomControl in Assembly A in Solution 1 references a string resourced from a resx file in Assembly B in Solution 1. Assembly C in Solution 2 has a dll reference to both Assembly A and Assembly B. A UserControl in Assembly C uses CustomControl in Assembly A. The Visual Studio WPF designer throws a FileNotFound exception when displaying the UserControl.
Let it throw error, just check whether your are able to complie and run, the Make the assembly In Solution C as exe and try to run it. Because with Visual studio 10.0.4, i have see that exception many times, but if it is complied it doesn't give any compiler error, just try to compile and run it

Can`t Load C++/CLI DLL resources

I'm trying just to see resource names but nothing appears.
I've made and compiled a C++/CLI (Managed) DLL in Visual Studio 2010 and added some Resource files as a test (one icon and one bitmap). I've checked with PE Explorer and the resources definitely are there.
My simple code:
Assembly asm = Assembly.LoadFrom("C:\\test.dll");
String[] res = asm.GetManifestResourceNames();
I know that the DLL is loaded because when I debug i can see all the infos in the 'asm' variable. Also i can Import data (using MEF) from the DLL.
So, the DLL has the resources and the code IS loading the assembly for sure. But why my 'res' variable always returns empty string list?
EDIT:
I've created a C# Class Library (.dll) with a resource just for a test. Now it works!! But still in my C++/CLI DLL the resources do not appear. Somehow they are in the DLL but the code cant reach it (only in the C++ DLL). Maybe it would have something to do with managed/unmanaged code, but since i'm compiling it with CLR it does not seem to be the case. Any suggestions?
SOLUTION
I've got it! Just in case someone needs.
According to these topics:
Embedding resource in a C++/CLI project
and
http://bytes.com/topic/net/answers/571530-loading-markup-xamlreader-load-resource-file#post2240705
the problem is exactly the C++/CLI thing. You have to add it in Input item under Linker tab in Project Properties. Now it seems to work fine. Thanks
I have a similar problem and your question helps me to solve it.
my project platform is C++/CLI and my DLL platform is c#.
I want to pack DLL into my executive file, hence we should put DLL in the project resource file through below steps at first:
1.copy DLL in project path.
2.put DLL name(e.g. test.dll) in below place
properties->linker->input->Embeded Managed Resource File
then we should read and use embedded DLL:
Stream^ stream = Assembly::GetExecutingAssembly()->GetManifestResourceStream("test.dll");
array<unsigned char>^ dllRawBuffer = gcnew array<unsigned char>(stream->Length);
int res = stream->Read(dllRawBuffer, 0, stream->Length);
stream->Close();
Assembly^ dllAssembly = Assembly::Load(dllRawBuffer);
System::Type^ testclass = dllAssembly->GetType("TestNamespace.TestClass");
MethodInfo^ TestMethod = testclass->GetMethod("TestMethodName");
// Create an instance.
Object^ Testobj = Activator::CreateInstance(testclass);
// Execute the method.
array<Object^>^ params = gcnew array<Object^>(2);
params[0] = 2;
params[1] = 3;
Object^ result = TestMethod->Invoke(Testobj, params);
obviously, this solution only works for managed DLLs.

How to reduce Enterprise Library 5.0 Logging memory usage?

I am using enterprise logging 5.0 within a .net 4.0 console application. I notice very high memory usage within my application. I was able to determine that the cause was due to the following call:
var logWriter = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
After some profiling and manual testing with a simple console application, I was able to determine that memory usage drop from 45mb to 10mb when the following dlls were removed from the execution folder:
Microsoft.Practices.EnterpriseLibrary.Validation.dll
Microsoft.Practices.EnterpriseLibrary.Data.dll
The log initialization is my first call to Enterprise library apis. My console application does not make any calls to the Data.dll and Validation.dll. They exist in my execution folder, because they are references for other class libraries and our deployment setup.
I am assuming that EnterpriseLibraryContainer.Current is initializing based on what is found in the execution folder. I tried creating my logwriter with the following but I got the same result:
var configSource = new FileConfigurationSource(configPath);
var logWriterFactory = new LogWriterFactory(configSource);
var logWriter = logWriterFactory.Create();
Is it possible to initialize a logwriter without increasing the memory usage with the validation and data dlls present in the execution folder?
UPDATE:
So after some debugging within the entlib source. I believe the following is finding the dll and instantiating the Validation.dll, and Data.dll, despite not being referenced at all in the project.
From EntLib50Src\Blocks\Common\Src\Configuration\ContainerModel\TypeLoadingLocator.cs
private IEnumerable<TypeRegistration> GetRegistrationsInternal(IConfigurationSource configurationSource,
Func<ITypeRegistrationsProvider, IConfigurationSource, IEnumerable<TypeRegistration>> registrationAccessor)
{
Type providerType = Type.GetType(Name);
if (providerType == null) return new TypeRegistration[0];
var provider = (ITypeRegistrationsProvider)Activator.CreateInstance(providerType);
return registrationAccessor(provider, configurationSource);
}
the call to Type.GetType(Name) looks in the Executing Assembly location, which seems to be the reason why it registered the entlib data access.
After Debugging further my original application which contains connection strings with Oracle ODP.net Providers. (which I failed to mention from the start)
(my current application execution makes no calls or references to data access, connection strings are defined because application uses dynamic calls to other dlls which need connection strings., but for my test I am not invoking any of those calls)
Since Microsoft.Practices.EnterpriseLibrary.Data.dll is found, EnterpriseLibrary continues default registration of types for dataaccess and I found that the following call is the cause for the huge memory spike:
\EntLib50Src\Blocks\Data\Src\Data\Configuration\DatabaseSyntheticConfigSettings.cs
private static DbProviderMapping GetDefaultMapping(string dbProviderName)
{
// try to short circuit by default name
if (DbProviderMapping.DefaultSqlProviderName.Equals(dbProviderName))
return defaultSqlMapping;
if (DbProviderMapping.DefaultOracleProviderName.Equals(dbProviderName))
return defaultOracleMapping;
// get the default based on type
var providerFactory = DbProviderFactories.GetFactory(dbProviderName);
if (SqlClientFactory.Instance == providerFactory)
return defaultSqlMapping;
if (OracleClientFactory.Instance == providerFactory)
return defaultOracleMapping;
return null;
}
The DbProviderFactories.GetFactory(dbProviderName) call when dbProviderName=Oracle.DataAccess.Client.OracleClientFactory causes the huge memory spike.
So looks like the reason for the huge memory spike was due to odp.net and the fact that its registering DBFactories.
It seems like I cannot create a logger without registering everything present in the executing assembly location. Ideally I would like to not register data access unless its explicitly told to.
The fact that ODP.NET is being used as the underlying provider is the main reason for the memory spike, and the GetDefaultMapping will return null anyways. The following change could be made:
private static DbProviderMapping GetDefaultMapping(string dbProviderName)
{
// try to short circuit by default name
if (DbProviderMapping.DefaultSqlProviderName.Equals(dbProviderName))
return defaultSqlMapping;
if (DbProviderMapping.DefaultOracleProviderName.Equals(dbProviderName))
return defaultOracleMapping;
if (dbProviderName != "Oracle.DataAccess.Client")
{
// get the default based on type
var providerFactory = DbProviderFactories.GetFactory(dbProviderName);
if (SqlClientFactory.Instance == providerFactory)
return defaultSqlMapping;
if (OracleClientFactory.Instance == providerFactory)
return defaultOracleMapping;
}
return null;
}
This still doesn't explain why Oracle.DataAccess.Client uses so much memory for :
DbProviderFactories.GetFactory(dbProviderName);
as well as to why its registering the data access with the container.

VC++ 6.0: Why is CASyncSocket::GetLastError() causing an access violation?

I'm using Visual C++ 6.0. I'm not sure of the service pack level of the visual studio installation, but the OS is Win 2K SP4. The failing code is part of a DLL.
Here's the code:
EIO::OpenConnection()
{
m_Client = new CSocket();
if(m_Client->Create() == 0) {
delete m_Client;
m_Client = NULL;
return CAsyncSocket::GetLastError();
}
if (!m_Client->Connect((LPCTSTR)m_IPAddress, 7)) {
delete m_Client;
m_Client = NULL;
return CAsyncSocket::GetLastError();
}
...<stuff>...
}
This compiles without error on my build system and executes without either of the calls to m_Client methods failing. When I move this DLL to the production system (Win 2K, not sure of service pack level yet), the call to m_Client->Connect() returns an error, so it goes into the IF block. CAsyncSocket::GetLastError() then the debugger to open and report an 0xC0000005 access violation. I don't understand this stuff enough to get anything out of the disassembly.
I've also tried CSocket::GetLastError() and m_Client->GetLastError() with the same results.
I'm fairly certain that m_Client->Connect() fails because of some security policy that's on the production machine that's absent on the development system, but I'd like to get the actual error code so I can help the IT guy narrow his search.
I haven't yet tried forcing a call to GetLastError() on my build system to see if I get an access violation there.
The GetLastError() method most likely calls WSAGetLastError().
But for WSAGetLastError() to work, WSACleanup() must not have been called yet.
I'm guessing that when you delete m_Client that exactly this happens.
Try calling GetLastError() before you delete the m_Client object.

Resources