Calling webservice from vbscript > option? - c#

for a project we need to call some webservices from vbscript. This is doable, but it requires building the entire XML fragment by hand. I'm spoiled by .NET so I want to do it better.
There is the option of building a COM component and calling that from vbscript, but unfortunately that also requires registering the COM component on the client, an option we don't have.
Another thing I thought of is building a console application which handles the webservice calls and exceptions and things. Problem here is that I need to return a string to the vbcode, where a consoleapp can only return an exit code and nothing more (as far as I know). Since it's a shared scenario, I can't write to a textfile either since that will cause problems in multiuser scenario's.
So I wondered if there are any options I missed and should consider? Or in the other case: what option would you guys pick and why?
Edit: worth mentioning; the webservice is mine and so is the server on which it runs. It will be secured with SSL and authorization to make sure no one other than our clients can use it. I set it up as a WCF service, but it's to be constructed so I can still change that (either the service type or the bindingtype).

The answer would really depend on your exact scenario. For example if you have control over the web service you could expose a netTcpBinding endpoint and then consume it directly from vbscript.
Here's a nice article which shows how to achieve this step by step. Basically your client code might look like that:
Dim addr
addr = "service:mexAddress=""net.tcp://localhost:7891/Test/WcfService1/Service1/Mex"","
addr = addr + "address=""net.tcp://localhost:7891/Test/WcfService1/Service1/"","
addr = addr + "contract=""IWcfService1"", contractNamespace=""http://tempuri.org/"","
addr = addr + "binding=""NetTcpBinding_IWcfService1"", bindingNamespace=""http://tempuri.org/"""
Dim service1
Set service1 = GetObject(addr)
wscript.echo service1.GetData(123)
The WCF service could either be hosted in WAS in IIS 7.0+ which allows to use netTcpBinding or be self hosted as a Windows Service for example.
Another possibility if you have control over the service is to expose it as a REST which is easier to consume. You could opt for JSON and XML which might still require parsing on the client but at least it will make things cleaner.
And if you are tied to SOAP then the recommended approach is to use the MSXML2.ServerXMLHTTP.6.0 as shown in this answer and build the envelopes manually.
The MSSOAP.soapClient control that shipped with Windows XP is now obsolete even if it allowed to achieve exactly what you were looking for with a SOAP service.

Related

How to invoke WCF method programmatically from windows service

I have successfully compiled and run Windows Service with WCF. With installutil, the Windows Service is successfully getting installed and started. I think I am at the end of my development and just need to invoke/call the method DoJobs() inside WCF. I don't need any user interaction and so I don't have any Windows forms or anything. I just want to invoke/call my WCF function programmatically just after serviceHost.Open();
The base address in app.config file is
http://localhost:8733/Design_Time_Addresses/WcfServiceLibrary1/Service1/
I am deploying my WCF from Windows service with the following code.
// Create a ServiceHost for the CalculatorService type and provide the base address.
serviceHost = new ServiceHost(typeof(WcfServiceLibrary1.Service1));
// Open the ServiceHostBase to create listeners and start listening for messages.
serviceHost.Open();
I have also added the service reference and created the below proxy, but not sure of its use.
WcfServiceLibrary1.WCFServiceRef.Service1Client
I have searched tutorials, the examples show how to invoke the WCF function on button_click event of any form after running Windows service. I just want to do that programmatically on start-up of Windows Service.
EDIT: The code inside my DoJobs() fetches the active tab url of firefox with DDE Client, which throws exception when done only in a Windows Service project but runs successfully when done in WCF project. Please see this for reference.
So I made a C#.Net solution with WCF called from a Windows Service and then I called DoJobs() inside Windows Service as shown below.
WcfServiceLibrary1.WCFServiceRef.Service1Client wcfObj = null;
...
protected override void OnStart(string[] args)
{
if (serviceHost != null)
{
serviceHost.Close();
}
serviceHost = new ServiceHost(typeof(WcfServiceLibrary1.Service1));
serviceHost.Open();
if (wcfObj == null)
{
wcfObj = new WcfServiceLibrary1.WCFServiceRef.Service1Client();
wcfObj.DoJobs();
}
}
But, it makes the call happen at the windows service layer, and is throwing the same DdeClient exceptions.
Can the base address url help any way to programmatically invoke DoJobs() in Web-Service? OR there are some other solutions.
Any help is highly appreciated.
Thanks.
This is my aggregated answer from my various comments I made to your post and to Noctis's answer (specifically that we did not know you were using DDE in the OP):
You can't use Dynamic Data Exchange (DDE) in a Windows Service because the latter does not have a message pump. Also DDE requires a Window handle to be passed as a parameter to DDE functions. You can use DDE in programs which do have a message pump does as a WinForms app. See this article for more information
Once your GUI app is running you can either minimize it to a Sys Tray icon or hide the app completely so the user is unaware. Regardless of its visible nature you should have no problem utilising DDE since it will have a message pump.
Now it may be the case you could add a message pump to a Windows Service but I wouldn't recommend it because it falls into the category of because you can do a thing, does not mean you should do a thing. A topic for another time. It's very similar to a recent SO question about how to display a WinForm in a console app - by default you can't and if you managed to you end up with an odd hybrid with much re-inventing of wheels. Not to mention its an ugly hack.
In summary, my best advice is to proceed with a GUI app.
Assuming you have :
// I'm assuming this is your proxy?
var proxy = WcfServiceLibrary1.WCFServiceRef.Service1Client;
// All you need to do is :
proxy.DoJobs() ;
Having seen your update and Micky`s answers, I'm just wondering why you're using DDE. Not sure what your requirements look like, but you can always use your MSMQ to send messages and queue things.

Invoke a method in a Console application that is currently running.

I have a console application I wrote in C# that polls multiple devices, collects some data, and stores the information on a database. The application runs on our web server, and I was wondering how to invoke a method call from the command console (so I can exec a command from php that will be read by the console application, a shell command would work as well).
Anyone got any ideas? I've been floating around 'the google' and have found nothing that will supply my current needs.
Also, i'm not adverse to making changes to the console application if an overhaul is needed there. Please, if your answer is COM Interop, provide a GOOD example of how I would build and call this from PHP / Apache2.
You could create a Service like this:
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(
Method = "GET",
UriTemplate = "/magic")]
void MagicMethod();
}
And a service implementation like this:
public class Service : IService
{
public void MagicMethod()
{
//magic here
}
}
to start a HTTP Service it should look like this:
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri("http://127.0.0.1:8080"))
ServiceEndpoint ep = host.AddServiceEndpoint(typeof(IService), new WebHttpBinding(), "");
ServiceDebugBehavior stp = host.Description.Behaviors.Find<ServiceDebugBehavior>();
stp.HttpHelpPageEnabled = false;
host.Open();
This will start a HTTP server on port 8080.
Then you can make a HTTP Get request to 'http://localhost:8080/magic' to invoke the method call.
Perhaps your console app can poll a directory for a certain file, and react to that.
Then your php app will only need to create that file and the console app should notice it and do whatever you want. I'm not really sure what you want to do.
I would look at using WCF. Your C# application would host a WCF service and then your PHP application could call into it, I believe PHP5 comes with a SOAP library which should make this relatively simple. Any other application you write will be able to easily call in to, especially if they're written in .NET.
I imagine COM would work fine, but I like the scalability of WCF, as if you have to end up moving these applications onto separate servers then you wouldn't need to change anything besides a URL.
There's a good example on this blog. If you're using PHP5 it should be a doddle, if you're having to use 4 then it will still be possible but will require just a bit more legwork.

Function call on server by multiple clients: Isolate each client calls

My project was standalone application then I decided to split it as client & server because I need powerful CPU usage and portability at the same time. Now multiple clients can connect to one server.
It was easy when 1 by 1 processing did the job. Now I need to call the same function & scope area again & again at the same time -via client requests-
Please can anyone give me some clue how should I handle these operations, I need to know how can I isolate clients' processes from each other at the server side? My communication is asynchronous, server receives a request and starts a new thread. I think I pass a parameter which one carries the client information, and another parameter as job id -to help client back, client may ask for multiple jobs and some jobs finish quicker than others-
Should I instantiate the class Process on each call? Can I use a static method, etc, any explanation will be of great help!
Below is the part of my code to need modification
class static readonly Data
{
public variable listOfValues[]
}
class Process
{
local variable bestValue
function findBestValue(from, to)
{
...
if(processResult > bestValue) bestValue = processResult
...
}
...
for(i=0;i<10;i++) startThread(findBestValue(i*1000,i*1000+999));
...
}
EDIT: I think I have to instantiate a
new Process class and call the
function for each client and ignore
the same client for same job since job is already running.
Not getting into your application design, since you didn't talk much about it, I think that your problem is ideal for using WCF WebServices. You get client isolation by design because every request will start in it's own thread. You can create WCF host as standalone application/windows service.
You can wrap your communication with WCF service and configure it to be PerCall service (meaning each request will be processed separately from others).
So you'll clean up your buisness logic from syncronization stuff. That's the best way, because managing and creating threads is not difficult to implement, but it is difficult to implement correctly and optimized for resources consumption.

Can a WebServiceHost be changed to avoid the use of HttpListener?

I am looking for a way to use a WCF WebServiceHost without having to rely on the HttpListener class and it's associated permission problems (see this question for details).
I'm working on a application which communicates locally with another (third-party) application via their REST API.
At the moment we are using WCF as an embedded HTTP server. We create a WebServiceHost as follows:
String hostPath = "http://localhost:" + portNo;
WebServiceHost host = new WebServiceHost(typeof(IntegrationService), new Uri(hostPath));
// create a webhttpbinding for rest/pox and enable cookie support for session management
WebHttpBinding webHttpBinding = new WebHttpBinding();
webHttpBinding.AllowCookies = true;
ServiceEndpoint ep = host.AddServiceEndpoint(typeof(IIntegrationService), webHttpBinding, "");
host.Open()
ChannelFactory<IIntegrationService> cf = new ChannelFactory<IIntegrationService>(webHttpBinding, hostPath);
IIntegrationService channel = cf.CreateChannel();
Everything works nicely as long as our application is run as administrator. If we run our application on a machine without administrative privileges the host.Open() will throw an HttpListenerException with ErrorCode == 5 (ERROR_ACCESS_DENIED).
We can get around the problem by running httpcfg.exe from the command line but this is a one-click desktop application and that's not really as long term solution for us.
We could ditch WCF and write our own HTTP server but I'd like to avoid that if possible.
What's the easiest way to replace HttpListener with a standard TCP socket while still using all of the remaining HTTP scaffolding that WCF provides?
Your problem is not related to HttpListener.
Your problem is:
* You have a oneClick application with limited permissions that
* Tries to open a Server port.
This is a contradiction. An untrusted limited permission application should NOT OPEN A SERVER PORT. This is why this is not allowed per definition.
Have you tried opening a normal socket port? It should not work either.
In general, limited trust end user applications should not host a web service ;)
That said, I ahve been in a similar situation trying to use WCF in a driver communication scenario - thank heaven my application runs with full permission.
You can easily compose your own stack via CustomBinding, using the higher level protocol stuff "as is", and rolling your own version of HttpTransport that isn't backed by HttpListener or IIS. Do-able, sure, but it's a lot of work. Take the existing HttpTransport bits apart with Reflector- there are a LOT of moving parts in there. You could probably hack up a simple PoC over Socket in a day or two if you don't need anything fancy like HTTPS or chunking, but making it robust will be tricky. Here's a good wrapup of a bunch of resources (may be a bit dated now).
You could also look at ripping apart enough of Cassini to make it hostable in your app, and loading the WCF pipeline in there (via .svc files and the service activation handler)- it'd require writing very little new code that way, but still give you a fairly robust and tested webserver.

Interoperate between C# and Java using web services without a Java EE application server?

I'm in a difficult position: We have a 3rd party enterprise system that exposes a Java-based API. However, we are a 100% .Net oriented development team. Essentially, I need to wrap the Java API with something that C# code can call.
Web services would be great, but the only Java application server supported on our infrastructure is WebSphere 6.1. This means the ancient (and deprecated) JAX-RPC web service framework is the only way for us to expose web services. Just getting a simple proof-of-concept working here has been a nightmare (because of Java inexperience, WebSphere being awful, JAX-RPC being clunky, and lots of JAR hell).
The new JAX-WS 2.0 web service framework in JAVA EE 5 looks great-- is there any way to run this without an entire Java application server? For example, in .Net's WCF (Windows Communication Framework), you can host services pretty much anywhere you want (in-process, Windows Service, IIS 6/7 etc).
What is the most lightweight way to wrap this library with some web services?
Yes.
If you can create a Java method which is 1) annotated with #WebMetod and 2) takes the needed parameters and call into your 3'rd party code, and wrap it up as a web application you can use the Metro stack - https://metro.dev.java.net/ - with any Servlet 2.5 web container (put it in the web containers global lib folder) to expose the above method as a web service. We are using an embedded Jetty, but I've verified this works with Tomcat.
I wrote up my findings in http://archive.midrange.com/java400-l/200904/msg00071.html
I downloaded Metro 1.4 from https://metro.dev.java.net/1.4/ (version 1.5 is very new and I haven't looked at it), which eventually unpacks to several jar files.
Copy webservices-api.jar, webservices-rt.jar, webservices-extra-api.jar and webservices-extra.jar (four files) to the folder containing "blessed" jarfiles common to all of tomcat - I believe it is ${TOMCAT}/lib for Tomcat 6.[1]
In your Eclipse project eventually ending up to be a WAR file:
If your workspace JRE is Java 5, you must add webservices-api.jar to the classpath (it should not be deployed in the end). If it is Java 6 you should be able to skip this step.
Create a class foo.Ping looking like:
package foo;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
Ping is a simple web service class providing a "yes, we have contact" class.
Currently the doPing() method provides a response with the host name and
address (if available) and the current server time.
*/
#javax.jws.WebService
public class Ping {
#javax.jws.WebMethod(action = "doPing")
public String doPing() {
System.out.println("Ping.doPing() called.");
String hostName;
try {
hostName = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
hostName = "unknown (" + e.getMessage() + ")";
}
String hostAddress;
try {
hostAddress = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
hostAddress = "unknown (" + e.getMessage() + ")";
}
return "Reached '" + hostName + "' (" + hostAddress + ") at "
+ new java.util.Date() + " java.version="
+ System.getProperty("java.version", "(not set)");
}
}
In your WEB-INF/web.xml add this snippet:
<listener>
<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
</listener>
<servlet>
<description>JAX-WS endpoint - this servlet must handle all endpoints</description>
<display-name>webservice</display-name>
<servlet-name>webservice</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- each endpoint must have a mapping to the JAX-WS endpoint servlet -->
<servlet-mapping>
<servlet-name>webservice</servlet-name>
<url-pattern>/ws</url-pattern>
</servlet-mapping>
Create a NEW file WEB-INF/sun-jaxws.xml:
<endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'>
<endpoint name='ping' implementation='foo.Ping'url-pattern='/ws'>
</endpoint
</endpoints>
Ensure that both web.xml and sun-jaxws.xml are included in the deployment!
Done!
Now deploy your war file to the Tomcat prepared above, and open "/ws" under your deployed web application. This might be http://localhost:8080/foo/ws;. This will give you a page with information including a link to WSDL for all web services, including the Ping. This link can be used directly in any WSDL processing tool, including the web service tool in Eclipse IDE for Java EE Developers and WSDCi.
Hope this helps you :)
[1] Not making them global WILL give you classloader problems!
I don't quite understand your statement "the only Java application server supported on our infrastructure is WebSphere 6.1" with respect to the question about how to run other stuff. But no, you don't need a full App Server just to expose a web service.
I think this is a good starting point for you: http://docs.codehaus.org/display/JETTY/J2se6HttpServerSPI
I ended up finding a solution that was far easier than any of the above. We created some simple classes (like the doPing() method in #Thorbjørn Ravn Andersen's answer) with the #javax.jws.WebService and #javax.jws.WebMethod annotations, then deployed them using:
string url = "http://localhost:8282/MyService"
MyService serviceInstance = new MyService();
Endpoint svc = Endpoint.publish(url, serviceInstance);
I was then able to point Visual Studio at http://localhost:8282/MyService?wsdl and generate a client. Easy as pie.
We have run a lot of requests through this service over a large span of time and have not noticed any problems. We wrapped this with the Java Service Wrapper so that it comes back up across reboots/JVM crashes, etc. A poor man's application server.
I hope this might help any other .NET developer looking to interoperate with Java without having to remap your brain to do it.
Since you can't update your version of the JDK, as I expect you are tied to what WebSphere needs, you may want to look at trying the axis famework, from apache.
This would require you to write a webservice that would just pass calls to the java code, but it should provide you the tools to get online, and it works well with older versions of java.
I expect that Jax-WS would be a problem unless by some miracle you are on JDK5 at least, then JAX-WS would be helpful, as the annotations makes the development more like the webservice model under .NET 2.0.

Categories