multithreadingc#-4.0solidworks

Is it possible to call a method that takes this.Handle as an argument inside a background worker in C#


I am working on a stand-alone WinForm program in C# that uses the Solidworks EPDM api. The program takes a top level assembly and finds all the referenced and referencing files in the assembly. e.g. all sub-assemblies, part files, and drawings. The program then checks out all the files from EPDM, updates the data cards, and checks in all the files back to the EPDM.

I have successfully implemented the portion of the code that finds all the referenced and referencing files and updates the data card information using a background worker. This portion of the code does not require access the the UI thread. I would like to be able to add the code that checks out the files and checks them back in within a background worker. The problem is that the methods used to do the checkout and check-in take this.Handle as an argument. I know that accessing the UI thread from within a background worker will throw a cross thread exception. The code does not access any of the UI controls. It only need access to this.Handle. Is it possible to pass this.Handle to a background worker in a thread safe way that will not throw a cross thread exception?

This is my first use of background workers so my knowledge is limited. Below is the code that I would like to run in a background worker.

    private void BatchCheckout(Dictionary<string, string> SelectedFiles)
    {
        try
        {
            IEdmBatchGet batchGetter = (IEdmBatchGet)vault.CreateUtility(EdmUtility.EdmUtil_BatchGet);           
            EdmSelItem[] ppoSelection = new EdmSelItem[SelectedFiles.Count];            
            IEdmFile5 aFile;
            IEdmFolder5 aFolder;
            IEdmFolder5 ppoRetParentFolder;
            IEdmPos5 aPos;
            int i = 0;

            foreach (KeyValuePair<string, string> kvp in SelectedFiles)
            {
                aFile = vault1.GetFileFromPath(kvp.Key, out ppoRetParentFolder);
                aPos = aFile.GetFirstFolderPosition();
                aFolder = aFile.GetNextFolder(aPos);
                ppoSelection[i] = new EdmSelItem();
                ppoSelection[i].mlDocID = aFile.ID;
                ppoSelection[i].mlProjID = aFolder.ID;
                i = i + 1;
            }
            batchGetter.AddSelection((EdmVault5)vault1, ref ppoSelection);
            batchGetter.CreateTree(this.Handle.ToInt32(), (int)EdmGetCmdFlags.Egcf_Lock);
            batchGetter.GetFiles(this.Handle.ToInt32(), null);
        }

        catch (System.Runtime.InteropServices.COMException ex)
        {
            MessageBox.Show("HRESULT = 0x" + ex.ErrorCode.ToString("X") + " " + ex.Message);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message + "\n" + GetStackTrace(ex));
        }
    }

I have been a reader of StackOverflow for many years and have found answers to just about every question I have ever had. This is my first ever question on StackOverflow. I am really hoping that someone will have an answer to this problem.

EDIT:

I have successfully test AndrewK's suggestion and am happy to report that it did work for my batch checkout method. When I run my batch check-in method in a background worker I'm getting the following COM exception:

Unable to cast COM object of type 'System.__ComObject' to interface type 'EPDM.Interop.epdm.IEdmBatchUnlock2'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{F0970446-4CBB-4F0F-BAF5-F9CD2E09A5B3}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

I only get this exception if I run the code from a background worker.

Here is the code from my BatchCheckin method:

    private void BatchCheckin(Dictionary<string, string> SelectedFiles)
    {
        try
        {
            int i = 0;
            IEdmFolder5 ppoRetParentFolder;
            IEdmFile5 aFile;
            IEdmFolder5 aFolder;
            IEdmPos5 aPos;
            EdmSelItem[] ppoSelection = new EdmSelItem[SelectedFiles.Count];
            IEdmBatchUnlock2 batchUnlock;

            foreach (KeyValuePair<string, string> kvp in SelectedFiles)
            {
                aFile = vault5.GetFileFromPath(kvp.Key, out ppoRetParentFolder);
                aPos = aFile.GetFirstFolderPosition();
                aFolder = aFile.GetNextFolder(aPos);
                ppoSelection[i] = new EdmSelItem();
                ppoSelection[i].mlDocID = aFile.ID;
                ppoSelection[i].mlProjID = aFolder.ID;
                i = i + 1;
            }

            batchUnlock = (IEdmBatchUnlock2)vault7.CreateUtility(EdmUtility.EdmUtil_BatchUnlock);
            batchUnlock.AddSelection((EdmVault5)vault5, ref ppoSelection);
            batchUnlock.CreateTree(0, (int)EdmUnlockBuildTreeFlags.Eubtf_ShowCloseAfterCheckinOption + (int)EdmUnlockBuildTreeFlags.Eubtf_MayUnlock);
            batchUnlock.Comment = "Updates";
            batchUnlock.UnlockFiles(0, null);
        }

        catch (System.Runtime.InteropServices.COMException ex)
        {
            MessageBox.Show("HRESULT = 0x" + ex.ErrorCode.ToString("X") + " " + ex.Message);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message + "\n" + GetStackTrace(ex));
        }
    }

I am getting the exception when I make the call to vault7.CreateUtility. The BatchCheckin code is nearly identical to the BatchCheckout. I'm making the same call to vault7.CreateUtility in both methods. The only difference is the EdmUtility flag is set to EdmUtil_BatchUnlock in the BatchCheckin method. Any clue on this one AndrewK?

UPDATE:

I was able to resolve the COM exception by changing batchUpdate from the IEdmBatchUnlock2 interface to the IEdmBatchUnlock interface. Here is the code change:

    private void BatchCheckin(Dictionary<string, string> SelectedFiles)
    {
        int i = 0;
        IEdmFolder5 ppoRetParentFolder;
        IEdmFile5 aFile;
        IEdmFolder5 aFolder;
        IEdmPos5 aPos;
        EdmSelItem[] ppoSelection = new EdmSelItem[SelectedFiles.Count];
        IEdmBatchUnlock batchUnlock = (IEdmBatchUnlock)vault7.CreateUtility(EdmUtility.EdmUtil_BatchUnlock);

        try
        {
            foreach (KeyValuePair<string, string> kvp in SelectedFiles)
            {
                aFile = vault5.GetFileFromPath(kvp.Key, out ppoRetParentFolder);
                aPos = aFile.GetFirstFolderPosition();
                aFolder = aFile.GetNextFolder(aPos);
                ppoSelection[i] = new EdmSelItem();
                ppoSelection[i].mlDocID = aFile.ID;
                ppoSelection[i].mlProjID = aFolder.ID;
                i = i + 1;
            }               
            batchUnlock.AddSelection((EdmVault5)vault5, ref ppoSelection);
            batchUnlock.CreateTree(0, (int)EdmUnlockBuildTreeFlags.Eubtf_ShowCloseAfterCheckinOption + (int)EdmUnlockBuildTreeFlags.Eubtf_MayUnlock);
            batchUnlock.Comment = "Release to Production ECO";
            batchUnlock.UnlockFiles(0, null);
        }

        catch (System.Runtime.InteropServices.COMException ex)
        {
            MessageBox.Show("HRESULT = 0x" + ex.ErrorCode.ToString("X") + " " + ex.Message);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message + "\n" + GetStackTrace(ex));
        }
    }

I am guessing that this is a bug in the IEdmBatchUnlock2 interface. The IEdmBatchUnlock2 will cause a COM exception if called from a background worker but will not cause a COM exception if called from the UI thread. The IEdmBatchUnlock interface will not cause a COM exception when called from a background worker.


Solution

  • Just put a 0 in there for the handle. As long as your code will not require user input, it will work. I do it often.

            batchGetter.AddSelection((EdmVault5)vault1, ref ppoSelection);
            batchGetter.CreateTree(0, (int)EdmGetCmdFlags.Egcf_Lock);
            batchGetter.GetFiles(0, null);