Wrapping MFC Objects For Use With .NET Remoting

Welcome to this week's installment of .NET Tips & Techniques! Each week, award-winning Architect and Lead Programmer Tom Archer from the Archer Consulting Group demonstrates how to perform a practical .NET programming task using either C# or Managed C++ Extensions. This article assumes a basic understanding of combining MFC and Managed Extensions as well as .NET remoting.

Combining the powers of MFC and .NET (via Managed Extensions) enables you to leverage the much more robust UI capabilities of MFC while taking advantage of the many features available in .NET that are not in MFC. One example of the latter is Remoting—the .NET equivalent of DCOM (except much more functional and easier to use). However, if you do attempt to use Remoting in an MFC application, you'll quickly run into the limitation that you cannot remote MFC objects across application boundaries. To illustrate this, consider the following example:

namespace RemotingMFCEx
{
  public __gc class StringArrayWrapper : public MarshalByRefObject
  {
  public:
    StringArrayWrapper(CStringArray* books) { m_books = books; }
    CStringArray* m_books;
  };

  public __gc class BookList : public MarshalByRefObject
  {
  private:
    CStringArray* m_books;
  public:
    BookList()
    {
      m_books = new CStringArray();
      m_books->Add(_T("Inside C#, Second Edition"));
      m_books->Add(_T("Extending MFC Applications with the .NET Framework"));
      m_books->Add(_T("Visual C++ .NET Bible"));
      m_books->Add(_T("The Microsoft Outlook 2003 Answer Book"));
    }
    ~BookList() { delete m_books; }
  public:
    CStringArray* GetList() { return m_books; }
    StringArrayWrapper* GetList2() { return new StringArrayWrapper(m_books); }
  };
}

Looking at the second—BookList—class, you can see that it exposes two public methods:

  • GetList—returns a pointer to a CStringArray object (an MFC type)
  • GetList2—returns a pointer to a StringArrayWrapper object (a managed type)

Now, assume that you had the following server code to register the BookList object:

TcpChannel* channel = new TcpChannel(9988);

ChannelServices::RegisterChannel(channel);

RemotingConfiguration::RegisterWellKnownServiceType(
  Type::GetType(S"RemotingMFCEx.BookList,RemotingMFCEx"),
                S"MFCRemotingDemo",
                WellKnownObjectMode::SingleCall);

Console::WriteLine(S"Press any key to stop server");
getch();

Finally, here's some example client code to access the BookList object:

TcpChannel* channel = new TcpChannel();
ChannelServices::RegisterChannel(channel);

#undef GetObject

BookList* RemoteObject = static_cast<BookList*>
  (Activator::GetObject(Type::GetType(S"RemotingMFCEx.BookList,RemotingMFCEx"),
                                      S"tcp://jeanvaljean:9988/MFCRemotingDemo"));

//CStringArray* sa = RemoteObject->GetList(); 
StringArrayWrapper* sw = RemoteObject->GetList2();
//CString s = sw->m_books->GetAt(0);

Console::WriteLine(S"Press any key to quit");
getch();

If you were to uncomment the call to GetList2, a System.NotSupportedException exception would be thrown due to the fact that remoting a CStringArray* (or any MFC type) across a remoting boundary is not supported. However, if you were to run the code as is, you'd find that the call to GetList2 works—or at least appears to work.

The problem is that the successful call to the GetList method would have you believe that you have successfully remoted a managed object that wraps a CStringArray pointer. However, what you really have is a reference object (StringArrayWrapper). When you attempt to access its CStringArray pointer member (m_books), the remoting framework would then attempt to transfer the requested object across the application domain boundaries, which would result in an exception of type System.Runtime.Serialization.SerializationException. You can see this by uncommenting the GetAt method call on the StringArrayWrapper::m_books member.

As of this writing, the only way to circumvent this limitation is to write a wrapper class that not only wraps the MFC object you wish to remote, but also provides access to each member function of the MFC object that will be accessed from the remote client. Using the same example, suppose that the remote client needs to access the CStringArray::GetAt member function. A simple wrapper for that would look like the following:

public __gc class StringArrayWrapper : public MarshalByRefObject
{
public:
  StringArrayWrapper(CStringArray* books) { m_books = books; }
  String* GetAt(int index) { return m_books->GetAt(index); }
private:
  CStringArray* m_books;
};

As you can see, the process of wrapping the MFC object and its needed member functions is a matter of making an internal call to the object's member function and returning a type that is convertible to the corresponding native type. In this example, I'm returning a String type that will be implicitly converted to a CString object on the client side.

The client code would now look like the following:

#undef GetObject

BookList* RemoteObject = static_cast<BookList*>
  (Activator::GetObject(Type::GetType(S"RemotingMFCEx.BookList,RemotingMFCEx"),
                        S"tcp://jeanvaljean:9988/MFCRemotingDemo"));	

StringArrayWrapper* sw = RemoteObject->GetList2();
CString str = sw->GetAt(1);
_tprintf(_T("%s\r\n"), str);

The MFC object exists only on the server. As a result, any invocation of its member functions will execute on the server and not on the client side. That might seem obvious, but it's very easy to sometimes forget what is getting executed where—especially if you're also remoting managed types, which are executing on the client side.



About the Author

Tom Archer - MSFT

I am a Program Manager and Content Strategist for the Microsoft MSDN Online team managing the Windows Vista and Visual C++ developer centers. Before being employed at Microsoft, I was awarded MVP status for the Visual C++ product. A 20+ year veteran of programming with various languages - C++, C, Assembler, RPG III/400, PL/I, etc. - I've also written many technical books (Inside C#, Extending MFC Applications with the .NET Framework, Visual C++.NET Bible, etc.) and 100+ online articles.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: December 18, 2014 The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this webcast and join industry experts as …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds