Download MFC Calculator Client source files - 28 KbDownload Demo ATL object and Web Service MFC client - 57 Kb
This is true - the day after I had completed converting ROPE.dll to work for my Win98 users, the Microsoft
Soap Toolkit 2.0 arrives! Not only does the new toolkit work on more flavours of Windows but it has all changed.
So OK, now I can get down to the real work of providing my users with great new Web Services using a single
DLL set, but hang on where's the CPP samples??? Yet again, a new technology blighted by VB only samples! I
decided as my first step in getting to grips with the new toolkit was to convert the samples to MFC, and here is
the first. An MFC implementation using the high level Soap Client API. The first set of sample code uses the
simple parameter types Calculator Service included with the Soap Toolkit. The second is a simple ATL object,
client and ASP listener, to test passing strings around.
Before starting you should follow the instructions in the SOAP Toolkit samples.htm file. First, you will
need to create a virtual directory on your web server. Then, you will need to create the ClcVBSrv DLL. The VB
project can be found in the samples/ClcVBSrv folder under your SOAP Toolkit installation directory. After
building the DLL, you will need to recreate the Web Services Description Language (WSDL) file and Web Services
Meta Language (WSML) file, using wsdlgen.exe, to include the location of your web server. The ASP listener
for this service does not need to be changed.
The project is a straight forward MFC dialog application. I have created a very simple interface similar to
the one in the VB client.
Including the SoapClient in the project
The easiest way to include the SoapClient in an MFC application is by using COleDispatchDriver class.
Basically the COleDispatchDriver class allows you to implement the client side of COM, providing access to
an object's methods and properties.
To add the SoapClient into the project, go to the Class Wizard. Click the 'Add Class' button and choose
'From a Type Library'. Then, locate MSSOAP1.dll. You will then be given a list of interfaces, contained within
the DLL, to choose from. For the purposes of this simple project, I only needed the ISOAPClient interface. This
adds two files to the project mssoap1.cpp and mssoap1.h.
To use the SoapClient interface add a variable of type ISOAPClient, e.g.
ISOAPClient * m_pClient
Creating the ISOAPClient pointer
As I have used a pointer for my SoapClient interface variable, two steps are required before it can actually
be used. First, create a new pointer of type ISOAPClient. This creates a C++ wrapper for the object. Then
create an IDispatch object which is automatically attached to our wrapper.
m_pClient = new ISOAPClient;
The CreateDispatch function handles the creation of the IDispatch pointer for us. We just need to supply the
ProgId (in this case MSSOAP.SoapClient) of the object we want to use.
Note: You can create a version dependant instantiation by including the version (e.g. "MSSOAP.SoapClient.1")
This is something I have never found a need for!
After creating the SoapClient, initialise it using the mssoapinit function passing the name of the server,
port and WSML file.
m_pClient->mssoapinit("http://www.MyService.com/SoapSamples/CalcVB.wsdl", "CalcVB", "CalcVBPortType", NULL);
Calling the Web Services functions
So we have got our SoapClient ready to use, but how do we call the services functions. In this case, simply
will cause a compiler error. The application needs to know something about the functions.
To use the Web Services functions, a function header and body needs to be declared. Using the Add function
as an example the header and body look like this
double Add(double dblA, double dblB, DISPID dispid);
double ISOAPClient::Add(double dblA, double dblB, DISPID dispid)
static BYTE parms =
InvokeHelper(dispid, DISPATCH_METHOD, VT_R8, (void*)&result, parms, dblA, dblB);
The function itself takes as parameters two doubles and returns a double. I have added the dispid parameter
to the function to pass the dispatch id of the function I am calling. The function body uses InvokeHelper to call
the Add function. The InvokeHelper function is fairly easy to use. Full details on using this function can be found
in the MSDN documentation.
I have added these functions to the mssoap cpp and header files, although you could declare them elsewhere
and pass in the SoapClient pointer/object.
Note: If you do add additional functions to the wizard created files, care must be taken when adding further
interfaces from the COM object, as the additional functions maybe deleted.
This only leaves getting the dispatch id of the function to call.
In actual fact, a quick and dirty method for calling the functions is to use the internal index of the OLE controller.
This will generally index the functions in the order that they are numbered. Indexing them from -1 upwards. This will
probably work nine times out of ten. However, it is not a rule of COM that the controller must do this and so the
result cannot be guaranteed.
So, I have created a function to get the dispid that I need.
DISPID CDemo1Dlg::GetDispid(CString strFxName)
OLECHAR * name = strFxName.AllocSysString();
m_pClient->m_lpDispatch->GetIDsOfNames(IID_NULL, &name, 1, GetUserDefaultLCID(), &dispid);
This function takes the name of the Web Service function we want to call and retrieves its dispatch id.
So OK, there's a whole lot more code here than in the VB sample. And no, the high level API approach to SOAP
messaging is not the most flexible. However, if you just want to get going, using your language of choice, and
to start including SOAP in your MFC projects then this is a relatively painless way of doing it.