I need to get unmanaged Windows C++ clients to talk to a WCF service. C++ clients could be running on Win2000 and later. I have a control over both WCF service and which C++ API is being used. Since it's for a proprietary application, it is preferable to use Microsoft stuff where possible, definitely not GNU licensed APIs. Those of you who have it working, can you share a step-by-step process how to make it working?
I have researched following options so far:
Any more ideas? Please answer only if you actually have it working yourself.
Edit1: I apologize for anyone who I might have confused: what I was looking for was a way to call WCF service from client(s) where no .NET framework is installed, so using .NET-based helper library is not an option, it must be pure unmanaged C++
The basic idea is to write the WCF code for your clients in C# (it's just easier this way) and use a C++ bridge dll to bridge the gap between your unmanaged C++ code and the managed WCF code written in C#.
Here is the step-by-step process using Visual Studio 2008 along with .NET 3.5 SP1.
The first thing to do is create the WCF Service and a means to host it. If you already have this, skip to Step 7 below. Otherwise, create a Windows NT Service following the steps from here. Use the default names offered by VS2008 for the project and any classes that are added to the project. This Windows NT Service will host the WCF Service.
Add a WCF Service named HelloService to the project. To do this, right-click the project in the Solution Explorer window and select the Add|New Item... menu item. In the Add New Item dialog, select the C# WCF Service template and click the Add button. This adds the HelloService to the project in the form of an interface file (IHelloService.cs), a class file (HelloService.cs), and a default service configuration file (app.config).
Define the HelloService like this:
``
[ServiceContract]
public interface IHelloService
{
[OperationContract]
string SayHello(string name);
}
public class HelloService : IHelloService
{
public string SayHello(string name)
{
return String.Format("Hello, {0}!", name);
}
}
Modify the Service1 class created in Step 1 above to look like this:
using System.ServiceModel;
using System.ServiceProcess;
public partial class Service1 : ServiceBase
{
private ServiceHost _host;
public Service1()
{
InitializeComponent();
}
protected override void OnStart( string [] args )
{
_host = new ServiceHost( typeof( HelloService ) );
_host.Open();
}
protected override void OnStop()
{
try {
if ( _host.State != CommunicationState.Closed ) {
_host.Close();
}
} catch {
}
}
}
Build the project.
Open the Visual Studio 2008 command prompt. Navigate to the output directory for the project. Type the following: `installutil WindowsService1.exe' This installs the Windows NT Service on your local machine. Open the Services control panel and start the Service1 service. It is important to do this in order for Step 9 below to work.
From the File menu, select the Add|New Project... menu item. Select the C# Class Library template. Change the name to HelloServiceClient and click the OK button. Right-click the project in the Solution Explorer and select the Properties menu option. In the Build tab, change the output path to ..\bin\Debug so the assembly and app.config file will be in the same directory as the MFC application. This library will contain the service reference, i.e., the WCF proxy class, to the WCF Hello Service hosted in the Windows NT Service.
In the Solution Explorer, right-click the References folder for the HelloServiceClient project and select the Add Service Reference... menu option. In the Address field, type the address of Hello Service. This should be equal to the base address in the app.config file created in Step 2 above. Click the Go button. The Hello Service should show up in the Services list. Click the OK button to automatically generate the proxy class(es) for the Hello Service. NOTE: I seem to always run into compilation problems with the Reference.cs file generated by this process. I don't know if I'm doing it wrong or if there is a bug, but the easiest way to fix this is modify the Reference.cs file directly. The problem is usually a namespacing issue and can be fixed with minimal effort. Just be aware that this is a possibility. For this example, I've changed the HelloServiceClient.ServiceReference1 to simply HelloService (along with any other required changes).
To allow the MFC Application to interact with the WCF service, we need to build a managed C++ "bridge" DLL. From the File menu, select the Add|New Project... menu item. Select the C++ Win32 Project template. Change the name to HelloServiceClientBridge and click the OK button. For the Application Settings, change the Application Type to DLL and check the Empty project checkbox. Click the Finish button.
The first thing to do is modify the project properties. Right-click the project in the Solution Explorer and select the Properties menu option. Under the General settings, change the Output Directory to ..\bin\Debug and change the Common Language Runtime Support option to Common Language Runtime Support (/clr). Under the Framework and References settings, add a reference to the .NET System, System.ServiceModel, and mscorlib assemblies. Click the OK button.
Add the following files to the HelloServiceClientBridge project - HelloServiceClientBridge.h, IHelloServiceClientBridge.h, and HelloServiceClientBridge.cpp.
Modify the IHelloServiceClientBridge.h to look like this:
#ifndef __IHelloServiceClientBridge_h__
#define __IHelloServiceClientBridge_h__
#include <string>
#ifdef HELLOSERVICECLIENTBRIDGE_EXPORTS
#define DLLAPI __declspec(dllexport)
#else
#define DLLAPI __declspec(dllimport)
#pragma comment (lib, "HelloServiceClientBridge.lib") // if importing, link also
#endif
class DLLAPI IHelloServiceClientBridge
{
public:
static std::string SayHello(char const *name);
};
#endif // __IHelloServiceClientBridge_h__
Modify the HelloServiceClientBridge.h to look like this:
#ifndef __HelloServiceClientBridge_h__
#define __HelloServiceClientBridge_h__
#include <vcclr.h>
#include "IHelloServiceClientBridge.h"
#ifdef _DEBUG
#using<..\HelloServiceClient\bin\Debug\HelloServiceClient.dll>
#else
#using<..\HelloServiceClient\bin\Release\HelloServiceClient.dll>
#endif
class DLLAPI HelloServiceClientBridge : IHelloServiceClientBridge
{ };
#endif // __HelloServiceClientBridge_h__
The syntax for the .cpp file uses managed C++, which takes some getting used to. Modify the HelloServiceClientBridge.cpp to look like this:
#include "HelloServiceClientBridge.h"
using namespace System;
using namespace System::Runtime::InteropServices;
using namespace System::ServiceModel;
using namespace System::ServiceModel::Channels;
std::string IHelloServiceClientBridge::SayHello(char const *name)
{
std::string rv;
gcroot<Binding^> binding = gcnew WSHttpBinding();
gcroot<EndpointAddress^> address = gcnew EndpointAddress(gcnew String("http://localhost:8731/Design_Time_Addresses/WindowsService1/HelloService/"));
gcroot<HelloService::HelloServiceClient^> client = gcnew HelloService::HelloServiceClient(binding, address);
try {
// call to WCF Hello Service
String^ message = client->SayHello(gcnew String(name));
client->Close();
// marshal from managed string back to unmanaged string
IntPtr ptr = Marshal::StringToHGlobalAnsi(message);
rv = std::string(reinterpret_cast<char *>(static_cast<void *>(ptr)));
Marshal::FreeHGlobal(ptr);
} catch (Exception ^) {
client->Abort();
}
return rv;
}
The only thing left to do is update the MFC application to invoke the SayHello() WCF service call. On the MFC form, double-click the Say Hello! button to generate the ButtonClicked event handler. Make the event handler look like this:
#include "IHelloServiceClientBridge.h"
#include <string>
void CMFCApplicationDlg::OnBnClickedButton1()
{
try {
std::string message = IHelloServiceClientBridge::SayHello("Your Name Here");
AfxMessageBox(CString(message.c_str()));
} catch (...) {
}
}
Run the application and click the Say Hello! button. This will cause the application to invoke the SayHello() method of the WCF Hello Service hosted in the Windows NT Service (which should still be running, by the way). The return value is then displayed in a message box.
Hopefully you can extrapolate from this simple example to fit your needs. If this does not work, please let me know so I can fix the post.