C#: creating partial functions dynamically - c#

I have a 3rd party scripting engine contained in a session in my code. The engine takes any delegate and makes it available to it's script with the same signature.
Now I want to have plugins that provide these delegates for the engine, but I also want extra data from the session without it showing up in the script.
The script consuming the delegate should have no idea about the session, but the plugin implementing it does. The plugin writer should be free to use any number or types of arguments for the plugin delegates, so I need to do this dynamically at run time.
For example:
//from available plugin delegates
delegate bool SendMessage(Session info, string ip, int port, string message);
delegate void LogMessage(Session info, string message);
//to create script delegates
delegate bool SendMessage(string ip, int port, string message);
delegate void LogMessage(string message);
So when the script engine calls LogMessage("Test") it should invoke LogMessage(mysession, "Test") in the plugin.
I found information on curry for adding defaults to delegates and Reflection could create the delegates, but how can they be fit together to accomplish this?
EDIT: full length example
public class Session
{
//Some metadata here
}
public class Plugin
{
private delegate bool SendMessage(Session info, string ip, int port, string message);
private delegate void LogMessage(Session info, string message);
public Delegate[] GetFunctions()
{
return new Delegate[] { new SendMessage(HandleSendMessage), new LogMessage(HandleLogMessage) };
}
private bool HandleSendMessage(Session info, string ip, int port, string message)
{
Console.WriteLine($"SEND {ip}:{port} >> \"{message}\"");
return true;
}
private void HandleLogMessage(Session info, string message)
{
Console.WriteLine($"LOG \"{message}\"");
}
}
//stand-in for 3rd party code
public class Engine
{
private IEnumerable<Delegate> _functions = null;
public void Add(IEnumerable<Delegate> functions)
{
//ignore this code, just simulating 3rd party behavior
_functions = functions;
}
public void Execute()
{
//ignore this code, just simulating 3rd party behavior
foreach (Delegate function in _functions)
{
ParameterInfo[] fparams = function.Method.GetParameters();
int n = fparams.Count();
object[] args = new object[n];
for (int i = 0; i < n; i++)
{
if (string.Compare(fparams[i].Name, "ip") == 0)
{
args[i] = "127.0.0.1";
}
else if (string.Compare(fparams[i].Name, "port") == 0)
{
args[i] = 80;
}
else if (string.Compare(fparams[i].Name, "message") == 0)
{
args[i] = "Some message";
}
else if (string.Compare(fparams[i].Name, "info") == 0)
{
Console.WriteLine("Error this should not be here");
args[i] = null;
}
}
function.DynamicInvoke(args);
}
}
}
class Program
{
static void Main(string[] args)
{
Plugin p = new Plugin(); //assume this instead comes from Assembly.Load(..) and Activator.CreateInstance(..)
Engine e = new Engine(); //stand-in for 3rd party code
List<Delegate> newDelegates = new List<Delegate>();
foreach (Delegate d in p.GetFunctions())
{
//QUESTION: create a new delegate same as (d) minus the first param (Session info)
//QUESTION: link the new delegate to (d) and set (Session info) to some value
newDelegates.Add(d); //add new delegate instead of (d)
}
e.Add(newDelegates);
e.Execute();
}
}
EDIT 2: Progress update
I can now create a delegate type with less variables then the original
/// <summary>
/// Based on code from user svick [https://stackoverflow.com/questions/9505117/creating-delegates-dynamically-with-parameter-names]
/// </summary>
class DelegateTypeFactory
{
private readonly ModuleBuilder _module;
public DelegateTypeFactory()
{
//Build in-memory assembly to contain the new types
AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("DelegateTypeFactory"), AssemblyBuilderAccess.RunAndCollect);
_module = assembly.DefineDynamicModule("DelegateTypeFactory");
}
public Type CreateDelegateType(MethodInfo method)
{
//Create new name for the type to avoid clashes
string nameBase = string.Format("{0}{1}", method.DeclaringType.Name, method.Name);
string name = GetUniqueName(nameBase);
//Create the toolset to make the new type
TypeBuilder builder = _module.DefineType(name, TypeAttributes.Sealed | TypeAttributes.Public, typeof(MulticastDelegate));
ConstructorBuilder constructor = builder.DefineConstructor(MethodAttributes.RTSpecialName | MethodAttributes.HideBySig | MethodAttributes.Public, CallingConventions.Standard, new[] { typeof(object), typeof(IntPtr) });
constructor.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
//define the methods params and filter unwanted param
ParameterInfo[] parameters = method.GetParameters();
parameters = parameters.Where(p => p.ParameterType != typeof(Session)).ToArray();
//design the method signature
MethodBuilder invokeMethod = builder.DefineMethod("Invoke", MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Public, method.ReturnType, parameters.Select(p => p.ParameterType).ToArray());
invokeMethod.SetImplementationFlags(MethodImplAttributes.CodeTypeMask);
for (int i = 0; i < parameters.Length; i++)
{
invokeMethod.DefineParameter(i + 1, ParameterAttributes.None, parameters[i].Name);
}
//Return the newly created delegate type
return builder.CreateType();
}
private string GetUniqueName(string nameBase)
{
int number = 2;
string name = nameBase;
while (_module.GetType(name) != null)
{
name = $"{nameBase}{number++}";
}
return name;
}
}
Usage:
DelegateTypeFactory factory = new ConsoleApplication1.DelegateTypeFactory();
Type newDelegateType = factory .CreateDelegateType(originalDelegate.Method);
However how one could instantiating the new delegate and make it call the original delegate with the default session value eludes me

It seems like you have Plugins passing delegates into the Engine.
The engine then invokes the plugins dynamically.
You can do this with closures, but the plugin would have to create the closure since it is creating the delegate.
So 3rd party developers could use this technique as well, it would be up to them. If they don't need any extra objects available in the delegate they don't have to.
It would be transparent to the Engine that the delegate has captured other variables.
I see in your main you have comments that indicate you're thinking about mutating the plugin functions there.
I don't know how you would do it there since you wouldn't know what paramaters the Plugin author intended to be in/visible.
So I wrote this to allow the Plugin to decide what it wants to hide.
I left your Handle* methods the way you wrote them, but they do have access to the Session objects if required.
public class Session
{
//Some metadata here
}
public class Plugin
{
private delegate bool SendMessage(string ip, int port, string message);
private delegate void LogMessage(string message);
public Delegate[] GetFunctions()
{
var sessionInfo = new Session();
return new Delegate[] { new SendMessage(HandleSendMessage(sessionInfo)), new LogMessage(HandleLogMessage(sessionInfo)) };
}
private SendMessage HandleSendMessage(Session info)
{
return delegate (string ip, int port, string message)
{
Console.WriteLine($"SEND {ip}:{port} >> \"{message}\"");
return true;
};
}
private LogMessage HandleLogMessage(Session info)
{
return delegate (string message)
{
Console.WriteLine($"LOG \"{message}\"");
};
}
}
//stand-in for 3rd party code
public class Engine
{
private IEnumerable<Delegate> _functions = null;
public void Add(IEnumerable<Delegate> functions)
{
//ignore this code, just simulating 3rd party behavior
_functions = functions;
}
public void Execute()
{
//ignore this code, just simulating 3rd party behavior
foreach (Delegate function in _functions)
{
ParameterInfo[] fparams = function.Method.GetParameters();
int n = fparams.Count();
object[] args = new object[n];
for (int i = 0; i < n; i++)
{
if (string.Compare(fparams[i].Name, "ip") == 0)
{
args[i] = "127.0.0.1";
}
else if (string.Compare(fparams[i].Name, "port") == 0)
{
args[i] = 80;
}
else if (string.Compare(fparams[i].Name, "message") == 0)
{
args[i] = "Some message";
}
else if (string.Compare(fparams[i].Name, "info") == 0)
{
Console.WriteLine("Error this should not be here");
args[i] = null;
}
}
function.DynamicInvoke(args);
}
}
}
class Program
{
static void Main(string[] args)
{
Plugin p = new Plugin(); //assume this instead comes from Assembly.Load(..) and Activator.CreateInstance(..)
Engine e = new Engine(); //stand-in for 3rd party code
List<Delegate> newDelegates = new List<Delegate>();
foreach (Delegate d in p.GetFunctions())
{
//QUESTION: create a new delegate same as (d) minus the first param (Session info)
//QUESTION: link the new delegate to (d) and set (Session info) to some value
newDelegates.Add(d); //add new delegate instead of (d)
}
e.Add(newDelegates);
e.Execute();
}
}

Related

Instrument a methods to intercept each method's call

I have the need (a new requirements) to perform few action before and after each method call of a bunch of classes in my project. Is a situation that somewhat reassemble a AOP but what i need is not that complex. I have some BusinessObject (class) and need that each method invokation of my API contained in that BOs will handle logging and authoriting. Because i would avoid to rewrite my codebase i have thougt to wrap my BO, in that way i can "instrument" them, but i am not sure if this is the best approach nor how to deal with some details
Example :
public class TestBo : BaseBo
{
private string test = "";
public TestBo ( string input )
{
test = input;
}
[PermissionRequired(PermissionAttribute.Login | PermissionAttribute.Read)]
public void ExampleMethod ()
{
Console.WriteLine("Run it with " + test);
}
public void WriteMessage ( string message )
{
Console.WriteLine("Write it " + message);
}
}
// IN THE BO Wrapper Class
public virtual OperationResultDto<object> Execute(Action action, Token token)
{
return this.ActionExecute(action, token);
}
public virtual OperationResultDto<object> Execute<T1>(Action<T1> action, Token token , T1 param)
{
return this.ActionExecute(action, token, param);
}
protected virtual OperationResultDto<object> ActionExecute (Delegate action, Token token, params object[] parameters)
{
try
{
if (action == null)
throw new ArgumentNullException("action");
this._logger.Debug(this.DumpParameters(action, parameters));
PermissionRequired permission = action.Method.GetCustomAttributes(false).FirstOrDefault(attr => attr is PermissionRequired) as PermissionRequired;
//TODO : authorization;
action.DynamicInvoke(parameters);
this._logger.Debug($"Invokation {action.Method.Name} ended");
return new OperationResultDto<object>() { Success = true, ReturnData = null };
}
catch (Exception err)
{
this._logger.Error(err, string.Empty);
return new OperationResultDto<object>()
{
Success = false,
ErrorText = err.Message,
Error = err,
ReturnData = null
};
}
return null;
}
//USAGE
TestBo testBo = new TestBo("Identity");
testBo.ExampleMethod();
Token token = new Token(1);
BoWrapper wrapper = new BoWrapper(testBo);
wrapper.Execute(testBo.ExampleMethod, token);
wrapper.Execute<string>(testBo.WriteMessage, token, "Message");
That seems to work as intended, but i don't like that :
wrapper.Execute<string>.. //I have to repeat method signature each time
public virtual OperationResultDto<object> Execute<T1>(Action<T1>..
//I have to write many overload (in BoWrapper) to handle all possible situation.

non-static field, error even when i declare the function as static

I am sill get a
"An object reference is required for the non-static field, method, or property..."
error even when i declare it as static.
I cant seem to hack out a fix for it. I have spent enough time on it and need help or this is my doom.
I also tied to set:
TestFeature test = new TestFeature();
and use
static public void DispData()
{
test.richTextBox1.Text = "1";
}
My goal is to dump a whole bunch of data incoming from com port into a richtextbox1.
dump[i] = string.Format("{0:X2}", hex.ToString());
DispData(ref string[] dump);
here is the full code:
namespace SW_Public
{
public partial class TestFeature : Form
{
public TestFeature()
{
InitializeComponent();
this.Text = FRM_Title.PadLeft(5);
richTextBox1.Text = "RTB1";
richTextBox2.Text = "RTB2";
}
.....
static public void DispData(ref string[] dump)
{
richTextBox1.Text = dump;
}
static void DisplayData(Byte Cmd, Byte[] RxData, int len)
{
switch (Cmd)
{
case (int)RXCMD.CMD_GETVERSION:
.....
case (int)RXCMD.CMD_RMEM:
{
string[] dump = new string[512];
for (int i = 0; i < len; i++)
{
byte hex = RxData[i];
dump[i] = string.Format("{0:X2}", hex.ToString());
DispData(ref string[] dump);
}
break;
}
}
}
}
}
Remove all of the static modifiers from all of the methods in the class.
You cannot access instance variables or methods directly from within static methods.

After Assembly.LoadFile setup Callback

I'm trying to figure out how to setup a call back after I load a DLL dynamically using Assembly.LoadFile. In the below example, "MyCallBackMethod" is a non delegate, so it will not work. I've created a new DLL, and Reference that DLL in both projects and I can pass that object around, but is that really the correct way to do it or am I overlooking something simple?
string fullDLLPath = #"C:\Code\MyTest\MyDLL.dll";
Assembly assembly = Assembly.LoadFile(fullDLLPath);
Type type = assembly.GetType("MyNameSpace.MyClass");
if (type != null)
{
//get both the start and stop method
MethodInfo myMethod = type.GetMethod("MyMethod");
if (myMethod != null)
{
object result = null;
//create instance
object classInstance = Activator.CreateInstance(type, null);
//get all parameters
ParameterInfo[] paramInfo = myMethod.GetParameters();
object[] paramToPass = null;
foreach (ParameterInfo pi in paramInfo)
{
paramToPass = new object[] { MyCallBackMethod };
break;
}
result = myMethod.Invoke(classInstance, paramToPass);
if (result != null)
{
Console.WriteLine(result);
}
}
}
I'll just go with a 3rd DLL which is referenced in both projects. Then I can pass that Class through the object[] with no issues. The DLL then puts that as a static within it's own Class and can reference that to send messages back to the EXE.
EXE:
static public Logger.Textlog Logging { get; } = new Logger.Textlog();
...
foreach (ParameterInfo pi in paramInfo)
{
paramToPass = new object[] { Logging };
}
DLL:
static public Logger.Textlog Logging { get; } = new Logger.Textlog();
public bool MyMethod(Logger.Textlog passedTextLog = null)
{
if (passedTextLog != null) {
Logging = passedTextLog;
Logging.WriteLine(LOG_TRANSACTION, "Here");
}
...
}

Use Rhino Mock to report the function that was called

I have a failing testcase that depends on an external module.
I want to use Rhino Mock to generate a report on called functions.
I created a minimal example that illustrates my problem:
using NUnit.Framework;
using Rhino.Mocks;
using System;
namespace StackOverflow_namespace
{
public interface IUsefulService
{
object HiddenAmongManyCalls();
}
public class ThirdPartyBase
{
private int a = 42;
public ThirdPartyBase(IUsefulService service)
{
object liveFastDieYoung = service.HiddenAmongManyCalls();
liveFastDieYoung.Equals(a);
}
}
public class MyParty : ThirdPartyBase
{
public MyParty(IUsefulService service) : base(service)
{
}
}
[TestFixture]
class StackOverflow
{
[Test]
public void Hypothetical()
{
IUsefulService service = MockRepository.GenerateMock<IUsefulService>();
try
{
var party = new MyParty(service);
}
catch(Exception e)
{
string[] calls = MagicallyGetTheCallsThatWereMadeToTheMock();
foreach(var call in calls)
{
//with my visual studio testrunner for nunit 3 I can investigate stored console output
Console.WriteLine(call);
}
Assert.Fail("Excpexted no exception but was '" + e.GetType().Name + "': " + e.Message);
}
}
private string[] MagicallyGetTheCallsThatWereMadeToTheMock()
{
return new[]
{
"This is where I am lost, I do not know how to get the calls from the repository."
};
}
}
}
I tried to find something online without success.
Do Rhino Mocks record all calls and can I access that list?
Edit:
An attempt to verify Expectations did not work since I am looking for calls I did not expect.
I could build a list of calls using GetArgumentsForCallsMadeOn. I can reflect on the Interface. I started on a method for that but I currently fail to see how I can convert a MethodInfo to an Action<T>.
private IEnumerable<string> GetCallsList<Interface>(Interface rhinomock)
{
Type interfaceType = typeof(Interface);
List<MethodInfo> interfaceMethodInfos = new List<MethodInfo>();
List<string> returnInfos = new List<string>();
StringBuilder callbuilder = new StringBuilder();
foreach (var property in interfaceType.GetProperties())
{
interfaceMethodInfos.Add(property.GetGetMethod());
interfaceMethodInfos.Add(property.GetSetMethod());
}
foreach (var method in interfaceType.GetMethods())
{
interfaceMethodInfos.Add(method);
}
foreach (var methodinfo in interfaceMethodInfos)
{
Action<Interface> magic = null; //convert methodinfo into action - still missing
var calls = rhinomock.GetArgumentsForCallsMadeOn(magic); //magic is currently null, here be crash
foreach (var call in calls)
{
bool more = false;
callbuilder.Clear().Append(interfaceType.Name).Append('.').Append(methodinfo.Name).Append('(');
foreach (var parameter in call)
{
if (more){ callbuilder.Append(", "); }
if (null == parameter) { callbuilder.Append("<null>"); }
else { callbuilder.Append(parameter.ToString()); }
more = true;
}
callbuilder.Append(')');
string callInfo = callbuilder.ToString();
returnInfos.Add(callInfo);
}
}
return returnInfos;
}
I was able to use reflection to get the output I wanted.
Here is the minimal example where the test fails and the output contains all method calls.
using NUnit.Framework;
using Rhino.Mocks;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace StackOverflow_namespace
{
public interface IUsefulService
{
object HiddenAmongManyCalls();
string TestCall2(string arg1, int arg2);
string FULLACCESS { get; set; }
string READONLY { get; }
}
public class ThirdPartyBase
{
private int a = 42;
public ThirdPartyBase(IUsefulService service)
{
service.TestCall2("callA", 1);
service.TestCall2("callB", 1);
object liveFastDieYoung = service.HiddenAmongManyCalls();
service.TestCall2("callA", 2);
service.TestCall2("callB", 2);
var a = service.FULLACCESS;
var b = service.READONLY;
service.FULLACCESS = "some";
liveFastDieYoung.Equals(a);
}
}
public class MyParty : ThirdPartyBase
{
public MyParty(IUsefulService service) : base(service)
{
}
}
[TestFixture]
class StackOverflow
{
[Test]
public void Hypothetical()
{
IUsefulService service = MockRepository.GenerateMock<IUsefulService>();
try
{
var party = new MyParty(service);
}
catch (Exception e)
{
var calls = GetCallsList(service);
foreach (var call in calls)
{
//with my visual studio testrunner for nunit 3 I can investigate stored console output
Console.WriteLine(call);
}
Assert.Fail("Excpexted no exception but was '" + e.GetType().Name + "': " + e.Message);
}
}
private IEnumerable<string> GetCallsList<Interface>(Interface rhinomock)
{
Type interfaceType = typeof(Interface);
List<MethodInfo> interfaceMethodInfos = new List<MethodInfo>();
List<string> returnInfos = new List<string>();
StringBuilder callbuilder = new StringBuilder();
foreach (var property in interfaceType.GetProperties())
{
AddMethodInfoIfValid(interfaceMethodInfos, property.GetGetMethod());
AddMethodInfoIfValid(interfaceMethodInfos, property.GetSetMethod());
}
foreach (var method in interfaceType.GetMethods())
{
AddMethodInfoIfValid(interfaceMethodInfos, method);
}
foreach (var methodinfo in interfaceMethodInfos)
{
int paramcount = methodinfo.GetParameters().Length;
object[] args = new object[paramcount];
Action<Interface> lambdacall = (i) => methodinfo.Invoke(i, args);
var calls = rhinomock.GetArgumentsForCallsMadeOn(lambdacall);
foreach (var call in calls)
{
bool more = false;
callbuilder.Clear().Append(interfaceType.Name).Append('.').Append(methodinfo.Name).Append('(');
foreach (var parameter in call)
{
if (more) { callbuilder.Append(", "); }
if (null == parameter) { callbuilder.Append("<null>"); }
else {
callbuilder
.Append('(').Append(parameter.GetType().Name).Append(")'")
.Append(parameter.ToString()).Append("'");
}
more = true;
}
callbuilder.Append(')');
string callInfo = callbuilder.ToString();
returnInfos.Add(callInfo);
}
}
return returnInfos;
}
private static void AddMethodInfoIfValid(List<MethodInfo> interfaceMethodInfos, MethodInfo methodinfo)
{
if (null != methodinfo)
{
interfaceMethodInfos.Add(methodinfo);
}
}
}
}

Update Parent object from within Child

I have a List in Class 1.
I have a helper class (Class 2) which is instantiated inside Class 1.
Class 2 should then do some work and update the List in Class 1.
I tried passing Class 1 into the Constructor of Class 2, but this only modifies the version within Class 2.
public class Server // Class 1
{
private string m_serverName;
private string m_loginUsername;
private string m_loginPassword;
private ServerConnectionTools m_connectionTools;
public List<Service> Services { get; private set; }
public Server(string serverName, string loginUsername, string loginPassword)
{
this.ServerName = serverName;
this.LoginUsername = loginUsername;
this.LoginPassword = loginPassword;
// Login to server and retrieve list of services
ConnectionTools = new ServerConnectionTools(this);
}
}
public class ServerConnectionTools // Class 2
{
private Server m_server;
private ManagementScope m_scope;
public ServerConnectionTools(Server server)
{
this.Server = server;
this.Scope = InitiateScope();
try
{
// Once this is finished updating, I need to update the Service List in Class 1.
this.UpdateServicesList();
}
catch (System.Runtime.InteropServices.COMException)
{
// Server is unavailable
Console.WriteLine("Unable to reach server {0}", server.ServerName);
}
}
public ManagementScope InitiateScope()
{
ManagementScope scope;
// If server is Remote server, log in with Credentials
// otherwise no need to connect.
if (System.Environment.MachineName.ToLower() != this.Server.ServerName.ToLower())
{
// Is a remote server, need credentials
ConnectionOptions options = new ConnectionOptions();
options.Username = this.Server.LoginUsername;
options.Password = this.Server.LoginPassword;
scope = new ManagementScope("\\\\" + this.Server.ServerName + "\\root\\cimv2", options);
}
else
{
// Local machine, no need for credentials
scope = new ManagementScope("\\\\" + this.Server.ServerName + "\\root\\cimv2");
}
return scope;
}
public void UpdateServicesList()
{
// Connect our scope to the actual WMI scope
this.Scope.Connect();
List<StringBuilder> servicesList = new List<StringBuilder>();
// Query system for Services
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_Service WHERE Caption LIKE 'xxx%'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(this.Scope, query);
ManagementObjectCollection services = searcher.Get();
if (services.Count > 0)
{
foreach (ManagementObject queryObj in services)
{
StringBuilder s = new StringBuilder();
s.Append(queryObj["Caption"].ToString());
s.Append(",");
s.Append(queryObj["State"].ToString());
s.Append(",");
s.Append(queryObj["ProcessId"].ToString());
s.Append(";");
servicesList.Add(s);
}
}
}
Edit: Added code, thought I was doing right by keeping it simple!
So when I run this.UpdateServicesList(); in the constructor of the ServerConnectionTools class, I need to update the List in the Server class.
Edit2: While typing this I had a brainwave... Return a list of services from the update function and call the update function from the first class. Think this is a better approach?
What is the best approach to doing this?
Apologies for what is probably a simple question...
Cheers
Dave
Edit: Removed code snippet after your edits.
Yes, return a list of services. That's much better than sending in the parent class to the helper class.
You don't appear to be using the server that you send into the helper class. So you can just fetch the services from the update function. I would rename the update function to FetchServices or something instead of update.
public Server(string serverName, string loginUsername, string loginPassword)
{
this.ServerName = serverName;
this.LoginUsername = loginUsername;
this.LoginPassword = loginPassword;
// Login to server and retrieve list of services
ConnectionTools = new ServerConnectionTools(this);
this.Services = ConnectionTools.FetchServiceTools();
}
Use delegates.
Like this:
// Delegate Specification
    public class MyClass
    {
        // Declare a delegate that takes a single string parameter
        // and has no return type.
        public delegate void LogHandler(string message);
        // The use of the delegate is just like calling a function directly,
        // though we need to add a check to see if the delegate is null
        // (that is, not pointing to a function) before calling the function.
        public void Process(LogHandler logHandler)
        {
            if (logHandler != null)
            {
                logHandler("Process() begin");
            }
            if (logHandler != null)
            {
                logHandler ("Process() end");
            }
        }
    }
    // Test Application to use the defined Delegate
    public class TestApplication
    {
        // Static Function: To which is used in the Delegate. To call the Process()
        // function, we need to declare a logging function: Logger() that matches
        // the signature of the delegate.
        static void Logger(string s)
        {
            Console.WriteLine(s);
        }
        static void Main(string[] args)
        {
            MyClass myClass = new MyClass();
            // Crate an instance of the delegate, pointing to the logging function.
            // This delegate will then be passed to the Process() function.
            MyClass.LogHandler myLogger = new MyClass.LogHandler(Logger);
            myClass.Process(myLogger);
        }
    }
In your situation you don't need whole service class in serviceConnectionTools.
here is my suggestion to you
Step 1 add an interface
interface caller
{
void setList(List<StringBuilder> par_list);
}
Step 2 implement the interface
public class Server : caller
{
private string m_serverName;
private string m_loginUsername;
private string m_loginPassword;
private ServerConnectionTools m_connectionTools;
public List<Service> Services { get; private set; }
public Server(string serverName, string loginUsername, string loginPassword)
{
this.ServerName = serverName;
this.LoginUsername = loginUsername;
this.LoginPassword = loginPassword;
// Login to server and retrieve list of services
ConnectionTools = new ServerConnectionTools(this);
}
public void setList(List<StringBuilder> par_list)
{
//do whatever you need to here.
}
}
step 3
public class ServerConnectionTools // Class 2
{
private caller m_serverCB;
private ManagementScope m_scope;
public ServerConnectionTools(caller par_serverCB)
{
m_serverCB = par_serverCB;
this.Scope = InitiateScope();
try
{
this.UpdateServicesList();
}
catch (System.Runtime.InteropServices.COMException)
{
// Server is unavailable
Console.WriteLine("Unable to reach server {0}", server.ServerName);
}
}
/// in your Updateservicelist function
public void UpdateServicesList()
{
// Connect our scope to the actual WMI scope
this.Scope.Connect();
List<StringBuilder> servicesList = new List<StringBuilder>();
// Query system for Services
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_Service WHERE Caption LIKE 'xxx%'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(this.Scope, query);
ManagementObjectCollection services = searcher.Get();
if (services.Count > 0)
{
foreach (ManagementObject queryObj in services)
{
StringBuilder s = new StringBuilder();
s.Append(queryObj["Caption"].ToString());
s.Append(",");
s.Append(queryObj["State"].ToString());
s.Append(",");
s.Append(queryObj["ProcessId"].ToString());
s.Append(";");
servicesList.Add(s);
}
}
m_serverCB.setList(servicesList);//add this to call method.
}
Now you don't have to send all server object to class2. You can use serverConnectionTools for other classed you may need in the future instead of just using only one class.You have better oop implementation now I hope.

Categories