PowerShell: how does the PowerShell instantiate C# classes - c#

Initially, I think PowerShell instantiate one class only when the cmdlet tagged on this class is called. On execution, each cmdlet falls into the BeginProcess -> ProcessRecord -> EndProcess(StopProcess) path, and after the EndProcess is done, it seems the process will end and then the memory will collect all these class objects as garbage.Therefore each class should live in their own life cycle and not share any resources. When we are calling these cmdlets,
However I find that classes do share the same static values in the same module. For example, assume in my project I have two classes:
namespace PSDSL
{
[Cmdlet(VerbsCommon.Get, "MyTest")]
public class GetMyTest : Cmdlet
{
public static GlobalUserName = "";
[Parameter(Mandatory = false)]
public string Filepath { get; set; }
protected override void InnerProcessRecord()
{
if (_filepath != null)
{
GlobalUserName = _filepath;
}
Console.WriteLine(GlobalUserName);
}
}
}
namespace PSDSL
{
[Cmdlet(VerbsCommon.Get, "MyTest2")]
public class GetMyTest2 : Cmdlet
{
[Parameter(Mandatory = false)]
public string Filepath { get; set; }
protected override void InnerProcessRecord()
{
if (_filepath != null)
{
GlobalUserName = _filepath;
}
Console.WriteLine(GlobalUserName);
}
}
}
The two commands are pretty similar except one defines a static GlobalUserName. Calling these 2 cmdlets shows that the GlobalUserName can be read\write from both cmdlets.
My confusion is that, when are the classes be instaniated?

Whole assembly loaded at once and stays loaded till restart of the PowerShell prompt.
Details:
Smallest unit of code isolation in .Net is Assembly (in most cases single managed DLL).
Process that uses managed runtime can't load less than single assembly at a time - so all classes from that assembly (and related once on demand) will be loaded together. As result all static fields will be present at the same time in memory (note that static fields are initialized "before first use of the class" which mean they are not necessary initialized on load of the assembly).
There also no way to "unload" class or even assembly without using separate AppDomains. PowerShell does not use multiple AppDomains to load assemblies for different modules (generally cross-AddDomain calls require special attention during implementation and you'd know about it by now). As result once loaded module stays in memory till you quit PowerShell (covered in Powershell Unload Module... completely).
Since assembly is loaded once for all commandlets in it all static fields will be present at once and keep they values till exiting of PowerShell.
Side note: I'd strongly recommend avoiding static fields for anything but really static immutable data in general. It is way to easy to leave some random values there and impact future code. In PowerShell pipeline is the way to pass information between commandlets, other types of processes (WinForms, ASP.Net,...) have they own preferred mechanism to pass data instead of using static.

Related

Instantiate a class with internal ctor, from a different assembly

I thought an internal ctor cannot be accessed from a different assembly. Today for the first time I actually needed to use this idea, but it doesn't work as I expected - it can be accessed from a different assembly.
namespace A {
public class AA {
internal AA() { }
}
}
namespace TestNamespace {
public class TestClass {
public void TestMethod() {
var instance = new A.AA(); // <-- this compiles!
}
}
}
...so I'm doing it wrong, or don't know what I'm doing.
Assembly != Namespace
Namespaces provide a logical organizational system. Namespaces are
used both as an "internal" organization system for a program, and as
an "external" organization system — a way of presenting program
elements that are exposed to other programs.
While
Assemblies are used for
physical packaging and deployment. An assembly may contain types, the
executable code used to implement these types, and references to other
assemblies.
Assemblies are usually projects, C# wise.
Read more about it here.

Emulate VB.Net Module in C#

I just started working in C# after working in VB.Net for a while. I have a dll that contains frequently used database connection strings (and some other db info) and another dll that contains frequently used directory paths. When creating an application in VB, I would use these dlls in a Module to set the environment to testing so the databases would point to the QA versions of the databases and the directories would point to my local directories. When I was ready to publish the application, I would set the environment to production. It looked something like this.
Module MyModule
Friend MyBizDBs As BizDBs
Friend MyBizPaths As BizPaths
Friend TodaysDate As Date
Friend Sub InitializeModule()
MyBizDBs = New BizDBs(DBReference.BizDatabases.DBConfig.DB_Environment.QA)
MyBizPaths = New BizPaths(BizPaths.PathEnvironment.Test)
TodaysDate = DBReference.BizDatabases.DBMethods.GetTodaysDate
End Sub
End Module
I could then call InitializeModule at the start of the application and whenever a class needed a connection string or a directory or anything else offered by those dlls, it would be pointing to the correct environment. Moreover, I didn't need to create a new instance of MyModule each time. Instead I could simply write:
Using sw as new StreamWriter(MyBizPaths.ReconciliationReport)
I know C# doesn't have Modules. I know static classes are similar, but I can't initialize the objects (MyBizDBs and MyBizPaths) to set them to the appropriate environment in static classes. I'm looking at a C# Singleton, but that reinitializes each time the class is called. Is there a better way to achieve what I'm trying to do in C#?
Thanks!
EDIT: Here is what the singleton looks like in C#.
public sealed class MySingleton
{
private static readonly MySingleton instance = new MySingleton();
private MySingleton() { }
public static MySingleton Instance
{
get
{
return instance;
}
}
public BizPaths MyBizPaths = new BizPaths(BizPaths.PathEnvironment.Test);
public BizDBs MyBizDBs = new BizDBs(DBConfig.DB_Environment.QA);
public DateTime todaysDate = DBMethods.GetTodaysDate();
}
There's only one way to emulate a VB module in C# and that's with a static class. Once compiled, a VB module IS a static class, so obviously that's what you should use in C# if you want the same behaviour as a VB module. The difference is that module members don't have to be qualified with their type name in VB, while C# static class members do require qualification.

C#, Context Object with Method that takes Interface to External DLL, Throws MissingMethodException

I have a service which acts as a scripting engine for normalization of content. The service extends its scripting language by dynamically loading DLLs at startup, passing a context object interface to a Load() method in each DLL. The context object provides the DLL with methods for Binding string names to factory objects, commands/functors, script object wrappers, etc.
I am having a problem where when the Load() method of one of the DLLs is called it throws a MissingMethodException if it attempts to call one of the binding methods. The method is one which takes an interface which is declared in another DLL, and I suspect something is going on with the method signature when passing the context object through Type.GetMethod.Invoke().
It should be noted that the exception is only thrown when dynamically loading the DLL via Assembly.LoadFile(). Referencing the DLL in the main process and using reflection to get the class/method, then dynamically invoking it from there causes no exception to be thrown.
My code/project reference setup ...
Content.dll:
References nothing
public class ContentPackage { ... }
public interface IContentDataSource
{
ContentPackage Receive();
void Send(ContentPackage content);
}
public interface IContentDataSourceFactory
{
IContentDataSource CreateInstance(string param);
IEnumerable<IContentDataSource> CreateInstances(string param);
}
public class ContentXmlDataSource : IContentDataSource
{
ContentPackage IContentDataSource.Receive() { ... }
void IContentDataSource.Send(ContentPackage content) { ... }
public class Factory : IContentDataSourceFactory
{
IContentDataSource IContentDataSourceFactory.CreateInstance(string param) { return new ContentXmlDataSource(param); }
IEnumerable<IContentDataSource> IContentDataSourceFactory.CreateInstances(string param) { ... }
IContentDataSourceFactory.CreateInstance(
public static Factory Singleton { get { return s_Singleton; } }
private static Factory m_Singleton = new Factory();
}
}
ExtensionInterface.dll:
References Content.dll
public interface IXmlTagCommand() { ... }
public interface IExtensionBindingContext
{
void BindCommand(string name, IXmlTagCommand cmd);
void BindDataSourceFactory(string name, IContentDataSourceFactory factory);
}
Service.dll:
References ExtensionInterface.dll, Content.dll
public partial class TheService
{
public class ExtensionBindingContext : IExtensionBindingContext
{
void IExtensionBindingContext.BindCommand(string name, IXmlTagCommand cmd)
{ ... }
void BindDataSourceFactory(string name, IContentDataSourceFactory factory)
{ ... }
ExtensionBindingContext(string basePath)
{
var paths = Directory.GetFiles(basePath, "*.dll");
foreach (var path in paths)
{
Assembly assembly = Assembly.LoadFile(path);
Type t = assembly.GetType("ExtensionLibrary");
MethodInfo method = t.GetMethod("Load",
BindingFlags.Public | BindingFlags.Static);
method.Invoke(null, new object[] { this }); // Throws exception!
}
}
}
TheService()
{
(new ExtensionBindingContext()).LoadExtensions(".\DLLFolder\");
}
}
Extension.StandardContentFactories.dll: (Loaded dynamically)
References ExtensionInterface.dll, Content.dll
public class ExtensionLibrary
{
public void Load(IExtensionBindingContext context)
{
context.BindCommand("SomeCommand", XTagCommands.SomeCommand); // Fine: No exception.
context.BindDataSourceFactory("ContentXml", ContentXmlFactory.Singleton); // Causes whole function to throw exception if not commented out, preventing even the "BindCommand" call to not be reached.
}
}
The exception specifics:
Exception:
System.Reflection.TargetInvocationException, {"Exception has been thrown by the target of an invocation."}
Inner Exception:
{"Method not found: 'Void SomeNamespace.ContentService.IExtensionBindingContext.BindDataSourceFactory(System.String, SomeNamespace.Content.IContentDataSourceFactory)'."}
If I comment out the BindDataSourceFactory() line in StandardContentFactories.dll, the problem goes away. I am also successfully loading multiple other DLLs in this manner.
I've tried the following:
- Checking GAC folders for same named assemblies from project, none found
- Cleaning solution and rebuilding
- Verifying that all assemblies were using same .NET (4.0, not client profile)
- Passing the interface in a wrapper object instead, changing the method to take the wrapper: Field not found exception instead
- Passing the factory object as an "Object" (updating method parameters as well), then casting that object back into the interface that the factory object inherits: Invalid cast
Is there some kind of issue with passing objects behind interfaces to DLLs via dynamic assembly loading and invoking if that interface has a method that takes an interface which is defined in another referenced assembly? Are there possible work arounds to allow for this dynamic binding of factories from unknown DLLs if the interface issue is non-solvable?
EDIT:
After checking the modules as per mike z's suggestion, I see that their are two copies of each shared interface DLL being loaded (Content.dll and ExtensionInterface.dll); the first set of DLLs is being loaded from the service's host process bin\Debug directory; the second set of DLLs is being loaded from the bin\Debug directory of the first addon DLL that is loaded by Assembly.LoadFile().
The process uses an array of directory paths to search for addon DLLs in, and I was able to force the process to find/load the DLL that has the problem first, which caused the problem to go away. I'm guessing that all of the Assembly.LoadFile() loaded DLLs are using the set of shared interface DLLs that are loaded as a side effect of the first call to Assembly.LoadFile() (i.e. for the first addon DLL). When these DLLs load, maybe they are only including interface information for the IExtensionBindingContext.BindCommand() but not the IExtensionBindingContext.BindDataSourceFactory() and/or IContentDataSourceFactory since they are not used by that DLL.
Does anybody know if this assessment is correct? If so, is there a (standard or correct) way to force my Assembly.LoadFile() DLLs to correctly load ALL of the interface/method information from a shared library? (I'd rather not use contrived solutions like forcing the load order from a config file or creating a fake library that used all the features at the start.)

How to redirect a method call?

I have got a DLL which works fine in Windows, but Inside one of its private functions the static System.IO.File.ReadAllBytes() is called. I have to use this DLL on a windows CE smart Device Project with Compact Framework 3.5 and there is no such method in the System.IO.File. I have tried to create a class named "File" inside the project like this:
public class File
{
internal static byte[] ReadAllBytes(string path)
{
}
internal static void WriteAllBytes(string path, byte[] bytes)
{
}
}
My own calls to the static Methods of the class File are redirected here But the calls inside the DLL methods still go to the System.Io.File class and I still get the MissingMethodException. I tried the methods with public modifiers but saw no change.
I even tried to rewrite the public method that calls the private method inside which the ReadAllbytes was invoked and used MethodInfo.Invoke with no success.
The question: Is there a way to force the method inside the Dll to accept my ReadAllbytes Method instead of System.File.IO.ReadAllBytes()? The invocation inside the DLL is like this:
using System.IO.File;
namespace Something
{
class SomeClass
{
public Boolean someMethod()
{
byte[] myBytes = File.ReadAllBytes(filePath);
}
}
}
Static methods like File.ReadAllBytes are resolved at compile time to a specific Assembly name and class. You will need to modify the DLL to change the call. Prehaps you could decompile and recompile it, or edit the IL.
*It is possible to redirect the call using the profiling hooks (the sameone the debugger uses), and that is how the Moles Mocking framework works. However it would not be suitable for production use.

Dealing with optional dependencies (C#)

We have an app which optionally integrates with TFS, however as the integration is optional I obviously don't want to have all machine need the TFS assemblies as a requirement.
What should I do?
Is it ok for me to reference the TFS libraries in my main assemblies and just make sure that I only reference TFS related objects when I'm using TFS integration.
Alternatively the safer option would be to reference the TFS libraries in some separate "TFSWrapper" assembly:
a. Is it then ok for me to reference that assembly directly (again as long as I'm careful about what I call)
b. Should I instead be exposing a set of interfaces for my TFSWrapper assembly to implement, and then instantiate those objects using reflection when required.
1 seems risky to me, on the flip side 2b seems over-the-top - I would essentially be building a plug-in system.
Surely there must be a simpler way.
The safest way (i.e. the easiest way to not make a mistake in your application) might be as follows.
Make an interface which abstracts your use of TFS, for example:
interface ITfs
{
bool checkout(string filename);
}
Write a class which implements this interface using TFS:
class Tfs : ITfs
{
public bool checkout(string filename)
{
... code here which uses the TFS assembly ...
}
}
Write another class which implements this interface without using TFS:
class NoTfs : ITfs
{
public bool checkout(string filename)
{
//TFS not installed so checking out is impossible
return false;
}
}
Have a singleton somewhere:
static class TfsFactory
{
public static ITfs instance;
static TfsFactory()
{
... code here to set the instance
either to an instance of the Tfs class
or to an instance of the NoTfs class ...
}
}
Now there's only one place which needs to be careful (i.e. the TfsFactory constructor); the rest of your code can invoke the ITfs methods of your TfsFactory.instance without knowing whether TFS is installed.
To answer recent comments below:
According to my tests (I don't know whether this is 'defined behaviour') an exception is thrown when (as soon as) you call a method which depends on the missing assembly. Therefore it's important to encapsulate your code-which-depends-on-the-missing-assembly in at least a separate method (or a separate class) in your assembly.
For example, the following won't load if the Talk assembly is missing:
using System;
using OptionalLibrary;
namespace TestReferences
{
class MainClass
{
public static void Main(string[] args)
{
if (args.Length > 0 && args[0] == "1") {
Talk talk = new Talk();
Console.WriteLine(talk.sayHello() + " " + talk.sayWorld() + "!");
} else {
Console.WriteLine("2 Hello World!");
}
}
}
}
The following will load:
using System;
using OptionalLibrary;
namespace TestReferences
{
class MainClass
{
public static void Main(string[] args)
{
if (args.Length > 0 && args[0] == "1") {
foo();
} else {
Console.WriteLine("2 Hello World!");
}
}
static void foo()
{
Talk talk = new Talk();
Console.WriteLine(talk.sayHello() + " " + talk.sayWorld() + "!");
}
}
}
These are the test results (using MSVC# 2010 and .NET on Windows):
C:\github\TestReferences\TestReferences\TestReferences\bin\Debug>TestReferences.exe
2 Hello World!
C:\github\TestReferences\TestReferences\TestReferences\bin\Debug>TestReferences.exe 1
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'OptionalLibrary, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
at TestReferences.MainClass.foo()
at TestReferences.MainClass.Main(String[] args) in C:\github\TestReferences\TestReferences\TestReferences\Program.cs:
line 11
C:\github\TestReferences\TestReferences\TestReferences\bin\Debug>
You might look at Managed Extensibility Framework (MEF).
A "plug-in" concept may be the way to go, and it may also allow you to (later) extend your application to work with other products than TFS if needed. Option 2a will be just as "risky" (failing when the linked files are missing) as option 1.
You can make an assembly with the required interfaces for your specific purpose, and reference this assembly from both your app and the "TFS plug-in". The latter then provides implementations of your interfaces and uses TFS to perform the operations. The app can dynamically load an assembly and create instances of the plug-in types needed (via Activator etc.) and cast those instances to your interfaces.
In fact, if you make those types inherit from MarshalByRef, you could even load them into another AppDomain and thus make a clean separation of your plugins, and also make them unloadable.

Categories