hexagon logo

PC-Dmis Automation C#

So I retired as of last July, but had all this stuff about PC-Dmis Automation which I've finally got around to uploading. Don't know it it is of any interest, but here it is.

The zip file has to Visual Studio project folders, both written in C#. One folder is a Class Library which has all the code for connecting with and dynamically interacting with PC-Dmis. The second folder is a C# application with uses the Class Library to connect and interact with PC-Dmis.

I also included two documents one explains how to setup the two Projects the other is about the application program.

Rather than trying to anticipate every possible question, the provided documents should be sufficient for someone familiar with visual studio and C#. Otherwise, I'll watch for questions to show up here. I can't promise to answer every question, given the complexity of what the answer might require. But I have taken phone calls and zoom opens up the possibility to demo live.

Good Luck

https://drive.google.com/drive/folders/1EDMe0GvN6TC_0Fcr_7Zt7_UqH-EV_8o2?usp=sharing
  • (kneeling in appreciation)

    Enjoy retirement!
  • So I retired as of last July, but had all this stuff about PC-Dmis Automation which I've finally got around to uploading. Don't know it it is of any interest, but here it is.

    The zip file has to Visual Studio project folders, both written in C#. One folder is a Class Library which has all the code for connecting with and dynamically interacting with PC-Dmis. The second folder is a C# application with uses the Class Library to connect and interact with PC-Dmis.

    I also included two documents one explains how to setup the two Projects the other is about the application program.

    Rather than trying to anticipate every possible question, the provided documents should be sufficient for someone familiar with visual studio and C#. Otherwise, I'll watch for questions to show up here. I can't promise to answer every question, given the complexity of what the answer might require. But I have taken phone calls and zoom opens up the possibility to demo live.

    Good Luck

    https://drive.google.com/drive/folders/1EDMe0GvN6TC_0Fcr_7Zt7_UqH-EV_8o2?usp=sharing


    We had a phone conversation probably about 3yrs ago... still, can't thank you enough! I've gone into things I never thought I was capable of and/or didn't even know existed all thanks to that short conversation and your public work you've shared here. Your story about the work you did at that laboratory and the early beginnings about CAD is just awesome!
  • As a beginner to PCDMIS automation I am so very grateful for your efforts here, !!!!!!
  • Being retired with a little time on my hands at the moment, I thought I would add here a little tutorial on the PC-Dmis library I made available on Google Drive (see my initial comment that started this thread).

    The Google drive has a zip file containing two C# project folders. Folder 'PcdmisLib' is the C# project which is a library of C# objects which enable the connection to PC-Dmis by your C# application. The second folder 'DmisMenu' is an example of a user C# application which uses the library. If you're new to Visual Studio and C# Setting up project folders and a Library and connecting it to your C# application can seem to be complicated, if you are having trouble with this PM me and we can set up a zoom where I can demo live the process. Plan on the demo taking about an hour.

    In this tutorial, I'm just going to walk through how to start PC-Dmis in it's own thread space and enable communication to and from the running PC-Dmis

    Here we go...

    In the application 'DmisMenu' there is a C# sourcefile called PcdmisMain.cs which is actually an extention of the application 'Mainform' class. C# allows the source code for a class to be broken up between multiple files. I purposely created the 'PcdmisMain' file to separate methods related to PC-Dmis from the rest of 'Mainform' class code. You can consider all the code in 'PcdmisMain' to be a part of the 'Mainform' class.

    On startup of the 'DmisMenu' application the 'Mainform' constructor is executed (see the Mainform method in file Mainform.cs).

            public Mainform()
            {
                InitializeComponent();
    
                // Turn off the menu strip till PC-Dmis has started.
                DmisMenuStrip.Enabled = false;
    
                HomeDir = Application.StartupPath;
                xmlFile = HomeDir + @"\DmisMenuData.xml";
    
                // Display the Main Form Title
                this.Text = "Project Action Mananger  Version " + Settings.Default.Version;
    
                // Start Pcdmis
                InitializePcdmis();
    
                // DataSet
                ReadData();
                UpDateProgramList();
                UpdateToolMenu();
            }
    


    The connection with PC-Dmis begins with the call to 'InitializePcdmis()' method, which is found in the PcdmisMain.cs source file. This method connecting to PC-Dmis is done in two parts. First, an instance of the 'PcdmisHelper' class which is in the PcdmisLib library is created. This class will be the object by which all interactions between your C# application and PC-Dmis will take place. This class is sometimes called a 'wrapper' class as it is made to wrap around PC-Dmis and hide the details of communicating with PC-Dmis from your C# application. From the point of view of your C# application the class PcdmisHelper is the running copy of PC-Dmis. Second, after we have an instance of 'PcdmisHelper' class we use this instance to connect to PC-Dmis

    The following two blocks of code from the InitializePcdmis method in the PcdmisMain source file shows how your C# application creates an instance of the PcdmisHelper class, then connects to PC-Dmis.

     1            string PcdmisVersion = Settings.Default.PcdmisVersion;
     2            try
     3            {
     4                CMMsession = new PcdmisHelper(ExternalToInternalVersion(PcdmisVersion));
     5            }
     6            catch (Exception e)
     7            {
     8                String message = e.Message;
     9                MessageBox.Show("Incorrect Version of PCDMIS or PCDMIS not found.",
    10                    "PCDMIS not Found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
    11                return;
    12            }
    


    Line 1: The version of PC-Dmis used by this application is set in the application configuration file. If you happen to have multiple versions of PC-Dmis installed on your PC, the PcdmisHelper class will ensure the connection the connection to correct version of PC-Dmis.

    Line 4: Create an instance of the class PcdmisHelper. As CMMsession is defined as a global variable, in may used anywhere in the Mainform method of your application to interact with the live PC-Dmis session. The ExternalToInternalVersion method is needed because for some reason the version used by external Hexagon documentation is not the same as the version used in the registery when PC-Dmis is installed. To activate PC-Dmis by version number it must be by using internal version number used by the registery. A call to the ExternalToInternalVersion method fixes this and passes to PcdmisHelper the correct version number.

    Now that we have an instance of the PcdmisHelper class, we can now start PC-Dmis and make a connection to the PC-Dmis session. Later in the InitializePcdmis method we use CMMsession to start this process.

     1            try // to startup and connect with PC-Dmis
     2            {
     3                CMMsession.ConnectPCDLRN();
     4            }
     5            catch (NoRegistryException e)
     6            {
     7                String message = e.Message;
     8                MessageBox.Show(message, "Connection Error - Check Help->PCDLRN Info",
     9                MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
    10                return;
    11            }
    


    Line 3: A call is made to the ConnectPCDLRN method in the PcdmisHelper class. (remember CMMsession is an instance of the PcdmisHelp class).

    Lines 5-11: There are a number of reason for which the startup and connection to PC-Dmis may fail. PcdmisHelper helper tries to capture and document the various errors encountered in a text variable the contents of which the C# application, in this case DmisMenu, can display.

    We now move to the PcdmisHelper.cs source file in the Library to follow the process of starting and connecting to PC-Dmis.

     1        /// <summary>
     2        /// This method starts the PCDMIS/PCDLRN CMM session in the background and
     3        /// establishes a connection with the running PCDLRN session. The session is
     4        /// running on a thread separate from the user DotNet application.
     5        /// </summary>
     6        public void ConnectPCDLRN()
     7        {
     8            if (!IsSessionActive)
     9            {
    10                Func<Boolean> method = Connect;
    11                method.BeginInvoke(ConnectionDone, method);
    12            }
    13            else
    14            {
    15                .
    16                . Code removed for brevity.
    17                .
    18            }
    19        }
    


    Line 8: A global boolean variable is used to track whether or not we have already started PC-Dmis.

    Line 10: Define a function variable that points to method called 'Connect' that returns a boolean value (we'll look at method 'Connect' next).

    Line 11: Use the system method to invoke a new thread, execute the 'method' function (which points to the method 'Connect'), then execute the method ConnectionDone. By using the BeginInvoke method our application doesn't 'lockup' when PC-Dmis is busy doing something. Both the C# application and PC-Dmis are running in their own thread and appear to be active and running at the same time.

    After the new thread is created by the system method, the 'Connect' method is executed as follows.

     1        /* execute this method to make connection to the PCDLRN. */
     2        private Boolean Connect()
     3        {
     4            Type comType;
     5            Object comObj;
    
     6            try
     7            {
     8                pcdSession = null;
     9                // Make sure the PCDLRN process is not already running.
    10                Process[] PCDLRN_List = Process.GetProcessesByName("PCDLRN");
    11                foreach (Process exe in PCDLRN_List)
    12                {
    13                    exe.Kill();
    14                }
    
    15                //PCDMIS_CLSID = getCLSID_RegisterValue();
    16                comType = Type.GetTypeFromCLSID(Guid.Parse(PCDMIS_CLSID), true);
    17                comObj = Activator.CreateInstance(comType);
    18                pcdSession = comObj as PCDLRN.Application;
    19                return IsSessionActive;
    20            }
    21            catch (Exception e)
    22            {
    23                ExceptionMessages = ExceptionMessages + "Problems starting PCDLRN CLSID " +
    24                PCDMIS_CLSID + "\n" + e.Message;
    25            }
    26            return false;
    27        }
    


    Lines 9-14: Make sure any existing running PC-Dmis processes are killed.

    Lines 16-18: This is the proper way of launching a windows program using the registry.

    Line 16: From the registry we get the 'comType' for a perticular version of PC-Dmis from the registry.

    Line 17: This the line that actually causes PC-Dmis to startup, when this happens the operator will see PC-Dmis screen momentarily appear on the monitor then disappear into the background. The Activator method returns the activated PC-Dmis session as an 'object'.

    Line 18: To use the 'object' return in Line 17, we need to recast the object as a PCDLRN.Application object. From here on the PcdmisHelper class can use pcdSession as a live PC-Dmis session.

    Recap

    After the above executed code, we have a C# application with the global variable 'CMMsession' which represents an instance of the wrapper class PcdmisHelper. The application will use this wrapper for all interactions with a live session of PC-Dmis. Within the wrapper we have the global variable 'pcdSession' which represents the PC-Dmis class PCDLRN.Application. The wrapper class will use this variable for all direct interactions with the live session of PC-Dmis.
  • Thanks for sharing your knowledge.

    May you enjoy a long and happy retirement.
  • In this tutorial we look at how our C# application can use the PC-Dmis wrapper to interact with PC-Dmis.

    In the previous tutorial we finished with a global variable in the C# DmisMenu application called 'CMMsession' which is an instance of 'PcdmisHelper' wrapper. In the wrapper, we have another global variable called 'pcdSession' which is an instance of the PCDLRN.Application class.

    The variable 'pcdSession' represents the running PC-Dmis application.
    The variable 'CMMSession' represents the instance of the PcdmisHelper wrapper class.

    To demonstrate an interaction between the C# application and PC-Dmis we'll set up the application to cause PC-Dmis to perform the simple action of becoming visible and invisible to the user.

    We first consider what method is available in the PCDLRN to cause PC-Dmis to be visible. By looking at the PC-Dmis programing documentation we find under the PC-Dmis Object Library - Application Object members - Public Properties, a property call 'Visible'.



    From the documentation we can see that this a Boolean property of the 'Application' object and that is both readable and writable. By setting it to true or false we can make PC-Dmis visible and non-visible.

    Using this information we add the following code to the PcdmisHelper class.

    1        /// <summary>
    2        /// Make the Pcdmis session visable.
    3        /// </summary>
    4        public bool Visible
    5        {
    6            get { if (pcdSession != null) return pcdSession.Visible; return false; }
    7            set { if (pcdSession != null) pcdSession.Visible = value; }
    8        }
    


    In C# this method is known as a class property which has a getter and a setter.

    Line 4: the name of this PcdmisHelper class property is 'Visible'

    Line 6: This is the getter method for the property for when this property is used on the right side of an assignment statement (we'll see this later in the tutorial).
    The code between the curly brackets uses the global variable pcdSession (which represents an instance of PCDLRN.Application object) to access and return the Boolean value of the member property 'Visible' of the actively running PC-Dmis application. If 'pcdSession' for some reason happens to be null, meaning it's not connected to a running copy of PC-Dmis, it will return a default value of FALSE.

    Line 7: This is the setter method for the property for when this property is used on the left side of an assignment statement. (we'll see this later in the tutorial).
    The code between the brackets assigns the Boolean value used on the right side of an assignment statement to the PCDLRN.Application member property 'Visible'. This will immediately cause the PC-Dmis Window to appear or disappear to the user.

    Now that we have added this method to the PcdmisHelper wrapper, let's see how to use this new wrapper method in the 'DmisMenu' application.

    On the 'MainForm' form window we added a button named 'btnPcdmisWindow'. Set the text label property of the button to 'View Pcdmis Window'. Set the 'Click' event of the button to 'btnPcdmisWindow_Click'

    In Mainform.cs we add the following event handler method.

    1        private void btnPcdmisWindow_Click(object sender, EventArgs e)
    2        {
    3            if (CMMsession.Visible == true)
    4            {
    5                CMMsession.Visible = false;
    6                return;
    7            }
    
    9            CMMsession.Visible = true;
            }
    


    This method handles the click event when the user clicks on the 'View Pcdmis Window' button. The method uses the PcdmisHelper wrapper object CMMsession to both GET and SET the Visible property of the wrapper.

    Line 3: Use the Visible property of the wrapper to access and get the value of the running PCDLRN.Application (pcdSession) 'Visible' member.

    Line 5: Use the Visible property of the wrapper to set the value of the running PCDLRN.Application 'Visible member to false if the current value of this member is true.

    Line 9: Otherwise, if the current value of the PCDLRN.Application 'Visible' member is false, set it to true.

    This concludes the tutorial on using the PcdmisHelper wrapper to control the active PC-Dmis program.

    At this point a legitimate question would be; Why use a wrapper in the first place?

    For several reasons:
    1. To hide the details and complexity of working with the PCDLRN objects. For example, consider the following code which handles the creation of a PC-Dmis subprogram file.

            /// <summary> Opens a new Pcdmis subprogram </summary>
            /// <remarks>
            /// The opened subprogram is ready to have assignments, tolerances and
            /// points written into it by the user DotNet application.
            /// </remarks>
            /// <param name="subName">Name of subprogram without path or extension.</param>
            /// <returns>
            /// Returns a PcdmisSubProgram object that has methods for writing to
            /// the Pcdmis subprogram.
            /// </returns>
            public PcdmisSubProgram CreateSubroutineProgram(string subName)
            {
                if (SessionIsAvailable)
                {
                    try
                    {
                        this.CloseAll();
                    }
                    catch (Exception ex)
                    {
                        ExceptionMessages = ExceptionMessages + ex.Message;
                    }
    
                    // Make sure the original is deleted before initializing a new subprogram.
                    PurgeSubProgram(subName);
    
                    pcdPartProgram = pcdPartPrograms.Add(AppProgSubrName(subName), PCDLRN.UNITTYPE.MM,
                        pcdDefaultMachine, DefaultProbeName);
    
                    return new PcdmisSubProgram(pcdPartProgram, subName);
                }
    
                return null;
            }
    


    From the user application point of view, creating a PC-Dmis subprogram file only requires calling the wrapper method 'CreateSubroutineProgram' with the subprogram name as a parameter. The wrapper handles all the house keeping required before creating a PC-Dmis subprogram. Obtains the correct path location for the subprogram file, then sets up the correct parameter list for creating an instance of a PCDLRN.PartProgram that represents a PC-Dmis subprogram. The wrapper also wraps the PCDLRN.PartProgram with a wrapper (PcdmisSubprogram.cs source code) that has convenient methods for adding PC-Dmis assignment commands to the subprogram in their correct PC-Dmis syntax.

    This achieves the goal of writing once, reusing many.
  • Thank you. I have subscribed. Enjoy retirement.
  • I received some messages with several questions on Visual Studio regarding setting up C# for writing programs for PC-Dmis, so I created this long comment that hopefully may answer some of those questions. This is in two parts, this part is mostly about setting up a bare bones application that connects with PC-Dmis. The second part has the actual code for this bare bones project. This bare bones project does not use any code from the google drive in my first comment above. It just has the bare minimum code for connecting to PC-Dmis in a separate thread.

    Good Luck

    Here are the steps for creating a barebones C# application that connects to PC-Dmis. This is not really what I consider a best practice form of writing C# code, but it does present a simplified code view of the process. In this demo I'm using the Community 2019 Edition of Visual Studio (it's free). Other version are okay, the interface on startup may differ but all the same tasks for creating a new project are the same, just located on different menus.

    I start Visual Studio and select 'Create a new project'.



    Select 'Windows Forms App (.NET Framework)'



    If you're new to C# use the same Project name and Solution name (uncheck the 'Place solution and project in the same directory' box if checked) that I use in the screen shot below. Under 'Framework', it doesn't generally matter which framework version you choose as long as use use the same version for all your PC-Dmis C# programming apps and libraries. The later versions may have C# code features not available in earlier versions. I've been using 4.5 successfully for the past couple of years.



    We now add the reference to the PC-Dmis COM file. On the right side of the VS window is the 'Solution Explorer'.. Right-click on 'References' and select 'Add References'.

    In the 'Reference Manager' pop-up menu (see next image below), on the left side select 'COM'. Then in the center pane, scroll down to a 'PC-DMIS ####.# Object Library' entry. Select that line ( if you have more than one version of PC-Dmis installed, pick one, it doesn't matter which). After selecting the line, be sure to also click on the check box to the left of that line. Now click the 'OK' button on bottom right.



    The 'Solution Explorer' should look like the following image. Note the addition of 'PCDLRN' to the list under 'References'.



    Next we create the User interface.
  • Now we add some components to the application interface or form that will be used with our PC-DMIS code. Currently, the application user form is empty. To this form we add two buttons and a text box from the 'Toolbox' tab on the right edge of the VS window. Place one button over the other and add the textbox to the right of the top button. Should look like the next image (I've reduced the size of the Form so it may look smaller than yours).

    {"data-align":"none","data-size":"full","data-tempid":"temp_21511_1635864803436_894"}

    Now I'm going to change the name of each component and the text on each button.

    Left-click on the top button, this is the button we will use to connect to PC-Dmis. On the right side of the VS window, below the 'Solution Explorer' is the 'Properties' panel. Scroll the properties list to the top and change the '(name)' property from 'button1' to 'btnConnect'. Now scroll down to the bottom and change the 'Text' property from 'button' to 'Connect PC-Dmis'.

    Left-click on the bottom button and change it's name to 'btnViewPcdmis' and the 'Text' property to 'View PC-Dmis Window'

    Left-click on the Textbox and change it's name to 'txbStatus'.

    Note that I added btn or txb to the name of each component, this is just a nomenclature I use to help me remember that the newly named component is either a button (btn) or TextBox (txb).

    The purpose of each of these components are: Top button is used to connect to PC-Dmis. The Textbox is colored Red if PC-Dmis is not connected, green if it is. The bottom button will be used as a simple test to prove that PC-Dmis is actually connected. By clicking on this button we can view the PC-Dmis window or hide it in the background.

    With the changes made above the user form looks like the following image. I've stretched out the width of the buttons so that the full labels could be seen.

    {"data-align":"none","data-size":"full","data-tempid":"temp_21512_1635864821462_743"}

    And we're ready to add some code.