CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 15 of 24

Hybrid View

  1. #1
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Lightbulb Sharing objects between application instances using WCF

    [This is the sequel to Sharing objects between application instances (using .NET Remoting)]

    Well, it took me some time to get back to this - there's a lot to read about WCF. And as I think this one will be mainly about WCF from now on, I've started a new thread about it.

    After having read lots of stuff about WCF and getting an idea of what it can all do and a vague idea of how I make it doing that, I decided to first translate the "greetings" sample from A Developer's Introduction to Windows Communication Foundation 4 from C# to C++/CLI to use it as an easy toy for initial experiments:

    Code:
    // WcfTest1Server.cpp: Hauptprojektdatei.
    
    // Translated from C# sample code at http://msdn.microsoft.com/en-us/library/ee354381.aspx
    
    #include "stdafx.h"
    
    #using "System.ServiceModel.dll"
    
    using namespace System;
    using namespace System::ServiceModel;
    using namespace System::ServiceModel::Description;
    
    [ServiceContract]
    public interface class IHello
    {
      [OperationContract]
      void SayHello(String ^name);
    };
    
    [ServiceContract]
    public interface class IGoodbye
    {
      [OperationContract]
      void SayGoodbye(String ^name);
    };
    
    public ref class GreetingService : public IHello, public IGoodbye  // Service implements both contracts
    {
    public:
      virtual void SayHello(String ^name)
      {
        Console::WriteLine("Hello {0}", name);
      }
    
      virtual void SayGoodbye(String ^name)
      {
        Console::WriteLine("Goodbye {0}", name);
      }
    };
    
    int main(array<System::String ^> ^args)
    {
      // Host is configured with two base addresses, one for HTTP and one for TCP
      ServiceHost ^host = gcnew ServiceHost(GreetingService::typeid,
        gcnew Uri("http://localhost:8080/greeting"),
        gcnew Uri("net.tcp://localhost:8081/greeting"));
      host->Open();
      for each (ServiceEndpoint ^se in host->Description->Endpoints)
        Console::WriteLine("A: {0}, B: {1}, C: {2}", se->Address, se->Binding->Name, se->Contract->Name);
      Console::WriteLine("Press <Enter> to stop the service");
      Console::ReadLine();
      host->Close();
      return 0;
    }
    That was easy so far.

    Difficulties started, however, when I tried to write a client for it, which I assumed not to be much more challenging either. And then it already took me some time to figure out that svcutil only accepts my server .exe as input if I compile it at least with the option /clr:pure instead of the mere /clr that gets set up by default by the IDE for a console app (unlike a Windows Forms app, BTW). (That does imply I can't use native code in a WPF service, doesn't it?)

    Then, after I had the .h file generated by svcutil, I tried to compile my minimalistic client with that header. (I don't post the client code itself for now since I'm sure it's irrelevant: The compiler already stalls while processing the .h file.) I get a bunch of C3766 errors, telling me I'd better implement the add/remove accessors of the five events inherited from ICommunicationObject, like void System::ServiceModel::ICommunicationObject::Closed::add(System::EventHandler ^). I get these errors for the two client classes HelloClient and GoodbyeClient which derive from ClientBase<TChannel> with the respective interface as the type parameter.

    However, according to MSDN, already ClientBase<TChannel> does implement them, yet these implementations are private. So what?

    I renamed the header file from its original name schemas.microsoft.com.2003.10.Serialization.h, which I found too long and somehow looked to me like svcutil would name all these files like that anyway, to WcfTest1Client.h, but I don't think that can have such consequences, can it?

    I'm confident I'll eventually figure this out, but there seems to be a considerable learning curve ahead. So, any help is appreciated.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  2. #2
    Join Date
    Jul 2002
    Posts
    2,543

    Re: Sharing objects between application instances using WCF

    Though WCF stuff can be implemented in C++/CLI, I think it is much better to make it in C# and connect to your existing application using class library. You can save a lot of efforts by using C#.
    BTW, according to my experience, WCF is one of W*F series that really works and has a good performance.

  3. #3
    Join Date
    Jan 2010
    Posts
    1,133

    Re: Sharing objects between application instances using WCF

    Quote Originally Posted by Eri523 View Post
    That does imply I can't use native code in a WPF service, doesn't it?
    Not sure... What if you isolate the native code, and create a .NET-only wrapper around it?

    Quote Originally Posted by Eri523 View Post
    I get a bunch of C3766 errors, telling me I'd better implement the add/remove accessors of the five events inherited from ICommunicationObject, like void System::ServiceModel::ICommunicationObject::Closed::add(System::EventHandler ^). I get these errors for the two client classes HelloClient and GoodbyeClient which derive from ClientBase<TChannel> with the respective interface as the type parameter.

    However, according to MSDN, already ClientBase<TChannel> does implement them, yet these implementations are private. So what?
    Those are events (on the MSDN page), not getters/setters - I'm a little rusty on C++/CLI equivalent of C# properties, but that's most likely what it wants. I know C++ .NET had some clumsy syntax for properties, and I think they changed it to something more intuitive in C++/CLI. Basically, the service contract is defined via the attributes (ServiceContract, OperationContract, DataContract, DataMember). The DataMember attribute is, in C# WCF model, applied to properties (which are syntactic sugar for get/set methods; nevertheless, note that functions are marked with the OperationContract attribute, but properties are a part of the data contract and have the DataMember attribute).

    As for svcutil, i don't think it's really required: it's purpose is (among other things I guess) to take a service, obtain the WSDL - which specifies the service contract, and use it to create a skeleton for a (potentially 3rd-party) client app.
    Something like this:

    But, since you created the service, you know the contract, so, I think that in effect you can skip that and create a test client manually. On the other hand, it would be nice to make it all work as it's supposed to... Because some other developer should be using the WSDL to create a client on his end.

    Anyway, I hope that makes sense, and I wish I could be of more help.

  4. #4
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Sharing objects between application instances using WCF

    Quote Originally Posted by Alex F View Post
    Though WCF stuff can be implemented in C++/CLI, I think it is much better to make it in C# [...]. You can save a lot of efforts by using C#.
    Mostly out of curiosity (you know, I'm somewhat neophobic about languages... ), what would I gain by making that transition? I suspect that workflow-style way of creating services, which sounds like one could click them together without writing a single line of code, isn't supported by C# Express anyway.

    [...] and connect to your existing application using class library.
    You mean I would use (or provide) a client-side DLL that exposes my services interface (or perhaps any application-specific adaption of it I want) and encapsulate interaction with the actual service? If yes, I think that DLL would, in most cases, just be an ultra-thin wrapper around the .h file generated by svcutil. As you'll see below, I got my test client, employing that .h file, to work in the meantime. The client .cpp file actually is just 26 code lines, and essentially there's nothing else in the client app besides that and the mentioned .h file. (I must admit, however, that of course this particular client is quite primitive, as is the service.)

    BTW, according to my experience, WCF is one of W*F series that really works and has a good performance.
    Good news, glad to hear!

    Quote Originally Posted by TheGreatCthulhu View Post
    Not sure... What if you isolate the native code, and create a .NET-only wrapper around it?
    Actually an idea I alredy thought about myself. It would certainly not be enough to isolate it in a separate module. As it's the assembly (i.e. .exe file) svcutil operates on and complains about, it'd need to be isolated in a separate DLL. So bye-bye to the monolithic one-.exe app I like so much. But then again, if the app is accompanied by its own server it's not that monolithic anymore anyway...

    At any rate, I probably won't need any native code in the project this is eventually meant to be used in at the moment. So these considerations are rather hypothetic...

    Those are events (on the MSDN page), not getters/setters[.]
    Actually, I was already concerned about using the term "accessors" in this context when I was writing the post (I somehow anticipated a reaction like yours from anyone... ) but I couldn't think of a better one.

    I'm a little rusty on C++/CLI equivalent of C# properties, but that's most likely what it wants. I know C++ .NET had some clumsy syntax for properties, and I think they changed it to something more intuitive in C++/CLI.
    add() is mapped to operator+=, remove() to operator-= and Invoke() to operator(). Not too clumsy I'd say. (And they are no propertes... )

    Basically, the service contract is defined via the attributes (ServiceContract, OperationContract, DataContract, DataMember). The DataMember attribute is, in C# WCF model, applied to properties (which are syntactic sugar for get/set methods; nevertheless, note that functions are marked with the OperationContract attribute, but properties are a part of the data contract and have the DataMember attribute).
    Though I wouldn't have sworn on it in my last post, it turned out that neiter the service contract nor the operation contract was the problem. As to the data contract: There's none involved here (or is there an implicit one regarding the String parameter?). And though it's encouraged all the way to use properties in .NET, I still mostly prefer explicit method calls over properties when an operation beyond a simple data transfer is involved, which obviously is the case when communicating with a service. And if it is a plain data transfer, I'll likely use plain public data members (perhaps due to the lacking notion of trivial properties in C++/CLI).

    As for svcutil, i don't think it's really required: it's purpose is (among other things I guess) to take a service, obtain the WSDL - which specifies the service contract, and use it to create a skeleton for a (potentially 3rd-party) client app.

    [...]

    But, since you created the service, you know the contract, so, I think that in effect you can skip that and create a test client manually.
    Actually, my main reason to use svcutil here was to create a starting point, since I didn't yet come across any relevant samples of how to create a client.

    On the other hand, it would be nice to make it all work as it's supposed to... Because some other developer should be using the WSDL to create a client on his end.
    Actually, I do have a WSDL that was produced as an intermediate step. I created the WSDL from the .exe and then the .h from the WSDL, both steps using svcutil. As I understood the svcutil documentation, there's no direct way from an .exe to a .h. However, I only had a brief look into the WSDL since I knew I wouldn't need to work with it directly.

    Anyway, I hope that makes sense, and I wish I could be of more help.
    Oh, it certainly did. And of course, as we all know, it's usually more productive to discuss things rather than exchanging ready-to-use solutions.

    So here's the client I have at this stage. Actually, it's just the same one I had as of my last post. It was only the .h file (and the app-config file, but I'd find that out later) that caused problems.

    Code:
    // WcfTest1.cpp: Hauptprojektdatei.
    
    #include "stdafx.h"
    
    #include "WcfTest1Client.h"
    
    using namespace System;
    
    int main(array<System::String ^> ^args)
    {
      String ^strName;
      Console::Write("Please enter your name: ");
      strName = Console::ReadLine();
      HelloClient ^helloclient = gcnew HelloClient;
      helloclient->SayHello(strName);
      helloclient->Close();
      GoodbyeClient ^goodbyeclient = gcnew GoodbyeClient;
      goodbyeclient->SayGoodbye(strName);
      goodbyeclient->Close();
    #ifdef _DEBUG
      Console::WriteLine("Hit <Enter> to continue...");
      Console::ReadLine();
    #endif
      return 0;
    }
    I mentioned in my last post that I had problems with the compiler demanding implementation of these five events. The solution I found for that feels pretty hackish but at least it works for now: I simply added dummy events to the client classes. Here's, exemplarily, HelloClient:

    Code:
    [System::Diagnostics::DebuggerStepThroughAttribute, 
    System::CodeDom::Compiler::GeneratedCodeAttribute(L"System.ServiceModel", L"4.0.0.0")]
    public ref class HelloClient : public System::ServiceModel::ClientBase<IHello^ >, public IHello
    {
    public:
      HelloClient();
      HelloClient(System::String^  endpointConfigurationName);
      HelloClient(System::String^  endpointConfigurationName, System::String^  remoteAddress);
      HelloClient(System::String^  endpointConfigurationName, System::ServiceModel::EndpointAddress^  remoteAddress);
      HelloClient(System::ServiceModel::Channels::Binding^  binding, System::ServiceModel::EndpointAddress^  remoteAddress);
      virtual System::Void SayHello(System::String^  name) sealed;
    
      // It insists on having implementations of the events, so I give it five dummies
    
    public:
      virtual event EventHandler ^Closed;
      virtual event EventHandler ^Closing;
      virtual event EventHandler ^Faulted;
      virtual event EventHandler ^Opened;
      virtual event EventHandler ^Opening;
    };
    Adding the events and a bit of reformatting to the two client classes was all I changed about the .h file. I just hope I didn't cut off something important in the private event implementations inside ClientBase<TChannel> with my dummy events.

    I'm uncertain about what the IHelloChannel and IGoodbyeChannel declared in the .h file are for. I couldn't find any references to them in the entire project code, and they're empty anyway except for the two interfaces they inherit. Could they be referenced implicitly from somewhere?

    Also important, of course was the app.config file created by svcutil, stored under the default name output.config. I could leave it uncanged except for adding the address attributes to the two endpoint nodes:

    Code:
            <client>
                <endpoint binding="basicHttpBinding" bindingConfiguration="DefaultBinding_IHello"
                    contract="IHello" name="DefaultBinding_IHello_IHello" address="http://localhost:8080/greeting" />
                <endpoint binding="basicHttpBinding" bindingConfiguration="DefaultBinding_IGoodbye"
                    contract="IGoodbye" name="DefaultBinding_IGoodbye_IGoodbye" address="http://localhost:8080/greeting" />
            </client>
    I have attached a ZIP file containing the client files (.cpp, .h and app.config) as well as the original .h and app.config files generated by svcutil for reference.

    One thing I don't really understand yet is tempuri.org. Right from the start I regarded it a mere placeholder for a real address (which got confirmed by the three-line Wikipedia article), so I replaced the four instances of it in the .h file with localhost:8080, but it turned out that the placeholder is referenced by default at some points inside the server which made connection attempts fail. I could make it work by simply reverting back to tempuri.org in the .h file, but of course I need to find out how to correctly set up the server instead.

    Next step I'll take will probably be implementing the data sharing scenario from the Remoting thread with WCF.
    Attached Files Attached Files
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  5. #5
    Join Date
    Jul 2002
    Posts
    2,543

    Re: Sharing objects between application instances using WCF

    If you need interprocess communication on the same computer, the best way is to use pipe and not HTTP. I don't remember details, take a look at NetNamedPipeBinding class. The change is transparent and affects only application configuration file.

  6. #6
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Sharing objects between application instances using WCF

    Thanks. That confirms thoughts I already had. For the first experiment I just wanted to connect to the given server, but I'll certainly look into NetNamedPipeBinding and aim to implement the sharing test scenario based on that.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  7. #7
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Sharing objects between application instances using WCF

    Ok, as an intermediate step I decided to first modify the greeting sample to use the netNamedPipeBinding which turned out to be easier than expected. The only thing I needed to change about the C++/CLI code was the statement in the server that instantiates the service host:

    Code:
      ServiceHost ^host = gcnew ServiceHost(GreetingService::typeid,
        gcnew Uri("net.pipe://localhost/greeting"));
    The rest could actually be done by modifying the app.config files. I removed anything or at least most of which isn't really needed and now have WCF use default values. This is the server's app.config (it didn't have one before):

    Code:
    <configuration>
      <system.serviceModel>
        <services>
          <service name="TestServer.GreetingService">
            <endpoint address="net.pipe://localhost/greeting"
                      binding="netNamedPipeBinding"
                      contract="IHello" />
            <endpoint address="net.pipe://localhost/greeting"
                      binding="netNamedPipeBinding"
                      contract="IGoodbye" />
          </service>
        </services>
      </system.serviceModel>
    </configuration>
    And this is the client's one which is considerably shorter now:

    Code:
    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <system.serviceModel>
            <client>
                <endpoint binding="netNamedPipeBinding"
                    contract="IHello" name="DefaultBinding_IHello_IHello" address="net.pipe://localhost/greeting" />
                <endpoint binding="netNamedPipeBinding"
                    contract="IGoodbye" name="DefaultBinding_IGoodbye_IGoodbye" address="net.pipe://localhost/greeting" />
            </client>
        </system.serviceModel>
    </configuration>
    However, now I'm facing some uncertainties about how to implement the test scenario form the Remoting thread using WCF. The central question is: How would I return a live object (the TestServer::Server instance in the Remoting model code) using WCF? I found it was pretty straightforward using Remoting by deriving the class in question from MarsharByRefObject but I didn't yet come across anything equivalent in WCF. Does WCF have something like that at all, and if yes, where?

    In the meantime I'm considering an alternative approach that simply avoids returning a live object to the client: Drop the factory class and make the server class a singleton by specifying [ServiceBehavior(InstanceContextMode=InstanceContextMode::Single)] (what AFAIK isn't possible at all when using Remoting which led to the introduction of the factory class in the first place). The main role of the original Server class is now assumed by a new class internal to the server of which the new singular exposed server instance is able to manage multiple instances. (Remember the original scenario this is meant to be used for in the real-life app.) The clients now simply submit requests to send mails by calling a server method, the rest happens invisibly inside the server app.

    This alterative approach feels a bit less OO to me, but perhaps it's more client/server style and certainly it provides a higher level of encapsulation. Any comments?
    Last edited by Eri523; September 22nd, 2011 at 02:38 PM.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  8. #8
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Sharing objects between application instances using WCF

    Eri, check out my CG Tray Notify article series.

    It uses a WCF service hosted from a Windows Service application to communicate with a Tray Icon app running by different logged on users.

    It also uses two-way binding to send events to the tray icon clients.

  9. #9
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Sharing objects between application instances using WCF

    Arjay, thanks for pointing me to those articles. They were quite instructive, in particular part III of course. And your design with the singular exposed server object holding a number of Folder instances resembles the design I plan to use in my real-life app to a surprising extent.

    Some things pointed out in your articles clarified things I've read on MSDN, while others made me question some things I thought I had understood about WCF. In particular this one: Does setting the InstanceContextMode of my service contract implementation to Single really extend its lifetime to the entire time the hosting ServiceHost remains open? IOW is the lifetime of an instance of the service class not subject to be limited by any of the WCF standard timeouts under these circumstances? In that case I'd need to implement my own "self destruction mechanism" since I want my service instance and in turn the server app instance to auto-shutdown a certain time after the last client instance has detached. And I'm currently uncertain how to do that.

    I see, however, two differenes between our two scenarios that don't let it look reasonable to make my server a Windows Service: My service instance is neither meant to be shared between distinct users, nor to outlive the last client instance by a really significant timespan.

    And, mainly out of curiosity: Are all the project and class templates you use in the articles, in particular the one for a Windows Service, actually supported by the C# Express Edition as well?

    Right now I'm working on the WCF implementation of my test scenario and I think it'll be ready for posting about tomorrow. And it'll probably be a bit more refined than the one from the Remoting thread, i.e. more inclined towards the real-life scenario.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured