This is my C# program:
class Program
{
static void Main(string[] args)
{
CallVbsFunction(1); //Work
CallVbsFunction(1.2); //Work
CallVbsFunction('a'); //Work
CallVbsFunction("a"); //!!Exception see bellow
}
private static void CallVbsFunction(object p)
{
var sc = new MSScriptControl.ScriptControl();
sc.Language = "VBScript";
sc.AllowUI = true;
try
{
sc.AddCode(System.IO.File.ReadAllText("script.vbs"));
sc.AddObject("myguid", p, false);
var parameters = new object[] { "a" };
sc.Run("test", ref parameters);
}
catch (Exception e)
{
Console.Out.WriteLine(e.ToString());
}
}
}
My VBScript file contents:
Function Test(a)
MsgBox myguid
End Function
And Finally this is my exception when I use AddObject() with string object:
System.Runtime.InteropServices.COMException (0x800A0005): Invalid
procedure call or argument at
MSScriptControl.IScriptControl.Run(String ProcedureName, Object[]&
Parameters) at Srcipting.Program.CallVbsFunction(Object p) in
Program.cs
You need to use a wrapper object that is ComVisible:
[ComVisible(true)]
public class StringWrapper
{
private string wrappedString;
public StringWrapper(string value)
{
wrappedString = value;
}
public override string ToString()
{
return wrappedString;
}
}
CallVbsFunction(new StringWrapper("a"));
The problem is that the .net String object looks like a native vb string to the MSScriptControl on the first look but not on the second look.
You only need to use this wrapper when you register a string directly or register a function that returns a string. There is no problem when registering an object that has properties of type string. There is also no problem for the parameters you pass to Run() because these will be correctly marshaled to native vb strings by the .net runtime.
So the maybe best option is to not provide individual strings to your script but an object that encapsulates all the different values you want it to use.
Define this class
[ComVisible(true)]
public class HostOptions
{
public string OptionA { get; set; }
public string OptionB { get; set; }
}
Then construct the object and set all the properties and register it with the script control
var hostOptions = new HostOptions();
hostOptions.OptionA = "AAA";
hostOptions.OptionB = "BBB";
sc.AddObject("HostOptions", hostOptions, false);
You can then use it in your script like this:
Function Test(a)
MsgBox HostOptions.OptionA
MsgBox HostOptions.OptionB
End Function
Related
I'm trying to create a "wrapper" around a dynamic object so I can keep method names on dynamic object matching the names of the methods in the wrapping class.
What I need to do is provide the helper a dynamic object, and the name of the method i want to call on it (via [CallerMemberName]), and the args for the call. I can't figure out how to invoke the call on the dynamic object. How can I do this?
class Program
{
static void Main(string[] args)
{
var dyn = new ClassWithDynamicProperty();
dyn.SendMessage("test");
Console.ReadKey();
}
}
public class ExampleDynamicClass
{
public void SendMessage(string msg)
{
Console.WriteLine(msg);
}
}
public class ClassWithDynamicProperty
{
public ClassWithDynamicProperty()
{
MyDynObject = new ExampleDynamicClass();
}
public dynamic MyDynObject { get; set; }
public void SendMessage(string theMessage)
{
//i want to replace this:
MyDynObject.SendMessage(theMessage);
//with this:
DynamicHelper.CallDynamic(MyDynObject, new object[] { theMessage });
}
}
public static class DynamicHelper
{
public static void CallDynamic(dynamic source, object[] args, [CallerMemberName]string methodName = null)
{
//source.methodName(args); How can i invoke this?
}
}
Turns out it's not that hard after all. I didn't know if normal reflection would work with dynamic types. All resources I found for dynamic objects involved overriding TryInvokeMember, which wasn't an option. Here's missing code:
var method = ((object)dynamicObject).GetType().GetMethod(methodName);
method.Invoke(dynamicObject, args);
I am making an IRC Chat bot for my stream. I found a few basic connectivity examples using C# so I decided to give it a try.
So far I love it
But i am stuck on this one part.
I want to store the bot commands inside an array of a structure type.
public delegate void cmdHandler(string[]);
struct botCommand
{
string name;
cmdHandler chandler;
bool isAdmin = false;
string help = "Nothing here.";
}
Is currently what I have, and then I want to beable to do this:
botCommand[]commands =
{
{ "TestCommand", testCommand(), 0, "Help for this" },
{ "TestCommand2", testCommand2(), 0 "..." },
......
};
So how do I link a generic function in that array?
or am I going about this all the wrong way?
Basically instead of having a giant Switch() statement to check for which command was used I want to loop through an array and see if the command is in there. If it is then call the function associated with that command.
EDIT:
This is exactly what I have now so you can see what I am trying to do
public delegate void cmdHandler(string[] ex);
struct botCommand
{
string name;
cmdHandler chandler;
bool isAdmin = false;
string help = "Nothing here.";
}
botCommand[] commands =
{
{"test", new cmdHandler(testf), 0, "" }
};
public void testf(string[] ex) {
return;
}
Steps of logic:
user enters the test command
Loop through all botCommands to see if we find the test command
Test command is found
Call the function associated with the test command and pass on an argument (the rest of the command)
To me it seems like you're mixing C/C++ concepts with C# (using struct instead of class, 0 for false, object initializers, etc...).
To solve your individual problem, you must instantiate your struct differently.
botCommand[] commands = new []
{
new botCommand {
name = "Test",
chandler = new cmdHandler(MyMethod),
isAdmin = false,
help = "No help for you..."
}
};
where MyMethod is defined as.
public static void MyMethod(string[] myArgs)
{
//... do something ...
}
However, I think a better approach would be to have an abstract class / interface for an individual command, list or dictionary of your commands.
public interface IBotCommand
{
string Name { get; }
bool IsAdmin { get; }
void Process(string[] args);
string HelpText { get; }
}
public class MyCommand : IBotCommand
{
string Name
{
get
{
return "NameOfTheCommand";
}
}
bool IsAdmin
{
get
{
return false;
}
}
void Process(string[] args)
{
// bla bla, im processing stuff
}
string HelpText
{
get
{
return "This is the help text";
}
}
}
And then using it.
List<IBotCommand> commands = new List<IBotCommand>();
commands.Add(new MyCommand());
// to find a command and execute it
IBotCommand cmdToExecute = commands.SingleOrDefault(c => c.Name == "NameOfTheCommand");
if (cmdToExecute != null)
{
cmdToExecute.Process(args); // where-ever args comes from
}
else
{
// unknown command "NameOfTheCommand"
}
I have a line of code that says
string sendQueueName = ESResource.Service.TOPICA.Src();
The way the class is laid out is as follows:-
public static class ESResource{
static ESResource() {
//initialization code
}
//other declarations and definitions
public enum Service { TOPICA,TOPICB,TOPICC };
public static string Path(this Service sn) { return FromKey(sn, "PATH"); }
public static string Src(this Service sn) { return FromKey(sn, "SRC"); }
}
I have a functionality that requires me to be able to pass in a string env
like this
string sendQueueName = ESResource.Service.TOPICA.Src(env);
But when i try to modify my Src function to support the above function call like this :
public static string Src(this Service sn, string env=""){ return FromKey(sn, "PATH", env); }
It says it cannot find a function with one parameter. What is the best way to pass in env which is an optional parameter?
I am a C++ programmer, now working on a C# project.
I am trying to understand in the below snippet why the value of string does not change even though the function is changing its value, I thought it is an object and would be passed as a reference.
public class TestClass
{
public TestClass(String passedStr)
{
passedStr = "Change me";
}
}
class Program
{
static void Main(string[] args)
{
String aString="I am what i am";
TestClass obj = new TestClass(aString);
Console.WriteLine(aString);
}
}
But behavior with user defined classes are different.
public class TestClass
{
private int x;
public int ID
{
get
{
return x;
}
set
{
x = value;
}
}
public TestClass(int a)
{
x = a;
}
}
public class Tester
{
public Tester(TestClass obj)
{
obj.ID = 999;
}
}
class Program
{
static void Main(string[] args)
{
TestClass obj = new TestClass(555);
Tester tester = new Tester(obj);
Console.WriteLine(obj.ID);
}
}
Let's go back to basics.
A variable is a storage location.
A variable of type string is a storage location that stores either null, or a reference to a string.
"passedStr" and "aString" are different variables.
When you call "new TestClass(aString)" you create a new storage location for "passedStr" and copy the contents of "aString" into it. You now have two variables that have the same content: a reference to a string.
Inside the constructor you change the value stored in the storage location for "passedStr". "aString" remains the same.
The "ref" and "out" keywords in C# mean "make the formal parameter and the argument aliases of each other". In that case you have only one variable with two different names. When you change one of them the other one changes as well, because they are the same variable.
No, it's passed by value; there's no ref keyword.
It's passing a reference type (here, a class) by value (no ref keyword), just like passing a copy of a pointer in C++. You're reassigning the pointer, not the actual data (which you can't do with strings anyway).
If you need pass-by-reference, try:
public TestClass(ref String passedStr)
{
passedStr = "Change me";
}
...
TestClass obj = new TestClass(ref aString);
Strings are passed by reference but the pointer is passed by value in C#. If you want to pass the string by reference you'll have to make use of the ref key word.
For example:
public class TestClass
{
public TestClass(ref string passedStr)
{
passedStr = "Change me";
}
}
class Program
{
static void Main(string[] args)
{
string aString="I am what i am";
TestClass obj = new TestClass(ref aString);
Console.WriteLine(aString); // "Change me"
}
}
passedStr is not the string, but a constructor parameter that holds a reference to the string. All your TestClass constructor is doing is changing the string that this parameter references. It has no effect outside the constructor.
What you are doing in the constructor, is that you are assigning a new String literal to the local variable passedStr.
In C, the equivalent function would look something like this:
Testclass constructor_testclass(char* passedStr) {
passedStr = "Change me";
}
I think it's obvious that this function does not change the value of the char* in the calling function.
The object I am what i am is being passed by reference, but the reassignment is to a local variable. You don't change the original object, but instead assign a new object (Change me) to the location of the argument.
public class Employee
{
public string ID { get; set; }
public string Name { get; set; }
public override string ToString()
{
return string.Format("ID = {0} Name = {1}", ID, Name);
}
}
public class TestClass2
{
public TestClass2(Employee e)
{
e.ID="007";
e.Name="james";
}
}
static void Main()
{
Employee e = new Employee();
e.ID = "0";
e.Name = "nobody";
TestClass2 t = new TestClass2(e);
Console.WriteLine(e); //Output ID = 007 Name = James
}
strings are passed by reference but the pointer is passed by value in C#
Parameter passing in C# by Jon Skeet
i want to make a component that they can use like this [following]
they can define directly
myControl control = new myControl("information","info","some other info");
myControl control = new myControl();
the both need to be valid;
they can make operation if they already set the value like
control.completemytask(); // it's need to working if they declare information otherwise thrown a error
they can make operation even if they not declare the value by declare the value when complete the operation
like
control.completetask("information","info","some other info");
how i can do this in c#.
are anyone show me the code then i can know this easily.
public class myControl
{
public myControl()
{
}
public myControl(string arg1, string arg2, string arg3)
{
Arg1 = arg1;
Arg2 = arg2;
Arg3 = arg3;
}
public void completemytask() {
if(String.IsNullOrEmpty(Arg1) ||
String.IsNullOrEmpty(Arg2) ||
String.IsNullOrEmpty(Arg3))
throw new ArgumentException("Not all arguments are specified.");
else
completetask(Arg1, Arg2, Arg3);
}
public void completetask(arg1, arg2, arg3)
{
// do what you want
}
public string Arg1 { get; set; }
public string Arg2 { get; set; }
public string Arg3 { get; set; }
}
myControl control = new myControl();
Above is the constructor of your class.
control.completemytask();
and
control.completetask("information","info","some other info");
are simply function overloading...
Read up a bit on OOP in C# :)
something like this should work.
public class MyControl
{
public MyControl(params string[] info)
{
this.Info = info;
}
public string[] Info { get; set; }
}
This can be achieved easily with constructor overloading
create two constructors with parameters..
public MyControl(string firstParameter, string secondParameter, string thirdParameter)
{
// store the parameters.. and initialise a bool flag to say the've been initialised.
...
_parametersInitilised = true;
}
Alternatively if you have multiple arguments yoiu'll need to use a Param array like this...
private string[] _myInfo;
public MyControl(params string[] info)
{
_myInfo = info;
}
This will give you an array of strings to deal with.
And for the secnd constructor.. create one with no parameters
public MyControls()
{
_parametresIntialised = false;
}
and then same thing for the 'CompleteMyTask' with Method Overloading..
public void CompleteMyTask(string firstParameter, string secondParameter, string thirdParameter)
{
// process with these arguments..
...
}
and another overload with no arguments..
public void CompleteMyTask()
{
if(!_paremtersInitialised)
throw new InvalidOperationException("Call to CompleteMyTask before initialiseation");
//call the overload with the supplied parameters...
CompleteMyTask(_firstParam, _secondParam, _thirdParam);
}