Windows Universal Apps: Call Python Method From C#
Solution 1:
In UWP your options are pretty limited. Universal applications run in a sandbox and are restricted in how they can interact with classic desktop applications, such as Python, which prevents you from simply "shell-executing" your script using the local Python installation.
There was a way to do it in Windows 8.1 WinRT using brokered components, but the application needed to be sideloaded; it wouldn't work with applications installed from store. Of course, it required the desktop version of Windows and didn't work on Windows Phone, either. Also, I didn't see any mention of this in relation to Windows 10 UWP and I seriously doubt it still works.
Probably your best bet is to try the UWP version of CPython. I never used it, I don't know the state it is in and I have no idea, how much effort would be required to get scikit-learn working in it. But if you want your application to work on multiple platforms (desktop, mobile, IoT...), I don't know of another option.
On the other hand, if you're only targeting desktop, I would suggest you abandon the use of UWP and create a classic desktop application instead. This will allow you to use the standard CPython version without any restrictions, either by invoking the locally installed version or by embedding it into your app.
Solution 2:
You could use a standalone-app proxy. As Damir Arh said, this approach can not be used to distribute your application via the store. The main idea is to register a custom extension for the proxy and launch it by executing a helper file.
Proxy:
staticclassProgram
{
conststring ProxyExtension = ".python-proxy";
conststring ResultExtension = ".python-proxy-result";
[STAThread]
staticvoidMain(paramsstring[] args)
{
if (args.Length != 1)
return;
if ("install".Equals(args[0], StringComparison.Ordinal))
{
var path = Application.ExecutablePath;
SetAssociation(ProxyExtension, "PythonProxy", path, "Python Proxy");
}
else
{
var path = args[0];
if (!File.Exists(path))
return;
var serviceExt = Path.GetExtension(path);
if (!ProxyExtension.Equals(serviceExt, StringComparison.OrdinalIgnoreCase))
return;
path = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path).TrimEnd());
var ext = Path.GetExtension(path);
if (!".py".Equals(ext, StringComparison.OrdinalIgnoreCase))
return;
var start = new ProcessStartInfo
{
FileName = "python.exe",
Arguments = '"' + path + '"',
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true,
};
using (var process = Process.Start(start))
{
using (var reader = process.StandardOutput)
{
var result = reader.ReadToEnd();
var output = path + ResultExtension;
using (var mutex = new Mutex(true, "PythonProxy Mutex"))
{
File.WriteAllText(output, result);
}
}
}
}
}
publicstaticvoidSetAssociation(string ext, string name, string openWithPath, string description)
{
using (var key = Registry.ClassesRoot.CreateSubKey(ext))
{
if (key == null)
return;
key.SetValue("", name);
}
using (var key = Registry.ClassesRoot.CreateSubKey(name))
{
if (key == null)
return;
key.SetValue("", description);
using (var shellKey = key.CreateSubKey("shell"))
{
if (shellKey == null)
return;
using (var openKey = shellKey.CreateSubKey("open"))
{
if (openKey == null)
return;
using (var commandKey = openKey.CreateSubKey("command"))
{
if (commandKey == null)
return;
commandKey.SetValue("", $"\"{openWithPath}\" \"%1\"");
}
}
}
}
using (var key = Registry.CurrentUser.OpenSubKey($@"Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\{ext}"))
{
if (key != null)
key.DeleteSubKey("UserChoice", false);
}
Native.SHChangeNotify(Native.SHCNE_ASSOCCHANGED, Native.SHCNF_IDLIST, IntPtr.Zero, IntPtr.Zero);
}
classNative
{
publicconstuint SHCNE_ASSOCCHANGED = 0x08000000;
publicconstuint SHCNF_IDLIST = 0x0000;
[DllImport("shell32.dll")]
publicstaticexternvoidSHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
}
}
Universal Application:
publicasyncvoidFun()
{
var local = ApplicationData.Current.LocalFolder;
var scriptFile = await local.CreateFileAsync("script.py", CreationCollisionOption.ReplaceExisting);
using (var stream = await scriptFile.OpenStreamForWriteAsync())
using (var writer = new StreamWriter(stream))
{
await writer.WriteLineAsync(@"print ""Hello, World!""");
}
var proxyFile = await local.CreateFileAsync("script.py.python-proxy", CreationCollisionOption.ReplaceExisting);
await Launcher.LaunchFileAsync(proxyFile);
var resultPath = "script.py.python-proxy-result";
var counter = 0;
IStorageItem resultFile = null;
while (resultFile == null)
{
if (counter != 0)
{
if (counter++ > 5)
thrownew Exception();
await Task.Delay(250);
}
resultFile = await local.TryGetItemAsync(resultPath);
}
try
{
using (var mutex = new Mutex(true, "PythonProxy Mutex")) { }
}
catch (AbandonedMutexException) { }
using (var stream = await local.OpenStreamForReadAsync(resultPath))
using (var reader = new StreamReader(stream))
{
var content = await reader.ReadToEndAsync();
var dialog = new MessageDialog(content);
await dialog.ShowAsync();
}
await scriptFile.DeleteAsync();
await proxyFile.DeleteAsync();
await resultFile.DeleteAsync();
}
Solution 3:
I believe if you use something like this ( How to print out the value that subprocess prints with C#? ) to run your python script and capture the stdout, you should be able to achieve the desired functionality
Post a Comment for "Windows Universal Apps: Call Python Method From C#"