Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic WebService (SOAP) client library for C++

I'm looking for a simple C++ WebService Client Library that can be easily linked into my application.

Preferably this library:

  • can be used to access any SOAP WebService (so I can pass the URL, the WebService name, the WebService method and all the arguments as arguments to a function call)
  • can be linked statically in a C++ application (so no DLL's)
  • is freeware or available at a low cost
  • can be used royalty-free in my application
  • can query the Web service for its WSDL and return me the available method names, arguments of the methods and their data types

Before anyone of you answers .NET: been there, tried it. My major objections against .NET are:

  • you can generate the proxy but it's impossible to change the WebService name in the generated proxy code afterwards, since .NET uses reflection to check the WebService name (see Dynamically call SOAP service from own scripting language for my question regarding that problem)
  • generating the proxy class on the fly doesn't always seem to work correctly

I already used Google to look up this information, but I couldn't find one.

Thanks

EDIT: To clarify this further, I really want something where I can write code like this (or something in this style):

SoapClient mySoapClient;
mySoapClient.setURL("http://someserver/somewebservice");
mySoapClient.setMethod("DoSomething");
mySoapClient.setParameter(1,"Hello");
mySoapClient.setParameter(2,12345);
mySoapClient.sendRequest();
string result;
mySoapClient.getResult(result);

No dynamic code generation.

like image 249
Patrick Avatar asked Nov 26 '10 13:11

Patrick


2 Answers

Have you looked at gSOAP? I think it will be suitable for your needs.

http://gsoap2.sourceforge.net/

like image 179
Starkey Avatar answered Sep 19 '22 11:09

Starkey


I found a solution using on-the-fly-generated assemblies (which I couldn't get working the previous time). Starting point is http://refact.blogspot.com/2007_05_01_archive.html.

E.g. This is the code to use the PeriodicTable web service:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Web;
using System.Web.Services;
using System.Web.Services.Description;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Xml.Serialization;
using System.IO;
using System.Reflection;

namespace GenericSoapClient
{
class Program
    {
    static void method1()
        {
        Uri uri = new Uri("http://www.webservicex.net/periodictable.asmx?WSDL");
        WebRequest webRequest = WebRequest.Create(uri);
        System.IO.Stream requestStream = webRequest.GetResponse().GetResponseStream();

        // Get a WSDL
        ServiceDescription sd = ServiceDescription.Read(requestStream);
        string sdName = sd.Services[0].Name;

        // Initialize a service description servImport
        ServiceDescriptionImporter servImport = new ServiceDescriptionImporter();
        servImport.AddServiceDescription(sd, String.Empty, String.Empty);
        servImport.ProtocolName = "Soap";
        servImport.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties;

        CodeNamespace nameSpace = new CodeNamespace();
        CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
        codeCompileUnit.Namespaces.Add(nameSpace);

        // Set Warnings

        ServiceDescriptionImportWarnings warnings = servImport.Import(nameSpace, codeCompileUnit);

        if (warnings == 0)
            {
            StringWriter stringWriter =
                 new StringWriter(System.Globalization.CultureInfo.CurrentCulture);

            Microsoft.CSharp.CSharpCodeProvider prov =
              new Microsoft.CSharp.CSharpCodeProvider();

            prov.GenerateCodeFromNamespace(nameSpace,
               stringWriter,
               new CodeGeneratorOptions());

            string[] assemblyReferences =
               new string[2] { "System.Web.Services.dll", "System.Xml.dll" };

            CompilerParameters param = new CompilerParameters(assemblyReferences);

            param.GenerateExecutable = false;
            param.GenerateInMemory = true;
            param.TreatWarningsAsErrors = false;

            param.WarningLevel = 4;

            CompilerResults results = new CompilerResults(new TempFileCollection());
            results = prov.CompileAssemblyFromDom(param, codeCompileUnit);
            Assembly assembly = results.CompiledAssembly;
            Type service = assembly.GetType(sdName);

            //MethodInfo[] methodInfo = service.GetMethods();

            List<string> methods = new List<string>();

            // only find methods of this object type (the one we generated)
            // we don't want inherited members (this type inherited from SoapHttpClientProtocol)
            foreach (MethodInfo minfo in service.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
                {
                methods.Add(minfo.Name);
                Console.WriteLine (minfo.Name + " returns " + minfo.ReturnType.ToString());
                ParameterInfo[] parameters = minfo.GetParameters();
                foreach (ParameterInfo pinfo in parameters)
                    {
                        Console.WriteLine("   " + pinfo.Name + " " + pinfo.ParameterType.ToString());
                    }
                }

            // Create instance of created web service client proxy
            object obj = assembly.CreateInstance(sdName);

            Type type = obj.GetType();

            object[] args0 = new object[] { };
            string result0 = (string)type.InvokeMember(methods[0], BindingFlags.InvokeMethod, null, obj, args0);
            Console.WriteLine(result0);

            object[] args1 = new object[] { "Oxygen" };
            string result1 = (string)type.InvokeMember(methods[1], BindingFlags.InvokeMethod, null, obj, args1);
            Console.WriteLine(result1);
            }
        }
    }
}

In this code I explicitly use methods[0] and methods[1] but in reality you would check the method names of course. In this example I get the names of all elements in the periodic table, then get the atomic weight of oxygen.

This example does not yet contain logic to support a proxy. I still need to add this, but for the moment, it solves my biggest problem, namely, having a generic SOAP client.

EDIT:

I know this code is C# and I was originally asking for a C++ solution, but this code proves that it can work in a .NET environment (which I can still use in limited parts of my application), and I will probably rewrite this code into C++/.NET, which solves my problem.

like image 45
Patrick Avatar answered Sep 20 '22 11:09

Patrick