I wanted a host .NET application (in this case 3ds Max, but it applies equally to any .NET API like AutoCAD) to inform a Python script running on the same machine but in a different process whenever a particular event occured.
I first rejected a number of possible solutions that were low-level and that would prevent me from moving the client to another computer.
- Named pipes
- Memory mapped files
- Windows messages
- DLL function invocation
Two remaining options seemed like possible candidates:
- Web sockets
- HTTP server running in the host
I liked the Web sockets approach, but the libraries for using them in C# and Python are not quite as mature or easy to use as I had hoped. Running an HTTP server and having the Python application poll it repeatedly seemed like the best option. However, I was concered about all of the wasted resource for the Python client to ping the host application repeatedly not to mention the potential for poor responsiveness.
After a bit of thought I realized that embedding the HTTP server in the Python application and treating the .NET application as the client would be a lot easier!
With an HTTP server running in the Python application, I was with a new set of possibilities for communicating data from the host application to the Python client.
- Simple HTTP API using query strings
- REST API
- SOAP API
- XML RPC API
After further investigation I decided the simplest out-of-the-box solution would be to use the SimpleXmlRpcServer module in Python and the System.Net.WebRequest class in .NET. After quickly scanning the information on XML RPC on Wikipedia and hacking the example in the documentation for WebRequest and SimpleXmlRpcServer I ended up with a working prototype with almost no effort:
Here is the sample .NET client:
using System; using System.Text; using System.Net; using System.IO; namespace SimpleXmlRpcClient { class Program { static void Main(string[] args) { WebRequest request = WebRequest.Create("http://localhost:8000/RPC2"); request.Method = "POST"; string postData = @"<?xml version=""1.0""?><methodCall><methodName>add<params><param><value><i4>21</i4></value></param><param><value>21 </param></params></methodCall>"; byte[] byteArray = Encoding.UTF8.GetBytes(postData); request.ContentType = "application/x-www-form-urlencoded"; request.ContentLength = byteArray.Length; Stream dataStream = request.GetRequestStream(); dataStream.Write(byteArray, 0, byteArray.Length); dataStream.Close(); WebResponse response = request.GetResponse(); Console.WriteLine(((HttpWebResponse)response).StatusDescription); dataStream = response.GetResponseStream(); StreamReader reader = new StreamReader(dataStream); string responseFromServer = reader.ReadToEnd(); Console.WriteLine(responseFromServer); reader.Close(); dataStream.Close(); response.Close(); Console.WriteLine("Press any key to continue ..."); Console.ReadKey(); } } }
Here is the sample Python host:
from SimpleXMLRPCServer import SimpleXMLRPCServer from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler # Restrict to a particular path. class RequestHandler(SimpleXMLRPCRequestHandler): rpc_paths = ('/RPC2',) # Create server server = SimpleXMLRPCServer(("localhost", 8000),requestHandler=RequestHandler) server.register_introspection_functions() # Register a function e def adder_function(x,y): return x + y server.register_function(adder_function, 'add') # Run the server's main loop server.serve_forever()
Hopefully this is enough to get your favorite .NET application talking to your favorite Python application. Happy hacking!