Execute local R script via R.NET - c#

I'm looking to use R.NET to execute an existing R script but haven't had success. Is this actually possible? I've run the example code successfully, so my basic setup is ok.
My code looks like the following:
static void RTest()
{
var envPath = Environment.GetEnvironmentVariable("PATH");
var rBinPath = System.Environment.Is64BitProcess ? #"C:\Program Files\R\R-3.0.1\bin\x64" : #"C:\Program Files\R\R-3.0.1\bin\i386";
Environment.SetEnvironmentVariable("PATH", envPath + Path.PathSeparator + rBinPath);
using( var engine = REngine.CreateInstance("RDotNet") )
{
engine.Initialize();
using( var fs = File.OpenRead(#"C:\R-scripts\r-test.R") )
{
engine.Evaluate(fs);
}
}
}
Which I'm running in a console app for testing (eventually I want to have it run server-side in a web app).
The r-test.R script works when run in RStudio so there's no problem there and should result in writing a csv file to disk. I do see some 'Loading required package' messages being output to the console so something is working but the resultant csv file does not appear.

As indicated in response to this post in the R.NET discussions, you can use engine.Evaluate(#"source('c:/path/to/r-test.R')"). Although a lot depends on the content of your script of course, it should work. That said your code looks like it should work as well, though I have not tried your approach.
It is possible that R.NET chokes on some particular R statement within your script. If you have visual studio it should be possible for you to attach to the process if you use R.NET compiled from source with debug symbols. If you have Visual Studio this is the easiest option; MonoDevelop / Xamarin studio is also an option, though a bit more involved. This should help you identify the troublesome line.
Hope this helps

Related

Calling another EXE with C# on Windows fails on target machine

I have created a Windows shell extension that provides a menu to users when they right-click in File Explorer. This then invokes a small dialogue App.
I'm a novice at software development so there is probably some schoolboy error I'm making but I just can't see it.
For testing purposes I am hard-coding the path to the EXE and ensuring it's present on the VM.
private void CallExteralAddLocation(string sFullPath)
{
string sEXE = Path.Combine(Application.StartupPath, "AddLocation.exe");
WriteLog(sEXE);
// during testing, hard-code this path
sEXE = "C:\\temp\\AddLocation.exe";
System.Diagnostics.Process.Start(sEXE, sFullPath);
}
private void WriteLog(string sText)
{
using (StreamWriter sr = new StreamWriter("C:\\Temp\\ShellExtLog.txt"))
{
sr.WriteLine(sText);
}
}
It works fine on my host machine but when running on a VM running Windows 10, which is where I am testing it, the dialogue App does not get invoked.
I have checked that I can invoke the AddLocation.exe from a command window on the VM and that works as expected. So it looks like it's just not getting called.
As it's a shell extension it's very difficult to debug. I tried using MessageBoxes and writing to the console but those wouldn't work, so I have added the WriteLog so that I have some idea of what it's doing.
This works on the host machine i.e. a log file is created and it shows the path to the EXE, but no log file is created on the VM.
NOTE: When testing on either machine, I'm installing afresh from the application's MSI. Also note that the host is running Windows 11 and the VM is running Windows 10.
So, why does the same code fail to either run the EXE or create a log file on the VM?
I eventually worked out that the problem was how C# handles strings.
If you read through my problem above you will see that, weirdly the same code worked fine on Windows 11 but not on Windows 10.
The problem was that I was letting the compiler interpret the path string. I had 'escaped' the slashes as shown here and it should have worked fine:
sEXE = "C:\\temp\\AddLocation.exe";
Changing it to the following resolved the problem so that it works on both now:
sEXE = #"C:\temp\AddLocation.exe";
Similarly, whereas it wouldn't create a log file on Win10, changing the path to the log file to the following fixed it:
using (StreamWriter sr = new StreamWriter(#"C:\Temp\ShellExtLog.txt"))

pythonnet Embedding Python in .net example failing to load module

I'm trying to run the Embedding Python in .NET example from https://github.com/pythonnet/pythonnet. I've followed troubleshooting articles to set the proper %PYTHONPATH% and %PYTHONHOME% to my anaconda environment in the program base directory.
After activating my anaconda environment, I have successfully imported sys, and imp as a test, and also sucessfully used PythonEngine.RunSimpleString(), but the numpy example fails with Python.Runtime.PythonException: ImportError : No module named 'numpy'
importing numpy from python in this environment was successful, but this and other packages fail to import in pythonnet.
Pythonnet version: 2.3 x64 (installed using conda install -c pythonnet pythonnet)
Python version: Python 3.5 x64 (anaconda)
Operating System: Windows 10
The following code produces the error:
static void Main(string[] args)
{
string envPythonHome = AppDomain.CurrentDomain.BaseDirectory + "cntk-py35";
string envPythonLib = envPythonHome + #"\Lib";
Environment.SetEnvironmentVariable("PYTHONHOME", envPythonHome, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("PATH", envPythonHome + ";" + Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Machine), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("PYTHONPATH", envPythonLib, EnvironmentVariableTarget.Process);
PythonEngine.PythonHome = envPythonHome;
PythonEngine.PythonPath = Environment.GetEnvironmentVariable("PYTHONPATH");
using (Py.GIL())
{
dynamic np = Py.Import("numpy");
Console.WriteLine(np.cos(np.pi * 2));
dynamic sin = np.sin;
Console.WriteLine(sin(5));
double c = np.cos(5) + sin(5);
Console.WriteLine(c);
dynamic a = np.array(new List<float> { 1, 2, 3 });
Console.WriteLine(a.dtype);
dynamic b = np.array(new List<float> { 6, 5, 4 }, dtype: np.int32);
Console.WriteLine(b.dtype);
Console.WriteLine(a * b);
Console.ReadKey();
}
}
It seems that any package under site-packages in my environment similarly fail. Adding to %PATH% did not work. Is there a way to get pythonnet to recognize and load these modules?
Setting up your python environment in .NET is a bit cumbersome.This issue is not well detailed on pythonnet website or most of suggested solutions I have found on the internet did not work for my computer. The reason is that every computer may have a different python setup environment (depending how you have installed python and the libraries). It took me a while as well but finally I have succeeded to call python modules and .py scripts from .NET. Here is what I did.
Pythonnet version: 2.4.0 x64 (installed using pip install # Anaconda CMD prompt)
Python version: Python 3.7 x64 (Anaconda)
Operating System: Windows 10
Keep in mind that everyone has a different Python environment, that is why you have to configure your environment first (in your VS project).
First, we need to assign "PATH", "PYTHONHOME" and "PYTHONPATH" variables.
in C# use:
string pythonPath1 = #"C:\Users\<your username>\Anaconda3";
string pythonPath2 = #"C:\Users\<your username>\Anaconda3\lib\site-packages";
Environment.SetEnvironmentVariable("PATH", pythonPath1, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("PYTHONHOME", pythonPath1, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("PYTHONPATH", pythonPath2, EnvironmentVariableTarget.Process);
I have used Anaconda to install python runtime and packages. Note that default Anaconda installation is under C:\Users(your username assigned in your computer)\Anaconda3. (You can find yours using code by (How can I get the current user directory?). If you did not use Anaconda, you need to locate the directory where python packages are installed on your computer.
1-C:\Users\\Anaconda3 directory must have your version of python DLL (i.e.python37.dll).
2-C:\Users\\Anaconda3\lib\site-packages has the "modules" (i.e. python frameworks like 'numpy').
3-You must reference python runtime in your project (Python.Runtime.dll).(use Windows File Explorer to find the file. If you use Anaconda the runtime is under C:\Users\\Anaconda3 directory).
4-Add following on top of your code:
using Python.Runtime;
5-In VS Solution Explorer right click on your project and select 'Properties'
and make sure to set 'Platform Target' (either x64 or x86)
6-After doing all this, if you can NOT run some of the 'modules' and receive a "Can not load module" or "can not find module" exception message, then follow the instruction here (https://github.com/microsoft/vscode-python/issues/9218)
Usually, uninstalling/re-installing the module will resolve the issue by updating the version. (i.e. pip uninstall numpy/pip install numpy).
NOTE: The code still works, even if you do not set PYTHONPATH variable above. PYTHONPATH variable is used, when you need to call a custom .py script, where you identify the directory where your file resides. A descriptive example can be found at https://stackoverflow.com/a/57910578/7675537
UPDATE: I have realized that one easy way to configure your Python Environment is using Visual Studio (I use VS 2017 Community version). Just go to Python in Visual Studio and go through the example. In VS under 'Python Environments' you can observe all the setups you have in your computer. I had problems to run .py scripts, like not being able to use 'import matplotlib.pyplot' and spent several hours reading articles on the internet but could not find a solution. Finally I switched my environment to 'C:\Users\\AppData\Local\Programs\Python\Python37\' and installed all the missing packages from visual studio's list (of suggestions) and it worked. I think calling Anaconda environment from .NET (via pythonnet or else) has problems. I would suggest not to use your Anaconda python environment if you make .NET calls to python. I use:
private static string pythonPath1 = #"C:\Users\<your name>\AppData\Local\Programs\Python\Python37";
static void Main(string[] args)
{
Test();
}
private static void Test()
{
string pathToPython = pythonPath1;
string path = pathToPython + ";" +
Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Machine);
Environment.SetEnvironmentVariable("PATH", path, EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("PYTHONHOME", pathToPython, EnvironmentVariableTarget.Process);
var lib = new[]
{
#"C:\Users\<your name>\<your python code is here>",
Path.Combine(pathToPython, "Lib"),
Path.Combine(pathToPython, "DLLs")
};
string paths = string.Join("; ", lib);
Environment.SetEnvironmentVariable("PYTHONPATH", paths, EnvironmentVariableTarget.Process);
using (Py.GIL()) //Initialize the Python engine and acquire the interpreter lock
{
try
{
// import your script into the process
dynamic sampleModule = Py.Import("yourpythoncode");
}
catch (PythonException error)
{
Console.WriteLine("Error occured: ", error.Message);
}
}
}
I was able to import the modules by adding Lib/site-packages to the PYTHONPATH variable (rather than the PATH) which adds the folder to sys.path. It was necessary for any other python libraries and custom python code to add the corresponding folder to PYTHONPATH.

Process.Run() with Args in Mono (Linux), not starting the process

I need to start updater application by passing URL of the patch file. Basic Process.Run works fine, however when I pass argument nothing happens. (This is Linux Mint and Mono)
I have already checked SO, net, etc. and tried solutions such as setting UseShellExecute to false or using ProcessStartInfo.
What am I doing wrong ?
Updater is located in the same folder where the main exe is.
Mono console shows no errors.
Works : (works for everyone so no surprise there)
Process.Start (Application.StartupPath + #"/Updater.exe");
Doesn't work (Nothing happens, also expected ?) :
Process.Start(Application.StartupPath + #"/Updater.exe", "URLToFile");
From other solutions I have tried :
Process.Start(new ProcessStartInfo(Application.StartupPath + #"/Updater.exe", #"URLToFile.zip") { UseShellExecute = false });
I also tried many solutions proposed here :
How to use Process.Start() or equivalent with Mono on a Mac and pass in arguments
And I checked some other blogs and google results. No solution ever worked for me sadly.
More info that might not be as important but could help :
Code works in Windows
I run Mono code separated from Windows code by checking platform
Application checks for updates then starts updater and closes itself
Updater is located in the same folder in which the main exe is
Updater is separated project
This is an portable application
What am I doing wrong? Thanks.
Try to prefix "mono " since you want in fact to run mono with your .exe as an argument. Also I determine the application path in another way:
string sAppPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
Process.Start("mono " + sAppPath + #"/Updater.exe");
Sorry, I did not test my code. Now I did a small project and tested it - just a form with a label and a button. Working fine. I'm getting the right path and the HelloWorld App gets started and processes the CmdLineArg. Here the core content comes:
private void DoIt()
{
string sAppPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo();
psi.FileName = "mono";
psi.Arguments = sAppPath + #"/HelloWorld.exe CmdLineArg";
System.Diagnostics.Process.Start(psi);
}

Creating new VS2015 solutions with 2 projects programmatically using C#

I am using the following code to try to programmatically create Visual Studio 2015 projects on the fly from within an existing Console Application. Why you ask? We are putting together a library of all our code snippets and we want a test/proof test solution for each one, and since we have hundreds of these, we aren't going to do it manually.
Ultimately this will be in an MVC5 app as its own project (class library?) but for now we are just trying to get it functional within this console application.
I am trying to create a new solution with 2 projects (a console application and a unit test project).I am also not even sure I need the unit test project, or whether one even works with a console app since there is no option to add a unit test to a console app in VS2015 solution generation.
Here is the code;
public class CreateConsoleProjectsProgrammatically
{
private const string Vs2015Type = "VisualStudio.DTE.14.0";
private const string Vs2015ConsoleProject = #"X:\Code Library\Helpers\ConsoleApplication\csConsoleApplication.vstemplate";
private const string Vs2015UnitTestProject = #"X:\Code Library\Helpers\UnitTestProject\UnitTestProject.vstemplate";
private const string Vs2015CodeLibraryBasepath = #"X:\Code Library";
public static void CreateVsConsoleProjectProgrammatically(string filename)
{
// Create a solution with two projects in it, based on project
// templates, a console project and a unit test project.
var vsType = Type.GetTypeFromProgID(Vs2015Type);
//error line is below
var vsInstance= Activator.CreateInstance(vsType, true);
EnvDTE.DTE dte = (EnvDTE.DTE)vsInstance;
dte.MainWindow.Visible = false; // optional: set to true if you want to see VS doing its thing
// create a new solution
dte.Solution.Create(#"X:\Code Library\", filename);
var solution = dte.Solution;
// create a C# console application
solution.AddFromTemplate(Vs2015ConsoleProject,Path.Combine(Vs2015CodeLibraryBasepath,filename), filename);
// create a unit test project
solution.AddFromTemplate(Vs2015UnitTestProject, Path.Combine(Vs2015CodeLibraryBasepath, filename + "_Test"), filename + "_Test");
// save and quit
dte.ExecuteCommand("File.SaveAll");
dte.Quit();
}
}
Here is the error which is from the Activator.CreateInstance
Creating an instance of the COM component with CLSID {A2FA2136-EB44-4D10-A1D3-6FE1D63A7C05} from the IClassFactory failed due to the following error: 8001010a The message filter indicated that the application is busy. (Exception from HRESULT: 0x8001010A (RPC_E_SERVERCALL_RETRYLATER)).
Not sure why I am getting a server busy error. It almost seems like its because VS is already open? But how else would the new solution be generated? I tried closing the solution and reopening it but that did nothing to solve the issue.
I also thought it may have been a connectivity issue, so I moved all the files and directories to C:\ but it produced the same error, so it wasn't an issue of using a networked drive location.
I also tried using
EnvDTE80.DTE2 dte2 = (EnvDTE100.DTE2)vsInstance;
but DTE2 is not an available property/method on EnvDTE100 according to the editor, even though I found some examples using it on the net.

Error when reading audio file?

I have the following function that I am attempting to use to determine the length of an MP3 file:
public static string GetMP3DurationBackup(string Filename)
{
string Duration = null;
WMPLib.WindowsMediaPlayer w = new WMPLib.WindowsMediaPlayer();
WMPLib.IWMPMedia m = w.newMedia(Filename);
if (m != null)
{
Duration = m.durationString;
}
w.close();
return Duration;
}
I have run into an issue where I get the following error:
Retrieving the COM class factory for component with CLSID
{6BF52A52-394A-11D3-B153-00C04F79FAA6} failed due to the following
error: 80040154..
when I call the above function from my web application (call below):
string test = MediaUtil.GetMP3DurationBackup(#"C:\Temp\Audio\bad.mp3");
But when I call it from a console application test harness I created (exact same call as above) it works fine. I have set the project that contains the function to target x86 in the Build properties, but that did not fix the issue.
Does anyone know why this would happen? Suggestions on where to start to debug this?
UPDATED FOR BOUNTY:
Ok, I've tried a number of things but I am still getting this error. Among other things I have tried the steps below which I felt were the most promising, but no dice:
Went into my registry and confirmed that the value at:
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{6BF52A52-394A-11d3-B153-00C04F79FAA6}\InprocServer32
is refering to C:\WINDOWS\SysWOW64\wmp.dll
Opened command prompt,
navigated to C:\WINDOWS\SysWow64, ran: regsvr32.exe wmp.dll
I have created a console app test harness and I am able to reproduce the error if I run the test project in x64. If I switch it to x86 it works fine.
Does anyone have any idea of why the above would not resolve the issue? Suggestions on where to look next?
You say it doesn't work in x64, but you try to register the 32-bit version of wmp.dll (C:\Windows\SysWow64 contains 32-bit assemblies).
Try to register the x64 version of wmp.dll, which is located in C:\Windows\System32 on a 64-bit platform.
If you don't have this file then there probably is no 64bit Windows Media Player available for your platform. But there is a workaround:
Create a 32-bit console application that takes the mp3 filename as command line argument and outputs the duration to stdout using Console.WriteLine, then in the webapp, you call the console application and capture the output like in this example on MSDN
Give this lib a whirl. Its fast and has no special requirements for software to be installed on the machine.
http://naudio.codeplex.com/

Categories

Resources