Encontré una solución usando ensamblajes generados sobre la marcha (que no pude obtener trabajando la vez anterior). El punto de inicio es http://refact.blogspot.com/2007_05_01_archive.html.
E.g. Este es el código para utilizar el servicio web PeriodicTable:
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);
}
}
}
}
En este código que utilizo de manera explícita methods[0]
y methods[1]
pero en realidad se comprobaría los nombres de los métodos de golf. En este ejemplo, obtengo los nombres de todos los elementos en la tabla periódica y luego obtengo el peso atómico del oxígeno.
Este ejemplo aún no contiene lógica para admitir un proxy. Todavía necesito agregar esto, pero por el momento, resuelve mi mayor problema, es decir, tener un cliente SOAP genérico.
EDIT:
Sé que este código es C# y yo estaba originalmente pidiendo un C++ solución, pero este código demuestra que puede funcionar en un entorno .NET (que todavía puedo usar en partes limitadas de mi aplicación), y probablemente reescribo este código en C++/.NET, que resuelve mi problema.
¡Es un buen punto! ¿Crees que se puede portar al entorno de C++ Builder? ¿O depende de .NET y Reflection? – bluish
@bluish, esto es realmente dependiente de .Net. El código usa .Net para generar la clase de cliente, y luego usa el compilador .Net C# para compilarlo, y usa la reflexión .Net para llamar a la clase. – Patrick
Aunque estoy contento de que el problema del OP esté resuelto, creo que esta respuesta simplemente no puede ser la respuesta aceptada de esta pregunta, ya que está en C# mientras la pregunta es específicamente sobre C++ ... – zgyarmati