首页 > 解决方案 > 为什么我不能通过 C# 使用 R 绘制图形?

问题描述

我必须使用软件环境 R 显示函数图。R.dll 的路径已经在«PATH»环境变量中。但是当我运行我的代码时,我得到一个异常:«System.AccessViolationException:Attempted to read or write protected memory。这通常表明其他内存已损坏»。它出现在这部分代码中:

public static void Execute(string command)
{
    IntPtr cmd, expr;
    ParseStatus status = 0;
    int error = 0;
    cmd = Rf_mkString(command);
    Rf_protect(cmd);
    Rf_protect(expr = R_ParseVector(cmd, -1, ref status, R_NilValue));
    Rf_unprotect(2);
    if (status != ParseStatus.PARSE_OK)
        throw new Exception("Failed");
    R_tryEvalSilent(VECTOR_ELT(expr, 0), R_GlobalEnv, ref error);
}

正是在Rf_protect(expr = R_ParseVector(cmd, -1, ref status, R_NilValue));;我不明白的原因。完整代码:

    using System;
using System.Runtime.InteropServices;
using System.Threading;

/// <summary>
/// Interface to the R engine
/// </summary>
public static class REngine
{
    /// <summary>
    /// TODO: write summary
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    struct vecsxp_struct
    {
        int length;
        int truelength;
    }

    /// <summary>
    /// TODO: write summary
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    struct VECTOR_SXPREC
    {
        int sxpinfo;
        IntPtr attrib;
        IntPtr gengc_next_node, gengc_prev_node;
        vecsxp_struct vecsxp;
    }

    /// <summary>
    /// TODO: write summary
    /// </summary>
    [StructLayout(LayoutKind.Sequential, Pack = sizeof(double))]
    struct SXPREC_ALIGN
    {
        VECTOR_SXPREC s;
    }

    /// <summary>
    /// R parser status code
    /// </summary>
    enum ParseStatus : int
    {
        PARSE_NULL,
        PARSE_OK,
        PARSE_INCOMPLETE,
        PARSE_ERROR,
        PARSE_EOF
    }

    // External functions from R.dll
    // NOTE: on Win32 the default calling convention is Stdcall, on Win64 CC does not matter

    [DllImport("R.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern int Rf_initEmbeddedR(int argc, IntPtr argv);

    [DllImport("R.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
    static extern IntPtr Rf_mkString(string s);

    [DllImport("R.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern IntPtr Rf_protect(IntPtr e);

    [DllImport("R.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern IntPtr R_ParseVector(IntPtr e, int n, ref ParseStatus PraseStatus, IntPtr SrcFile);

    [DllImport("R.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern void Rf_unprotect(int n);

    [DllImport("R.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern IntPtr R_tryEvalSilent(IntPtr e, IntPtr env, ref int error);

    [DllImport("R.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern IntPtr Rf_findVar(IntPtr e, IntPtr env);

    [DllImport("R.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    static extern IntPtr Rf_install(string s);

    [DllImport("R.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern int Rf_length(IntPtr e);

    [DllImport("R.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern void Rf_endEmbeddedR(int fatal);

    /// <summary>
    /// Attribute
    /// </summary>
    /// <param name="e">e</param>
    /// <returns>IntPtr</returns>
    static IntPtr ATTRIB(IntPtr e)
    {
        unsafe
        {
            return *(IntPtr*)IntPtr.Add(e, sizeof(int));
        }
    }

    /// <summary>
    /// Data pointer
    /// </summary>
    /// <param name="e">e</param>
    /// <returns>IntPtr</returns>
    static IntPtr DATAPTR(IntPtr e)
    {
        return IntPtr.Add(e, Marshal.SizeOf(new SXPREC_ALIGN()));
    }

    /// <summary>
    /// Real value
    /// </summary>
    /// <param name="e">e</param>
    /// <returns>value</returns>
    static double REAL(IntPtr e)
    {
        unsafe
        {
            return *(double*)DATAPTR(e);
        }
    }

    /// <summary>
    /// Element of a vector
    /// </summary>
    /// <param name="e">vector</param>
    /// <param name="i">0-based element index</param>
    /// <returns>element</returns>
    static IntPtr VECTOR_ELT(IntPtr e, int i)
    {
        unsafe
        {
            return *(IntPtr*)IntPtr.Add(DATAPTR(e), sizeof(IntPtr) * i);
        }
    }

    /// <summary>
    /// Empty value
    /// </summary>
    static IntPtr R_NilValue = IntPtr.Zero;

    /// <summary>
    /// Global environment
    /// </summary>
    static IntPtr R_GlobalEnv = IntPtr.Zero;

    /// <summary>
    /// Absent value
    /// </summary>
    static IntPtr R_UnboundValue = IntPtr.Zero;

    static REngine()
    {
        Rf_initEmbeddedR(0, IntPtr.Zero);
    }

    /// <summary>
    /// Run command in R
    /// </summary>
    /// <param name="command">command</param>
    public static void Execute(string command)
    {
        IntPtr cmd, expr;
        ParseStatus status = 0;
        int error = 0;
        cmd = Rf_mkString(command);
        Rf_protect(cmd);
        Rf_protect(expr = R_ParseVector(cmd, -1, ref status, R_NilValue));
        Rf_unprotect(2);
        if (status != ParseStatus.PARSE_OK)
            throw new Exception("Failed");
        R_tryEvalSilent(VECTOR_ELT(expr, 0), R_GlobalEnv, ref error);
    }
    /// <summary>
    /// Read real variable from R
    /// </summary>
    /// <param name="varname">variable name</param>
    /// <returns>value</returns>
    public static double GetDouble(string varname)
    {
        double result = 0.0;
        IntPtr var;
        Rf_protect(var = Rf_findVar(Rf_install(varname), R_GlobalEnv));
        if (var == R_UnboundValue)
        {
            Rf_unprotect(1);
            throw new Exception("Unknown variable");
        }
        else
        {
            result = REAL(var);
        }
        Rf_unprotect(1);
        return result;
    }

    /// <summary>
    /// Terminate R
    /// </summary>
    public static void Exit()
    {
        Rf_endEmbeddedR(0);
    }
}


/// <summary>
/// Win32 API data structures and funtions
/// </summary>
class Win32
{
    /// <summary>
    /// Win32 Message
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct MSG
    {
        public IntPtr hwnd;
        public UInt32 message;
        public IntPtr wParam;
        public IntPtr lParam;
        public UInt32 time;
        public POINT pt;
    }

    /// <summary>
    /// Point
    /// </summary>
    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        public int X;
        public int Y;

        public POINT(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }
    }

    [DllImport("user32.dll")]
    public static extern sbyte GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin,
       uint wMsgFilterMax);

    [DllImport("user32.dll")]
    public static extern bool TranslateMessage([In] ref MSG lpMsg);

    [DllImport("user32.dll")]
    public static extern IntPtr DispatchMessage([In] ref MSG lpmsg);
}

/// <summary>
/// Main class
/// </summary>
class Program
{
    /// <summary>
    /// Displays a plot window
    /// </summary>
    static void Plot()
    {
        REngine.Execute("plot(x, y, type=\"l\")");

        // Message loop

        Win32.MSG msg;

        while (Win32.GetMessage(out msg, IntPtr.Zero, 0, 0) != 0)
        {
            Win32.TranslateMessage(ref msg);

            Win32.DispatchMessage(ref msg);
        }
    }

    /// <summary>
    /// The entry point
    /// </summary>
    /// <param name="args">Command line arguments</param>
    static void Main(string[] args)
    {
        // Show info
        // TODO: think about localization

        Console.WriteLine("This program displays a plot of a function specified by a formula.");
        Console.WriteLine();

        // TODO: implement automatic detection of R

        Console.WriteLine("Before using this program, please download and install the R language from");
        Console.WriteLine();

        Console.WriteLine("\t\t\thttp://www.r-project.org");
        Console.WriteLine();

        Console.WriteLine("and add the path to R.dll to your PATH environment variable.");
        Console.WriteLine();

        Console.WriteLine("If you already have R, press any key. Otherwise, close this program.");
        Console.ReadKey();

        // Input formula and x range

        Console.WriteLine("Enter a formula of the form y = f(x), for example: y = 2*sin(x)+5:");
        string formula = Console.ReadLine();

        Console.WriteLine("Enter the range of x, for example: -5 5");
        string[] xrange = Console.ReadLine().Split(' ');

        REngine.Execute(string.Format("xmin = {0}", xrange[0]));
        REngine.Execute(string.Format("xmax = {0}", xrange[1]));
        REngine.Execute("x = seq(xmin, xmax, by=(xmax-xmin)/100)"); // hmmm... 100 points ?
        REngine.Execute(formula);

        // Show the plot

        new Thread(Plot).Start();

        // Wait until the user hits a key

        Console.WriteLine("Press any key to exit");
        Console.ReadKey();

        // Close R and exit
        // TODO: check if there are "static destructors" in C#

        REngine.Exit();
        Environment.Exit(0);
    }
}

有谁知道是什么原因?谢谢!

PS我的问题是关于如何在c#中实现R.dll,而不是lib。这个问题不是重复的。

标签: c#rdll

解决方案


推荐阅读