|
Not sure why this is downvoted? Perhaps it was very bad pre-edit.
Anyway, what you are asking for is a classic plugin architecture, and that's a well solved problem. There are three steps to it:
- Create a boundary interface that defines what the plugin should do. This interface should be in an assembly available to both the plugin developer (plugins need a compile time reference to the assembly) and the application. In the case of a releasable app or something where the same team develops both sides it can go in the main application assembly.
- Create plugin classes which implement the interface and do the work.
- Write some reflection code in your main application that finds instances of the plugins in relevant assemblies, and registers them.
The exact implementation of that depends on whether you need AppDomain separation between plugins (for example if you want to support plugin update or unloading without restarting the application, or different security contexts), and whether plugins will be in separate assemblies.
In this case the boundary interface is
public interface IReportGenerator {
void Validate();
void Execute();
}
... although I'd expect some form of input to be passed to both methods in reality.
The simplest version of the reflection code (look in a single assembly only) looks like this:
public void RegisterPlugins(Assembly a) {
foreach(Module mod in a.GetLoadedModules()){
foreach(Type ty in mod.GetTypes()){
foreach(Type intf in ty.GetInterfaces()){
if(intf == typeof(IReportGenerator)){
RegisterPlugin((IReportGenerator)Activator.CreateInstance(ty));
}
}
}
}
}
For a full implementation including looking up assemblies in a directory and loading each plugin into an AppDomain, check out my game lobby article[^] (LobbyClient/GameType.cs or LobbyServer/GameTypes.cs). Although don't necessarily copy the coding style in there, that is old code from before I knew what I was doing.
|
|
|
|
|
<pre lang="c#">
string fileToLoad = @"C:\Users\nitin.jain\Documents\visual studio 2012\Projects\ConsoleApplication1\ClassLibrary1\bin\Debug\ClassLibrary1.dll";
string migrationStepsDllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ClassLibrary1.dll");
AppDomainSetup appDomainSetup = new AppDomainSetup() { PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory, ShadowCopyFiles="true" };
Evidence evidence = AppDomain.CurrentDomain.Evidence;
AppDomain appDomain = AppDomain.CreateDomain("ClassLibrary1", evidence, appDomainSetup);
Assembly assembly = Assembly.LoadFile(fileToLoad);
Type type = assembly.GetType("ClassLibrary1.Class1");
object foo = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod("CheckAdress");
methodInfo.Invoke(foo, new object[] { "navdeep" });
AppDomain.Unload(appDomain);
Console.WriteLine("App domain unloaded");
Console.ReadLine();
This code is in console application and it works fine in the sense that it loads and unload the assembly in the domain.
But when my console is running and i try to delete the dll it says that dll is opened in console application.
So it is locked by it. How can i make sure that after unloading it, also unlocks the file.
|
|
|
|
|
Assembly assembly = Assembly.LoadFile(fileToLoad);
The assembly object is still in memory, so it is most likely holding an open handle to the file. You could try to dispose of it to see if that releases the file.
|
|
|
|
|
It doesn't look like you've loaded that assembly in the new app domain, hence it will be locked for the duration that your application is running. You can't unload an assembly per se, but you can unload the appdomain which its loaded in.
Have a look at the method AppDomain.CreateInstanceAndUnwrap to instantiate types in a different domain.
Regards,
Rob Philpott.
|
|
|
|
|
Hi,
I wrote an application that is running on multiple systems (>50). As this application gets new features frequently, I want to write a Launcher that is updating the program.
Exchanging the exe file is not a problem, but it get in trouble when the config file of the application changes.
The app.config file is divided into multiple configSections depending on its platform. I use a CustomSection as well, so the config file looks like this:
="1.0"="utf-8"
<configuration>
<configSections>
<section name="Section1" type="App.CustomSection,App"/>
<section name="Section2" type="App.CustomSection,App"/>
</configSections>
<Section1
Attrib1="true"
Attrib2="true"
.
.
.
AttribN="Value"
/>
<Section2
Attrib1="true"
Attrib2="true"
.
.
.
AttribN="Value"
/>
</configuration>
Now the launcher shall detect new attributes (comparing to a file at a file server), and insert them at the correct position (e.g. if an attribute Attrib1.5 is added, between Attrib1 and Attrib2).
The already present attribute values shall not be changed, but remain at their machine specific configuration.
I tried to solve this with the System.Xml namespace, but as I'm not very familiar with it, I was not able to solve my problem.
I also tried a Streamreader. This works partly, but if there are some blank lines on the machine, this does not work and if the order of the attributes has been changed, neither.
Here the code snippet:
private bool syncConfigFiles(string serverConfigFile, string localConfigFile)
{
List<string> _serverFileContent = FileHandler.getFileContent(serverConfigFile);
List<string> _localFileContent = FileHandler.getFileContent(localConfigFile);
for (int i = 0; i < _serverFileContent.Count; i++)
{
if (_serverFileContent[i].Trim() != _localFileContent[i].Trim())
{
string _checkStringServer = _serverFileContent[i].Trim();
string _checkStringLocal = _localFileContent[i].Trim();
if (_checkStringServer.Contains("=") && _checkStringLocal.Contains("="))
{
_checkStringServer = _checkStringServer.Substring(0, _checkStringServer.IndexOf("="));
_checkStringLocal = _checkStringLocal.Substring(0, _checkStringLocal.IndexOf("="));
}
if (_checkStringServer != _checkStringLocal)
{
_localFileContent.Insert(i, _serverFileContent[i]);
}
}
}
FileHandler.writeLines(localConfigFile, _localFileContent, false);
return true;
}
I already googled some hours and used this forum search, but no success.
Can you help me?
Thanks in advance,
Flockston
|
|
|
|
|
When the application launches, read the configuration file from the server and import that into your structure that holds all of your configuration data in your application. You DO have a structure to hold this stuff, correct?!
Then you can use a library, such as this[^], to Merge the machine specific configuration data into your existing configuration object, then serialize the completed structure back out to the machine config.
|
|
|
|
|
Hi,
just wanted you to inform that I have a solution now. I had a closer look into the XML part of .net and came up with the following solution that works pretty well:
private bool syncConfigFiles(string serverConfigFile, string localConfigFile)
{
XmlDocument _localDoc = new XmlDocument();
XmlDocument _serverDoc = new XmlDocument();
_localDoc.Load(localConfigFile);
_serverDoc.Load(serverConfigFile);
XmlNodeList _localNodes = _localDoc.DocumentElement.ChildNodes;
XmlNodeList _serverNodes = _serverDoc.DocumentElement.ChildNodes;
foreach (XmlNode _serverNode in _serverNodes)
{
foreach (XmlNode _localNode in _localNodes)
{
if (_serverNode.Name == _localNode.Name)
{
XmlAttributeCollection _serverAttributes = _serverNode.Attributes;
if (_serverAttributes != null)
{
for (int i = 0; i < _serverAttributes.Count; i++)
{
if (_localNode.Attributes[_serverAttributes[i].Name] == null)
{
XmlNode _newNode = _localNode.OwnerDocument.ImportNode(_serverNode, true);
if (i > 0)
{
_localNode.Attributes.InsertAfter(_newNode.Attributes[_serverAttributes[i].Name], _localNode.Attributes[_serverAttributes[i - 1].Name]);
}
else
{
int _tempI = i;
while (_localNode.Attributes[_serverAttributes[_tempI + 1].Name] == null && _tempI < _serverAttributes.Count)
{
_tempI++;
}
if (_tempI < _serverAttributes.Count)
{
_localNode.Attributes.InsertBefore(_newNode.Attributes[_serverAttributes[i].Name], _localNode.Attributes[_serverAttributes[_tempI + 1].Name]);
}
else
{
_localNode.Attributes.Append(_newNode.Attributes[_serverAttributes[i].Name]);
}
}
}
}
}
}
}
}
XmlWriterSettings _settings = new XmlWriterSettings();
_settings.NewLineOnAttributes = true;
_settings.Indent = true;
_settings.CloseOutput = true;
XmlWriter _writer = XmlWriter.Create(localConfigFile, _settings);
_localDoc.Save(_writer);
_writer.Close();
return true;
}
Log entries and some checks are still missing, but the functionality is given.
Best regards
Flockston
|
|
|
|
|
OK, that looks like it handles attributes only.
What if you throw in a new node on the server config with no attributes?
DON'T make the mistake of thinking that's "never going to happen".
|
|
|
|
|
Thanks for your hint! I did not think about that, because it is very unlikely that there will be new nodes within our environment.
But of course you are right, it CAN happen.
So I'll add the "node handling" to the code and update my post later.
Best Regards
Flockston
|
|
|
|
|
When you go to reuse that code you won't have to add that little feature later.
|
|
|
|
|
Hi ,
Is there a way to make our visual studio output exe to run as Admin always ?. I am running visual studio as Admin and I have a Wix installer project with me.Even if I set the admin flag in the exe manually and then make the setup file , it goes and install it as with out having any permissions. Please help
|
|
|
|
|
You need to add an Application Manifest to the project and set the required privilege level in it.
|
|
|
|
|
how to write custom file properties for a FILE. And retrieve them back.
|
|
|
|
|
I'm all ears - please tell us!
I suggest you read this - How to ask a question[^]
=========================================================
I'm an optoholic - my glass is always half full of vodka.
=========================================================
|
|
|
|
|
See here[^].
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
How to append two different file formats and detach them back to its original form.
|
|
|
|
|
You've posted this on QA. Please do not repost, let it be on QA and wait til someone answers your question. If you didn't get any response, you can improve your question and make it more specific.
Thanks.
Don't mind those people who say you're not HOT. At least you know you're COOL.
I'm not afraid of falling, I'm afraid of the sudden stop at the end of the fall! - Richard Andrew x64
|
|
|
|
|
ZIP them?
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
convert string("from var in dbModel.OM_SHIFT") to system.linq.iqueryable in C#
|
|
|
|
|
No thanks - I don't want to.
How to get an answer[^]
=========================================================
I'm an optoholic - my glass is always half full of vodka.
=========================================================
|
|
|
|
|
You really haven't asked a question. You just posted a demand.
But, if you're looking to convert a free-form string into a valid LINQ query, you'd have to build your own query processing library to do it, which is a rather large task. There is no built-in function to do this.
|
|
|
|
|
I've been fighting this battle on my own for months, I could elaborate on the details of everything I've tried and why I ultimately gave up on those other paths... but that would amount to one hell of a long post. I think it might be better just to stick with telling you the path I'm on.
I'm developing an application to print serialized PDFs. Long story short, the most workable solution for getting the PDF to the printer seems to be installing Foxit Reader and using command lines to print using default printer configurations.
Since I don't want to risk mangling the existing printer configuration, I create a clone. If a print setting needs changed, it is written to the clone printer's default settings. When finished printing, I remove the clone and no one is the wiser.
I'm using a WPF PrintDialog to get information about the original printer and allow the user to modify printer configurations. Working from this point, I find the print server that the printer is installed on and grab its print queue.
I'm having hit and miss success at this point. If someone thinks they can offer direction, I can elaborate on what specific failures are occurring, but it seems to be different issues with different printers. Sometimes I am unable to install a clone of the original printer and sometimes the application fails to retrieve information about the original printer.
I was using the general logic outlined here: http://msdn.microsoft.com/en-us/library/aa970846%28v=vs.110%29.aspx
but that didn't work with network printers. My attempts to use PrintServer instead of LocalPrintServer work when the printer is locally installed and are only partially successful otherwise.
I think what I need is to understand the proper way to work with print servers and print ques. How does this change in a network environment? I feel like I'm missing something fundamental about what needs to take place. I don't know, can't really explain what I need to learn if I don't know what to call it in the first place. I'm hoping someone has done this type of thing before and knows what the missing link is.
|
|
|
|
|
Why do you need to create a clone? As I understand the usage of printers in Windows, you can change any settings for each print job, but the printer's default settings always remain.
|
|
|
|
|
If you know a way to create a printable document object out of a PDF, I'm open to new ideas. My understanding was that this required the printer to have native support for printing PDFs. I'm not certain that this would always be true or I would just do it this way and be done with it. Alternatively, I could use a third party library/tool like ghost script, but this seemed to give me about an equivalent level of control relative to using process commands.
Lots of other options available if you shell out $400+. I tried a demo of PDFRender4NET. At first, it seemed to support the printersettings class, which theoretically gave me a way to inject a properly configured devmode structure. Later, their support staff told me PDFRender4NET can't pull printer configurations from the print settings object. It was never intended to configure a printer in that manner.
Printing using a process to issue command line instructions is the closest I've come to something that works. At least, this is the best solution I've been able to figure out. Some other examples involve using pinvoke signatures, but native api calls are too much of a black box for me to really understand how to use them.
Unfortunately, I need to set configurations that are only accessible through the devmode structure. I can't set them via the command line, which means it prints using the default configuration. Changing the default settings seemed like an easy work around, but if the application crashes or we have a power outage mid-print, etc, it would require manually setting the defaults back to normal. Creating a temporary clone is just a precaution against such things and avoids the clutter of having duplicate print ques for each printer pre-installed.
|
|
|
|
|
I am trying to make a fairly robust file writer. I want to specify the format of the file in an XML file. I need to be able to get the datasource name dynamically from the XML file, the XML will look something like this:
<Row Name="Stuff" MoreThanOne="True" Seq="2" Source="data">
I am not sure what that data will look like. It might be a list, it might be a single object, but I want to be able to enumerate it's properties in my class. Something like this:
var data = data.GetType().GetProperty(fieldName).GetValue(data, null).ToString()
I have most of the code ironed out, just not sure how to handle the data source. I could make it less generic and take the data source name out of the XML and just handle it code side, but I like the robustness of having the instructions all XML contained.
Thoughts?
Cheers, --EA
|
|
|
|