Import fail yeilds System.AccessViolationException

Trying to do some batch work to a bunch of SLDPRT files. Out of 1600 SLDPRT files everything is working as expected… However, consistently on one SLDPRT file, my console app crashes with a “System.AccessViolationException” that I cannot seem to be able to catch.

Anyone know how to work around the Exception? I’m less worried that 1 out of 1600 SLDPRT file imports fail than I am about not being able to catch and deal with this exception in my console application. I also have no clue how to post code in this forum (sorry).

Any good ideas? Code and details below. Thank you.

SLDPRT - Attached SCREW;BH;HS;GB-70.2__M8-1.25 x 60.SLDPRT (3.5 MB)

static void Main(string[] args)
{
    string PartFilePath = {UNIX STRING TO PART FILE ON YOUR COMPUTER}
    string Output3dmPath = {output 3dm path}
    using (new RhinoCore(new string[] { "/NOSPLASH" }, WindowStyle.Hidden))
    {
        var doc = RhinoDoc.Create("Small Objects - Meters");
        string importScript = string.Format("-_Import \"{0}\" _Enter", file.Replace('/', '\\'));
        try
        { 
          RhinoApp.RunScript(importScript, false);
        } catch (System.AccessViolationException)
        {
          Console.WriteLine("NEVER HIT");
         }
}

Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at UnsafeNativeMethods.CRhinoApp_RunScript1(String script, Int32 echoMode)
   at Convert.Program.Main(String[] args) in ....Program.cs:line 50

I believe Rhino runs out of memory. There might be potential memory leak.

I would recommend not to create new RhinoDoc for every file.

Maybe add an unhandled exception handler, see

1 Like

Thank you for the suggestions gang! I spent the day troubleshooting without any success. Here’s were things get a bit odd… On further inspection, I can process the previously attached SLDPRT fine by it’s own, it’s only when looping over that part and another part(s) does the crash seem to happen.

@menno - I integrated the UnhandledException Event example into my console app, but it doesn’t seem to work for this exception.

What I’m doing with my console application is looping over *.SLDPRT files and saving them out as .3dm files. Using a single RhinoDoc in the session to do this. At the end of each part, I clear all RhinoDoc objects with -

foreach (var obj in doc.Objects)
    doc.Objects.Purge(obj.RuntimeSerialNumber);

Below is the full.cs if anyone has any other ideas on how to handle this? Attached is also the .cs document as well as two test .SLDPRT files. Just place the two parts in the same folder, pass the folder path of the two parts as the argument into the app.

I could be very wrong, but it’s almost like the RhinoCore itself is throwing some sort of exception, not the console app. Thank you for following this! Just purchased a Rhino license, coming off an eval, and hoping there is a solution to this or I’m skunked!

Program.cs (2.6 KB)
SCREW;BH;HS;GB-70.2__M8-1.25 x 60.SLDPRT (3.5 MB)
SCREW;BH;HS;GB-70.2__M8-1.25 x 62.SLDPRT (3.6 MB)

using System;
using System.IO;

using Rhino.Runtime.InProcess;
using Rhino.Geometry;
using Rhino;
using System.Security.Permissions;

namespace Convert
{

    class Program
    {
    #region Program static constructor
    static Program()
    {
        RhinoInside.Resolver.Initialize();
    }
    #endregion

    // Use STAThread for this app as we are starting Rhino in a mode that does actually
    // include user interface (we just never show the main window). This allows for things
    // like RhinoApp().RunScript to properly run.
        [System.STAThread]
        [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
        static void Main(string[] args)
        {
            AppDomain currentDomain = AppDomain.CurrentDomain;
            currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);

            string rPath = args[0].Replace("/", "\\");
            string[] filePaths = Directory.GetFiles(rPath, "*.SLDPRT");

            try
            {
                using (new RhinoCore(new string[] { "/NOSPLASH" }, WindowStyle.Hidden ))
                {
                    string dumpDir = "C:/Users/alpha/Documents/scratch/dev/dump";
                    var doc = RhinoDoc.Create("Small Objects - Meters");

                    foreach (string prt in filePaths)
                    {
                        Console.WriteLine(prt);
                        string filename = Path.GetFileNameWithoutExtension(prt);
                        string output3dm = Path.ChangeExtension(prt, ".3dm");
                        

                        string importScript = string.Format("-_Import \"{0}\" _Enter -_Export Version=4 \"{1}\" _Enter", prt, output3dm);
                        bool r = RhinoApp.RunScript(importScript, false);

                        foreach (var obj in doc.Objects)
                            doc.Objects.Purge(obj.RuntimeSerialNumber);
                    }

                    RhinoApp.RunScript("-_Exit No", false);
                }
            }
            catch (Exception ex)
            {
            Console.Error.WriteLine(ex.Message);
            Console.WriteLine("press any key to exit");
            Console.ReadKey();
            }
        }

        static void MyHandler(object sender, UnhandledExceptionEventArgs args)
        {
            Exception e = (Exception)args.ExceptionObject;
            Console.WriteLine("MyHandler caught : " + e.Message);
            Console.WriteLine("Runtime terminating: {0}", args.IsTerminating);
        }
    }
}

Here is the specific Exception for the above:

C:\Users\alpha\Documents\scratch\dev\scratch\error\SCREW;BH;HS;GB-70.2__M8-1.25 x 60.SLDPRT
C:\Users\alpha\Documents\scratch\dev\scratch\error\SCREW;BH;HS;GB-70.2__M8-1.25 x 62.SLDPRT

Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
   at UnsafeNativeMethods.CRhinoApp_RunScript1(String script, Int32 echoMode)
   at Convert.Program.Main(String[] args) in C:\Users\alpha\Documents\scratch\rhinoInside\rhino-developer-samples\rhino.inside\dotnet\SampleConvert\Program.cs:line 49
Press any key to continue . . .

The SCREW;BH;HS;GB-70.2__M8-1.25 x 60 file does not open in Rhino at all. It either contains a feature that is not supported, or it is corrupted.

1 Like

Thank you for checking. The question then really is how to catch a corrupt part like this before it crashes the Rhino.Inside console application process? I’ll keep trying today. It seems a conventional “try”, “catch” and unhandeled exception approach doesn’t work to catch the issue.

Let me know if you have any advice or suggestions. Thank you.

Looks like you can’t catch these exceptions for good reason. Although it may be possible with the HandleProcessCorruptedStateExceptionsAttribute - but you’re definitely on thin ice here, do not assume that the RhinoInside still works after you’ve caught such an exception. I would only use it to flag a problem to the user and then exit the program.

1 Like

Thank you again for the suggestion. I will attempt to implement the “HandleProcessCorruptedStateExceptionsAttribute” and report back here.

Ideally what I’d want to do when a corrupt part comes along and crashes Rhino.Inside, is to exit and restart the program, skipping and logging the corrupt part. It would be 100% a viable option to just skip over issues like this and continue the batch process with a new Rhino program session.

I agree, though the ultimate solution is for @dale to know about the problematic file and to allow the McNeel team to fix this.

1 Like

Got some goodish news!

Your suggestion about the “HandleProcessCorruptedStateExceptions” worked! I actually ended up reading a lot on the topic here too (for future views of this thread) - https://docs.microsoft.com/en-us/archive/msdn-magazine/2009/february/clr-inside-out-handling-corrupted-state-exceptions#id0070035

With your input, I’m one step closer to realizing my goal for this file processor console application! :slight_smile:

The final stretch that I could also use your input on, would be how to recursively initiate the batch operation again with a new Rhino session, after the AccessViolation is caught. So far I’m able to…

  • Start the console application to loop over an array of sldprt file paths.
  • Catch the AV exception now for some control
  • Remove from the array of sldprt files the “corrupt” sldprt and all those that are before it (completed)

From my Python background, I want to recursively call “ConvertStuff” method again after catching the exception, with the updated list as an argument, BUT… attempting to do so blows everything up with another exception -

Unhandled Exception: System.Runtime.InteropServices.COMException: Error HRESULT E_FAIL has been returned from a call to a COM component.

I know you’ve already helped me out a lot, BUT… If you have any pointers on best practices of how to “reset” or “restart” a new rhino session from within the same console application, I would be grateful. I’ve tried messing around with RhinoApp.Exit(), but that doesn’t seem to work in this application.

Below is the modified C# for reference. Thank you again for taking the time for your previous and hopefully future input!

using System;
using System.IO;

using Rhino.Runtime.InProcess;
using Rhino.Geometry;
using Rhino;
using System.Security.Permissions;
using System.Runtime.ExceptionServices;
using Microsoft.CSharp;
using System.Linq;

namespace Convert
{
    class Program
    {
    #region Program static constructor
    static Program()
    {
        RhinoInside.Resolver.Initialize();
    }
    #endregion

    // Use STAThread for this app as we are starting Rhino in a mode that does actually
    // include user interface (we just never show the main window). This allows for things
    // like RhinoApp().RunScript to properly run.
        [System.STAThread]
        static void Main(string[] args)
        {

            string rPath = args[0].Replace("/", "\\");
            string[] filePaths = Directory.GetFiles(rPath, "*.SLDPRT");
            ConvertStuff(filePaths);
        }
        [HandleProcessCorruptedStateExceptions]
        static void ConvertStuff(string[] filePaths)
        {
            string CurPrt = "none";
            int ret = 0;
            try
            {
                using (new RhinoCore(new string[] { "/NOSPLASH" }, WindowStyle.Hidden))
                {
                    var doc = RhinoDoc.Create("Small Objects - Meters");
                   
                    for(int i=0; i<filePaths.Length; i++)
                    {
                        CurPrt = filePaths[i];
                        ret = i;
                        string filename = Path.GetFileNameWithoutExtension(CurPrt);
                        string output3dm = Path.ChangeExtension(CurPrt, ".3dm");
                        
                        string importScript = string.Format("-_Import \"{0}\" _Enter -_Export Version=4 \"{1}\" _Enter", CurPrt, output3dm);
                        bool r = RhinoApp.RunScript(importScript, false);
                        Console.WriteLine(output3dm);

                        foreach (var obj in doc.Objects)
                            doc.Objects.Purge(obj.RuntimeSerialNumber);
                    }
                    RhinoApp.RunScript("-_Exit No", false);
                }
            }
            catch (AccessViolationException ex)
            {
                // removing entry range of and before the part that thre the AccessViolationException
                var tmp = filePaths.ToList();
                tmp.RemoveRange(0, filePaths.Length-ret+1);
                filePaths = tmp.ToArray();
                ConvertStuff(filePaths); // THROWS System.Runtime.InteropServices.COMException
            }
        }
    }
}

You will need to exit your program, because its state is corrupted. You may (may!) succeed in moving the offending file before trying again, but other than that like I said before, you are on thin ice after catching the AV and cannot expect anything to work properly anymore.

1 Like

That’s a bummer, but thank you for the confirmation. This console app is being initiated by python. If a session goes south, I should be able to pass back an index of the file array or the file path of the bad file and restart another sub process from there.

Hey, @menno, thank you for your patience and guidance on this issue. Not once were you short or condescending in your responses to my questions. Very much appreciate the assistance. Cheers!

1 Like

And @dale, let me know if you need any additional information from me to help you troubleshoot this on the Rhino development end.

1 Like

Thanks for your kind words, I try to help like I want to be (and was) helped myself :slightly_smiling_face:

One thing I was still thinking of, if you’re running this from python: you can set the exit code to something meaningful using Environment.Exit(int exitCode), then use that code in Python to figure out if things were OK or not. For example, and exit code of 0 is typically used as “Success”, and non-zero exit-codes indicate failure. You could for example pass the (array_index + 1) as exit-code to signal where things went wrong (adding one is needed to also be able to catch an error in the first array entry)

Edit: I’m going to try my hand at developing a actual rhp plugin as opposed to using Rhino.Inside to see if I can get around these issues. Feel free to still provide any helpful input in the meantime.

@menno - The work around you suggested got me pretty far with Python. When I hit the “AccessViolationException” I was able to push exit data and start where I left off in Python. However… I’m now trying to get things fully functioning in only C# without any Python and using a single console app.

Everything seems to be working as expected, but I’m getting the Rhino “Licensing” window when I try to load another fresh session after a “AccessViolationException”. The license query puts a hard pause on the Console App.

So two questions:

  1. After the “AccessViolationException” is caught, what steps can I take to kill the active Rhino session to return the license?
  2. What should I be doing to re-initialize a new session of RhinoInside after the exception recovery?. Do I need to run RhinoInside.Resolver.Initialize(); again with anything else?

If I can get past this licensing query, I’m hopeful this might work. I’m running node-locked license. See below for a more technical overview of what I’m doing.

Thank you for your help and time.

CODE OUTLINE:

        public void ProcessFileQueue(string outputExt = ".3dm")
        {
            int c = 1;
            int count = 0;
            while (c == 1)
            {
                count++;
                Console.WriteLine(String.Format("------ LOOP {0} ------", count));
                Console.WriteLine(String.Format("----- COUNT {0} ------", _FileQueue.Count));
                c = FileQueueTo3dm();
            }
        }

        private static int FileQueueTo3dm()
        {
            int Index = -1;
            List<FileInfo> FileQueueCopy = _FileQueue;
            try
            {
                using (new RhinoCore(new string[] { "/nosplash", "/safemod" }, WindowStyle.NoWindow))
                {
                    RhinoApp.Closing += OnClosing; // adding subscriber
                    for (int i = 0; i < FileQueueCount; i++)
                    {
                        Index = i;
                        using (var doc = RhinoDoc.CreateHeadless(Path.Combine(Rhino.ApplicationSettings.FileSettings.TemplateFolder, "Small Objects - Meters.3dm")))
                        {
                                // DO PROCESSING WORK HERE
                        }

                    RhinoApp.Exit();
                }
            }
            catch (AccessViolationException) // at times Rhino will fail on import of sldprt and turn into corrupt state. This catches that and allows for some recovery. TODO - Proof out crash recovery
            {
                _FileQueue = FileQueueCopy; // Global FileQueue is updated to reflect progress.
                RhinoApp.Exit();
                return 1; // return of 1 results in this method looping, but with updated FileQueue.
            }
            return 0; // return of 
        }

To be honest, I don’t know how to resolve this, sorry.

All good. I’ve reached out to tech support directly as I think this may be something that needs to be logged with the internal development team at Mcneel. Thank you again for the all other information and troubleshooting you took the time to provide.

Hi Clayton,
I got your email with mention of this thread. I see there are many sldprt files that you sent. Is there a specific file I should be testing with?

Hi Steve and thank you for the reply.

This seems to be an issue when consecutively processing many sldprt files. If you have my project, it should be setup to process all of the sldprt files on debug. I sent so many sldprt files so you could see how batch processing them with my solution causes the exception.

Thank you.

Ok; I’ll try to run the process in the next few days to see if I can repeat the problem.