I'm trying to name a server-client messaging application using wcf.
Here's the server part so far.
namespace server
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) /// once the form loads, create and open a new ServiceEndpoint.
{
ServiceHost duplex = new ServiceHost(typeof(ServerWCallbackImpl));
duplex.AddServiceEndpoint(typeof(IServerWithCallback), new NetTcpBinding(), "net.tcp://localhost:9080/DataService");
duplex.Open();
this.Text = "SERVER *on-line*";
}
private void buttonSendMsg_Click(object sender, EventArgs e)
{
Message2Client(textBox2.Text); /// The name 'Message_Server2Client' does not exist in the current context :(
}
class ServerWCallbackImpl : IServerWithCallback /// NEED TO SOMEHOW MERGE THIS ONE WITH THE FORM1 CLASS
{
IDataOutputCallback callback = OperationContext.Current.GetCallbackChannel<IDataOutputCallback>();
public void StartConnection(string name)
{
/// client has connected
}
public void Message_Cleint2Server(string msg)
{
TextBox1.text += msg; /// 'TextBox1' does not exist in the current context :(
}
public void Message2Client(string msg)
{
callback.Message_Server2Client(msg);
}
}
[ServiceContract(Namespace = "rf.services",
CallbackContract = typeof(IDataOutputCallback),
SessionMode = SessionMode.Required)]
public interface IServerWithCallback ///// what comes from the client to the server.
{
[OperationContract(IsOneWay = true)]
void StartConnection(string clientName);
[OperationContract(IsOneWay = true)]
void Message_Cleint2Server(string msg);
}
public interface IDataOutputCallback ///// what goes from the sertver, to the client.
{
[OperationContract(IsOneWay = true)]
void AcceptConnection();
[OperationContract(IsOneWay = true)]
void Message_Server2Client(string msg);
}
}
}
I just can't figure out, how do I merge "class Form1:Form" and "class ServerWCallbackImpl : IServerWithCallback", so that I would be able to induce the Message2Client function from a buttonclick, as well as add TextBox1.text += msg when the *Message_Cleint2Server* happens.
Thanks!
You don't need to merge your class with the form: you need to create an instance of it. Something like this:
IServerWCallback server = new ServerWCallbackImpl();
server.Message2Client("hello world");
However (from the structure of your code so far), you'll probably need to have created an instance of the class earlier. This allows you to connect that instance and keep it around for later operation.
You may also want to read the MSDN pages on classes and objects (instances of classes) to make sure you've understood the concepts fully before you continue - this stuff is pretty fundamental to .NET programming.
What is the Need for merging why can't You Use Inheritance?
Solving Multiple Inheritance in C#.net
Click this for more details (stackoverflow)
i think that will give you the answer
Related
I would like to add a REST API server to my WinForms application. I have chosen to use Grapveine for that purpose.
Here's my code:
namespace RestServerTest
{
public partial class Form1 : Form
{
private RestServer mServer;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
mServer = new RestServer();
mServer.Start();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
mServer.Stop();
mServer.Dispose();
}
}
[RestResource]
class MyRoute
{
[RestRoute]
public IHttpContext HelloWorld(IHttpContext context)
{
// Todo: how access form object from here?
context.Response.SendResponse("Hello, world.");
return context;
}
}
}
Currently I have no idea how to actually access my Form object from the REST route (without using an ugly global/static variable).
How would one do that elegantly?
If you want the current form (or any other object/variable in your project) to be accessible to your routes, you can take advantage of Dynamic Properties. Both the IRestServer and IHttpContext implement IDynamicProperties, which gives you two ways to accomplish your goal.
Add either of these to your Form1_Load() method.
Add a Reference On The Server
server.Properties.Add("CurrentForm", this);
Add a BeforeRouting Event Handler
server.Router.BeforeRouting += cxt =>
{
cxt.Properties.Add("CurrentForm", this);
};
Access a Property In a Route
In either case, you can access the property using the built in extensions methods:
// On the server
var form = context.Server.GetPropertyValueAs<Form1>("CurrentForm");
// On the context
var form = context.GetPropertyValueAs<Form1>("CurrentForm");
Im making a program what connects to multiple 3th party systems. The connect with different formats so i created multiple classes to deal with them. I have now three 4 classes.
The MainForm is the first class. This is the basic windows form class with the user interface.
SDKCommunication is the second class.
VMS (this class handles the events given of by the 2th party system and activates methods on SDK COmmunication)
Events
Events Class
public class Events
{
public event EventHandler LoginStateChanged;
private bool loginstate;
public bool LogInState
{
get { return this.loginstate; }
set
{
this.loginstate = value;
if (this.LoginStateChanged != null)
this.LoginStateChanged(this, new EventArgs());
}
}
}
part of SDKCommunicatie class
Events events = new Events();
public void onLogon(string username, string directory, string system)
{
events.LogInState = false;
}
MainForm Class
SDKCommunicatie sdkcommunicatie = new SDKCommunicatie();
Events events = new Events();
public MainForm()
{
InitializeComponent();
events.LoginStateChanged += new EventHandler(events_LoginStateChanged);
}
void events_LoginStateChanged(object sender, EventArgs e)
{
log.Info("EventFired loginstateChanged");
}
When the LogInState Changes in the SDKCommunicatie class. There needs to be an event fired in the MainForm class. But sadly that doesn't work.
But when I change the loginstate in the mainform(with a buttonclick)(see code below) the event is fired. But that is not the intention i would like to have.
private void button1_Click(object sender, EventArgs e)
{
events.LogInState = true;
}
If my question isn't clear enough, please let me know.
VMS class Added as reply to #Astef
class VMS {
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(MainForm));
GxUIProxyVB m_UIProxy = new GxUIProxyVB();
public string username2;
public string directory2;
public string Status;
public void initOmni()
{
m_UIProxy.CreateInstance();
m_UIProxy.OnLogon += new _IGxUIProxyVBEvents_OnLogonEventHandler(m_UIProxy_OnLogon);
m_UIProxy.OnLogoff += new _IGxUIProxyVBEvents_OnLogoffEventHandler(m_UIProxy_OnLogoff);
m_UIProxy.OnError += new _IGxUIProxyVBEvents_OnErrorEventHandler(m_UIProxy_OnError);
m_UIProxy.OnAlarmStatusEx2 += new _IGxUIProxyVBEvents_OnAlarmStatusEx2EventHandler(m_UIProxy_OnAlarmStatusEx2);
}
public void login(string username, string password, string directory)
{
username2 = username;
directory2 = directory;
initOmni();
m_UIProxy.LogOn(directory, username, password,false);
}
public void logOff()
{
m_UIProxy.LogOff();
}
void m_UIProxy_OnLogon()
{
SDKCommunicatie sdkcommunicatie = new SDKCommunicatie();
sdkcommunicatie.onLogon(username2, directory2, "Genetec Omnicast");
}
I have fixed this with deleting the following:
SDKCommunicatie sdkcommunicatie = new SDKCommunicatie();
And adding the following in the base of VMS:
SDKCommunicatie sdkcommunicatie;
But now i got a new error in the mainform when i tried to call a class in SDKCommunicatie
connectedStatus = sdkcommunicatie.connectedStatus();
I got the following error:
NullReferenceException was unhandled
You are not using the same instance of the Events class, and that's why on button click you catch LoginStateChanged. You should inject the same instance of Events class to SDKCommunicatie class, then you'll be able to listen to event changes.
Edit:
Jeremy Todd and I were both writing at the same time.
Events in your SDKCommunicatie are not fired because you've created an individual instance of class Events for it. That is not the instance you have placed on the MainForm.
Inject the right instance (pass a reference) to SDKCommunicatie from MainForm through constructor, property or somehow else. For example:
MainForm:
SDKCommunicatie sdkcommunicatie;
Events events = new Events();
public MainForm()
{
InitializeComponent();
events.LoginStateChanged += new EventHandler(events_LoginStateChanged);
sdkcommunicatie = new SDKCommunicatie(events);
}
void events_LoginStateChanged(object sender, EventArgs e)
{
log.Info("EventFired loginstateChanged");
}
SDKCommunicatie:
Events events;
public SDKCommunicatie(Envents eventsInstance)
{
events = eventsInstance;
}
public void onLogon(string username, string directory, string system)
{
events.LogInState = false;
}
Your SDKCommunication class and your MainForm class each have their own separate instance of Events, so any events you trigger from one won't be visible from the other -- they're being raised on an entirely different object.
What you need is a single instance of the Events class that both SDKCommunication and MainForm can share -- that way they'll both be seeing the same thing. There are several different approaches you could take for this. Depending on what it needs to do, one very simple possibility might be to make Events a static class, and then the events would be visible everywhere without needing to create any instances.
I have solved the riddle.
When i need a method is a class i can call the method directly like this:
public class MainForm : Form
{
SDKCommunication sdkcommunication = new SDKCommunication();
public MainForm()
{
}
private void Button1_Click(oject sender, EventArgs e)
{
sdkcommunication.method("Test")
}
}
This is pretty straightforward. Look here the receiverclass:
public class SDKCommunication
{
method(string word)
{
//do something with word
}
}
The biggest problem is calling the class with the form(the original class). I have solved this with a eventhandler.
class CustomEventHandler1 : EventArgs
{
public CustomEventHandler1(string u, string d)
{
msgu = u;
msgd = d;
}
private string msgu;
private string msgd;
public string Username
{
get { return msgu; }
}
public string Directory
{
get { return msgd; }
}
}
Then the SDKCOmmunication class should look like this:
class SDKCommunication
{
public event EventHandler<CustomEventHandler1> RaiseCustomEventHandler1;
protected virtual void OnRaiseCustomEventHandler1(CustomEventHandler1 e)
{
EventHandler<CustomEventHandler1> handler = RaiseCustomEventHandler1;
if (handler != null)
{
handler(this,e);
}
}
//Custom Method that is called somewhere
internal void custommethod()
{
OnRaiseCustomEventHandler1(new CustomEventHandler1("johnsmith", "localhost");
}
}
Then in the mainform class:
public class MainForm : Form
{
public MainForm()
{
sdkcommunication.RaiseCustomEventHandler1 += new EventHandler<CustomEventHandler1>(sdkcommunication_RaiseCustomEventHandler1);
}
void sdkcommunication_RaiseCustomEventHandler1(object sender, CustomEventHandler1 e)
{
//Do something.
}
}
The information sended with the event you can get with e.Username and e.Directory. In this example they are strings where e.Username = johnsmith and e.Directory = localhost.
I hope somebody can use this information for their own code.
Class:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerSession)]
public class MainService : IChat
{
IChatCallback ChatCallback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
Chat chat = new Chat(this);
public void ShowChat()
{
chat.Show();
}
public void SendInstantMessage(string user, string message)
{
chat.RaiseMsgEvents(user, message);
ChatCallback.InstantMessage(user, message);
}
}
Form:
public partial class Chat : Form
{
MainService service;
public Chat(MainService service)
{
InitializeComponent();
OnMsgReceivedEvent += new OnMsgReceived(callback_OnMsgReceivedEvent);
this.service = service;
}
private void btnSend_Click(object sender, EventArgs e)
{
service.SendInstantMessage("Admin", txtMsg.Text);
}
}
The mainForm use the class like this:
public partial class Form1 : Form
{
ServiceHost host;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
host = new ServiceHost(typeof(WCF_Server.MainService));
host.Open();
}
}
In the main form, i just pass the class, no initializing, but in the class when ShowChat() called i need to show the chat form and get to this class method so i can send messages.
.NET is an object oriented language. In fact, every class is an object.
The error you are getting is because you're instantiating an object with "this" on the global level.
UPDATE
Based on your update you could do the following and it will work. You might want to refactor this some more to ensure that it's not going to break any business rules etc.
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerSession)]
public class MainService : IChat
{
IChatCallback ChatCallback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
//Changed this to be just a declaration. This will be null,
// as there is no object yet, this is really just a pointer to nothing.
//This tells the system that you might/are planning to use an object called
//chat, but it doesn't exist yet.
Chat chat;
// Get your default constructor going. This will create the actual chat object, allowing the rest of your class to access it.
public MainService()
{
//instantiate it! (or as some of our co-ops say "We're newing it")
chat = new Chat(this);
}
//now that chat is actually instantiated/created you can use it.
public void ShowChat()
{
chat.Show();
}
public void SendInstantMessage(string user, string message)
{
chat.RaiseMsgEvents(user, message);
ChatCallback.InstantMessage(user, message);
}
}
This is just a personal pet peeve, but having a function parameter the same name as a global variable is... well for me a no no. I noticed this on your Chat.Chat(MainService) function.
Of course it is, just create a method that takes this class of yours as a parameter and call it...
As other posts have suggested, you'll want to re-consider how you instantiate your chat field within your example class. I would consider lazy loading the property, like so...
private ChatForm _Chat = null;
private ChatForm Chat
{
get
{
if (this._Chat == null)
{
this._Chat = new ChatForm(this);
}
return this._Chat;
}
set { this._Chat = value; }
}
Using lazy-loading will ensure you're able to use the keyword this upon request.
I am trying to modify a TextBox that belongs to Form2 from within a WCF object.
namespace server2
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
}
private ServiceHost duplex;
private void Form2_Load(object sender, EventArgs e) /// once the form loads, create and open a new ServiceEndpoint.
{
duplex = new ServiceHost(typeof(ServerClass));
duplex.AddServiceEndpoint(typeof(IfaceClient2Server), new NetTcpBinding(), "net.tcp://localhost:9080/service");
duplex.Open();
this.Text = "SERVER *on-line*";
}
}
class ServerClass : IfaceClient2Server
{
IfaceServer2Client callback;
public ServerClass()
{
callback = OperationContext.Current.GetCallbackChannel<IfaceServer2Client>();
}
public void StartConnection(string name)
{
var myForm = Form.ActiveForm as Form2;
myForm.textBox1.Text = "Hello world!"; /// <- this one trows “System.NullReferenceException was unhandled”
/// unless Form2 is selected when this fires.
callback.Message_Server2Client("Welcome, " + name );
}
public void Message_Cleint2Server(string msg)
{
}
public void Message2Client(string msg)
{
}
}
[ServiceContract(Namespace = "server", CallbackContract = typeof(IfaceServer2Client), SessionMode = SessionMode.Required)]
public interface IfaceClient2Server ///// what comes from the client to the server.
{
[OperationContract(IsOneWay = true)]
void StartConnection(string clientName);
[OperationContract(IsOneWay = true)]
void Message_Cleint2Server(string msg);
}
public interface IfaceServer2Client ///// what goes from the sertver, to the client.
{
[OperationContract(IsOneWay = true)]
void AcceptConnection();
[OperationContract(IsOneWay = true)]
void RejectConnection();
[OperationContract(IsOneWay = true)]
void Message_Server2Client(string msg);
}
}
Yet the "myForm.textBox1.Text = "Hello world!";" line throws System.NullReferenceException was unhandled"...
Any ideas, thanks!
myForm might not be of the Form2 type, or it might not contain a textBox1 field. Make sure to check for null for both those cases.
As discussed in the comments of the initial question, the problem is you're referring to ActiveForm, when the form you want is not active. Whenever attempting to cast using the as keyword, the result will be null if the cast is invalid. Since you grabbed a form that could not be cast to Form2 (because it was a different kind of form), you correctly received a null reference exception.
Assuming you have enforced singleton rules on Form2 and you haven't played with the form's name, you can access it by way of the Application.OpenForms collection like so:
(Form2)Application.OpenForms["Form2"];
In your code sample that could look like this:
public void StartConnection(string name)
{
//var myForm = Form.ActiveForm as Form2;
var myForm = Application.OpenForms["Form2"] as Form2;
myForm.textBox1.Text = "Hello world!"; /// <- this one trows “System.NullReferenceException was unhandled”
/// unless Form2 is selected when this fires.
callback.Message_Server2Client("Welcome, " + name );
}
That said, I don't think I'd give responsibility of modifying form controls to a WCF service. I'd much sooner consume events fired by the service inside my form.
Still struggling with the server part of my wcf application.
The problem is that the creating a "callback" object within the server class cased a "System.NullReferenceException was unhandled" error.
If I understand right, it happens when I create this server object - ServerClass myServer = new ServerClass();
So I guess I should somehow create a list for server objects and create & add this objects automatically whenever a client makes a connection. Please suggest, what would be the best way of doing that?
Here's my code so far:
namespace server2
{
public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
myServer.eventHappened += new EventHandler(eventFunction);
}
ServerClass myServer = new ServerClass();
// here I instance a new server object.
// yet I believe that I need to make a list, that would store this objects, so that multiple clients would be able to connect,
// and I would be able to pick, to whom I want to send a callback.
void eventFunction(object sender, EventArgs e)
{
label1.Text = myServer.clientName;
}
private ServiceHost duplex;
private void Form2_Load(object sender, EventArgs e) /// once the form loads, create and open a new ServiceEndpoint.
{
duplex = new ServiceHost(typeof(ServerClass));
duplex.AddServiceEndpoint(typeof(IfaceClient2Server), new NetTcpBinding(), "net.tcp://localhost:9080/service");
duplex.Open();
this.Text = "SERVER *on-line*";
}
}
class ServerClass : IfaceClient2Server
{
public event EventHandler eventHappened;
IfaceServer2Client callback = OperationContext.Current.GetCallbackChannel<IfaceServer2Client>();
// ERROR: System.NullReferenceException was unhandled
public string clientName = "noname";
public void StartConnection(string name)
{
clientName = name;
MessageBox.Show(clientName + " has connected!");
eventHappened(this, new EventArgs());
// ERROR: System.NullReferenceException was unhandled :(
callback.Message_Server2Client("Welcome, " + clientName);
}
public void Message_Cleint2Server(string msg)
{
}
public void Message2Client(string msg)
{
}
}
[ServiceContract(Namespace = "server", CallbackContract = typeof(IfaceServer2Client), SessionMode = SessionMode.Required)]
public interface IfaceClient2Server ///// what comes from the client to the server.
{
[OperationContract(IsOneWay = true)]
void StartConnection(string clientName);
[OperationContract(IsOneWay = true)]
void Message_Cleint2Server(string msg);
}
public interface IfaceServer2Client ///// what goes from the sertver, to the client.
{
[OperationContract(IsOneWay = true)]
void AcceptConnection();
[OperationContract(IsOneWay = true)]
void RejectConnection();
[OperationContract(IsOneWay = true)]
void Message_Server2Client(string msg);
}
}
Thanks!
This can not be done that way. First of all, the Callback Channel is only available within an operation, not when the instance of the service class is created.
Secondly, the instances of your ServerClass are created by the WCF service host depending on your WCF configuration. For example, there may be one instance per call (!). So creating an instance yourself and attaching an event handler does not affect the automatically created instances. That's why you get an exception in StartConnection.
What I'd do in that case is:
Create a singleton class that publishes the desired event
Attach a handler to the event from within your main code. This will be the code that listens to events
Create a public method (like ConnectionStarted) which raises the event
Call this method from your ServerClass
If you don't need to wait for the event handler to finish, you can also raise the event asynchronously in a separate thread. You then have to make sure that the event handler attached in step 2) handles thread contexts properly using for example this.Invoke (Forms) or this.Dispatcher.BeginInvoke (WPF).
Try putting that line in the class constructor:
class ServerClass : IfaceClient2Server
{
public event EventHandler eventHappened;
IfaceServer2Client callback;
public ServerClass ()
{
callback = OperationContext.Current.GetCallbackChannel<IfaceServer2Client>();
}
...
}
If still no luck it probably means you can use OperationContext.Current only inside some operation.