2

I have a few functions in a Solidworks Addin which call on a VBA macro (Via the runMacro2 method) a co-worker has been working on for the last few weeks. In his code he calls a Solidworks function which, under certain, unknown conditions, hangs for a long period of time. How long seems to depend upon the size and quantity of bodies in the part. Considering at least one of the functions we want to run this from i automatic, this just wont do.

I have tried using the Thread.Join(int) method (shown below) but it doesnt work. I also tried modifying the code from this answer Close a MessageBox after several seconds with the same results. Is there anything I can do either in C# or VBA to handle a timeout for this without re-writing his entire macro?

    public void runBB()
    {
        Stopwatch testStop = new Stopwatch();
        Thread workerThread = new Thread(bbRun);
        testStop.Start();
        workerThread.Start();
        if (!workerThread.Join(50))
        {
            workerThread.Abort();
            testStop.Stop();
            MessageBox.Show("Unable to generate Bounding Box after " + testStop.ElapsedMilliseconds/1000 + " seconds. Please enter data manually.", "Solidworks Derped Error.");
        }
        return;

    }//Still uses Macro (2-5-16)
    public static void bbRun()
    {
        iSwApp.RunMacro2(macroPath + "BOUNDING_BOX.swp", "test11", "main", 0, out runMacroError);
        return;
    }
Community
  • 1
  • 1
Nick
  • 665
  • 5
  • 12

1 Answers1

2

I was getting this same exact issue with SOLIDWORKS hanging on an open of a file. Almost all reference on SO was that you should never do this, but in this scenario, you either have to close it or wait forever. In C# I created a callWithTimeout method:

    private void callWithTimeout(Action action, int timeoutMilliseconds, String errorText) {
        Thread threadToKill = null;
        Action wrappedAction = () =>
        {
            threadToKill = Thread.CurrentThread;
            action();
        };

        IAsyncResult result = wrappedAction.BeginInvoke(null, null);
        if (result.AsyncWaitHandle.WaitOne(timeoutMilliseconds)) {
            wrappedAction.EndInvoke(result);
        } else {
            threadToKill.Abort();
            throw new TimeoutException(errorText);
        }
    }

Then the code that was hanging put in a block as such:

bool timedOut = false;
try {
    callWithTimeout(delegate() {
        // code that hangs here
    }, 60000, "Operation timed out.  SOLIDWORKS could not open the file.  This file will be processed later.");
} catch (TimeoutException){
    timedOut = true;
} finally {
    if(timedOut) {
        Process[] prs = Process.GetProcesses();
        foreach (Process p in prs) {
            if (p?.ProcessName.Equals("SLDWORKS") ?? false)
                p?.Kill();
        }
    }
}
AndrewK
  • 1,223
  • 2
  • 16
  • 25
  • I tried this, replacing the "// code that hangs here" with "bbRun();", as well as the actual macro call I show above, neither worked. I assume the timeout you have, 60000 is 60000 ms, or 60 seconds, correct? – Nick Feb 11 '16 at 19:39
  • yes 60 seconds...can you step through to see what happens? It should call the block of code using the asyncwaithandle.waitone(). You might not need the part to kill the solidworks process in the finally block. that was what i had to do to get it going again – AndrewK Feb 11 '16 at 19:54
  • Stepping through, it got to this line if 'if(result.AsyncWaitHandle.WaitOne(timeoutMilliseconds))' before it hung again. – Nick Feb 11 '16 at 20:22
  • instead of the `iSwApp.RunMacro2(...)`, can you try a `Thread.Sleep(60001);` see if it kicks out when running code instead of a macro. – AndrewK Feb 11 '16 at 20:29
  • The `Thread.Sleep(60001)` did kick out like it should. I ended up putting the actual line of code which is causing the hang in there `swModel.Extension.Create3DBoundingBox()` and it still hangs under the same conditions, the timeout is making no difference. – Nick Feb 12 '16 at 12:03
  • I feel like this is redundant, but attempt to start the line of code in a separate thread: `new Thread (() => swModel.Extension.Create3DBoundingBox()).Start();` – AndrewK Feb 12 '16 at 17:01
  • `callWithTimeout(delegate () { new Thread(() => swModel.Extension.Create3DBoundingBox()).Start(); }, 10, "Operation timed out. SOLIDWORKS could not open the file. This file will be processed later.");` I ran this, and it just went right through, like it worked. but no Bounding Box in SW. Then I tried running the BB from SW cutlist, and it hangs just the same on my test part. – Nick Feb 12 '16 at 18:06
  • SOLIDWORKS must be hanging so that you cannot even stop the newly created thread. I would recommend using SOLIDWORKS Rx and do a problem capture to see what is going on. – AndrewK Feb 18 '16 at 17:40
  • I ran the problem capture, but am not sure what I am looking for in it. A few more interesting tidbits from this, the function isnt hanging indefinitely, its just taking several minutes to complete. And it seems to be holding up Solidworks in general, as I cant even open a separate instance (Ive been testing in 2015, and I could not open a second instance of 15, nor one of 14) BUT, if I already have a second instance running, I can still work in that one. so it does seem like, whatever that function is doing, it prevents any other Solidworks threads from starting. – Nick Feb 24 '16 at 12:15
  • How are you getting the iSwApp object reference? `iSwApp = (SldWorks.SldWorks)Marshal.GetActiveObject("SldWorks.Application");` or `iSwApp = new SldWorks.SldWorks();` ? I have had more issues when using the GetActiveObject(...) than creating a new instance. – AndrewK Feb 24 '16 at 15:16
  • Heres my iSwApp declaration; `public static ISldWorks iSwApp = null;` And here is where I give it a value; `iSwApp = (ISldWorks)ThisSW;` – Nick Feb 24 '16 at 15:22
  • The `iSwApp = (ISldWorks)ThisSW;` is in a `public bool ConnectToSW(object ThisSW, int cookie)` method. I cant find where it is being called from. I tried to use the `iSwApp = new SldWorks.SldWorks();`, but its giving me an error on the `.SldWorks();` saying the typename does not exist. – Nick Feb 24 '16 at 16:09
  • try just `iSwApp = new SldWorks();` or see if Visual Studio has any hints on how to resolve, just need to get it to point to the right namespace. – AndrewK Feb 24 '16 at 20:34
  • I just tried several variations of that and its a no go. VS Doesnt seem to have any suggestions. I decided to try something, and ran another of my functions through the timeout code (With the timeout set to 10ms), and it worked perfectly. Im starting to think that BoundingBox function of SW's is just trouble. – Nick Feb 25 '16 at 13:19