[CONNECT C# COM] Set Workset Custom Properties


We are attempting to write an application that will builds a workset programmatically. We are attempting to automate the process of filling out custom workset properties from our Financial Management System via a COM started ORD session. Using property handlers we can get the property handler for the custom fields but cannot get or set the property. When trying to get the value of a custom field the following exception is thrown: "System.Runtime.InteropServices.COMException: 'Not Available'" is thrown. I can access the build-in properties without a problem.

using MicroStationDGN;
using System;
using System.Windows.Forms;

namespace WorksetPropertySetter
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {

            MicroStationDGN.ApplicationObjectConnector oMSAppConnector = new MicroStationDGN.ApplicationObjectConnector();

            MicroStationDGN.Application oMSApp = oMSAppConnector.Application;
           
            DesignFile dgn = oMSApp.OpenDesignFile(@"C:\Worksets\FDOT\0_WORKSET_TEMPLATE.dgnws");
            PropertyHandler ph = oMSApp.CreatePropertyHandler(dgn);

            var props = ph.GetAccessStrings();

            //look up build in properties
            if (ph.SelectByAccessString("WorkSetName"))
            {
                Object workSetName = ph.GetValue();        
                ph.setValue("WorksetName Value");
            }

            if (ph.SelectByAccessString("WorkSpaceName"))
            {
                Object WorkSpace = ph.GetValue();
            }

			//look up custom properties
            if (ph.SelectByAccessString("ContractNumber")) //succesful
            {
                Object workSetName = ph.GetValue(); //fails with exception
            }

            if (ph.SelectByAccessString("WorkSetDescription")) //succesful
            {
                Object workSetName = ph.GetValue(); //fails with exception

            }
            oMSApp.Quit();
            oMSApp = null;
            oMSAppConnector = null;
        }
    }
}

  • Hi ,

    and I can take a look into this further, though it would be helpful to know if using the .NET APIs is a better option for your solution, or if you need to use the older VBA/COM interface for some specific reasons.

    Thank you,
    Bob and Artur



  • We are trying to complete this task from an application external to MicroStation (thus COM). I've been able to access the properties from an internal addin but when I get to the point of trying to set the properties everything is readonly (plus the complication of trying to access an internal addin from an external application). I can send the skeleton project solution and workset template if needed.

  • Hi Mike,

    If you like to provide your full simplified VBA/COM test case solution we can certainly test with that.  However, like all VBA/COM solutions you should first validate the code running in-process as successful then migrate to out-of-process with a level of confidence.  If the code fails for write in-process then it will do so out-of-process and provide less meaningful error codes/messages. Does your workset modify code work as an in-process VBA? If so, please do provide the simplified (skeleton) test case project for review.

    My (preferred) suggestion would be to keep external VBA/COM code minimal and simple as possible (just execute keyins) and communicate with a custom in-process addin that provides your needed functionality.  Where your custom addin would provide keyins w/arguments like: e.g.

    • FDOTWorkset [Set | Close] <FileSpec>
    • FDOTWorkset <Create | Update | Delete> PropertyName [PropertyType | PropertyValue]

    Thank you,
    Bob



  • Hi ,

    If you still prefer to use the VBA/COM PropertyHandler path, please feel free to provide a test app for us to troubleshoot the exact code conditions you are using.

    If however, you have existing C# code to implement my prior recommendation and can provide key-ins to simplify the potential VBA/COM threshold issues, please feel free to go that route and update us with a reply that this is the path you prefer/choose to resolve the issue.

    Lastly, here is a code snip that may be of help if you (or others) wish to Explore an existing .WorkSet file (.dgnws) and add a few properties to get started. We may add or replace this code snip since we have asked development to review and/or provide any better recommendations.

    public static void DgnWorkSetExplore(string sFileSpec)
    {
        var sb = new System.Text.StringBuilder();
        BDNET.DgnDocument dgnDoc = BDNET.DgnDocument.CreateForLocalFile(sFileSpec);
        DgnFileOwner dgnFileOwner = BDNET.DgnFile.Create(dgnDoc, DgnFileOpenMode.PreferableReadWrite);
        BDNET.DgnFile dgnFile = dgnFileOwner.DgnFile;
        StatusInt errorStatus;
        dgnFile.LoadDgnFile(out errorStatus);
        dgnFile.FillDictionaryModel();
    
        // Display WorkSetInfo - Object Properties
        BDNET.DgnWorkSetInfo wsInfo = DgnWorkSetInfo.ExtractFromDgnFile(dgnFile);
        sb.AppendFormat("Processing File: {0}{1}", sFileSpec, Environment.NewLine);
        sb.AppendFormat("AreAllPropertiesLoaded: {0}{1}", wsInfo.AreAllPropertiesLoaded, Environment.NewLine);
        sb.AppendFormat("ConnectGUID: {0}{1}", wsInfo.ConnectGUID, Environment.NewLine);
        sb.AppendFormat("Description: {0}{1}", wsInfo.Description, Environment.NewLine);
        sb.AppendFormat("Name: {0}{1}", wsInfo.Name, Environment.NewLine);
        sb.AppendFormat("ProjectAssetType: {0}{1}", wsInfo.ProjectAssetType, Environment.NewLine);
        sb.AppendFormat("ProjectIndustry: {0}{1}", wsInfo.ProjectIndustry, Environment.NewLine);
        sb.AppendFormat("ProjectLocation: {0}{1}", wsInfo.ProjectLocation, Environment.NewLine);
        sb.AppendFormat("ProjectName: {0}{1}", wsInfo.ProjectName, Environment.NewLine);
        sb.AppendFormat("ProjectNumber: {0}{1}", wsInfo.ProjectNumber, Environment.NewLine);
        sb.AppendFormat("ProjectStatus: {0}{1}", wsInfo.ProjectStatus, Environment.NewLine);
        sb.AppendFormat("ToString: {0}{1}", wsInfo.ToString(), Environment.NewLine);
        Trace.WriteLine(sb);
        BDNET.NotificationManager.OutputMessage(new NotifyMessageDetails(OutputMessagePriority.Information, "WorkSetInfo - Properties", sb.ToString(), NotifyTextAttributes.None, OutputMessageAlert.None));
    
        // Add Custom Properties
        // NOTE: Re-open Design File to see changes in MicroStation's "Explorer" properties pane.
        wsInfo.AddProperty("MyItem", "MyItem value");
        wsInfo.AddProperty("MyItemDT", DateTime.Now);
        wsInfo.Write(dgnFile);
    
        // Display WorkSetInfo - Custom Properties (via its EC data)
        try
        {
            sb.Clear();
            StandaloneECDInstance lclECInstance = wsInfo.ToECInstance();
            foreach (IECProperty property in lclECInstance.ClassDefinition)
            {
                IECPropertyValue propertyValue = lclECInstance[property.Name];
                sb.AppendFormat("AccessString: {0}, StringValue: {1}{2}", propertyValue.AccessString, propertyValue.StringValue, Environment.NewLine);
            }
            Trace.WriteLine(sb);
            BDNET.NotificationManager.OutputMessage(new NotifyMessageDetails(OutputMessagePriority.Information, "ECInstance - Properties", sb.ToString(), NotifyTextAttributes.None, OutputMessageAlert.None));
        }
        catch (Exception e)
        {
            Trace.WriteLine("Exception: " + e.ToString());
        }
        // Cleanup
        dgnFile.Release();
    }
    
    public static void DgnWorkSetTest(string unparsed)
    {
        string lFileSpec = unparsed;
        if (lFileSpec.Length == 0)
            //lFileSpec = @"C:\ProgramData\Bentley\CONNECT Edition\Configuration\WorkSpaces\Example\WorkSets\MetroStation.dgnws";
            lFileSpec = ConfigurationManager.GetVariable("_USTN_WORKSETDGNWS");
        DgnWorkSetExplore(lFileSpec);
        return;
    }

    HTH,
    Bob and Artur



  • Thanks for the code, I'd already started looking into an addin using C# and have it working. Guess I'll have to pass the data I need in the addin via some type of temporary file from the external application. Only thing I have left now is how can I set the "Sheet Index" name via code?

    Here's my current workflow. Launch external application, copy our workset template folder, workset.cfg, workset.dgnws and rename accordingly. Open the just copied new dgnws file and populate our custom properties. All this is working correctly now. Only thing left is how do I change the "Sheet Index" name? (it shows the name of our workset template, not the new workset). When I create a new workset via the CONNECT GUI the Sheet Index name automatically is updated to match the new workset name.

  • Hi Mike,

    I did a quick search for "Sheet Index Name" on the communities to see if others may have issues (known or otherwise) related to naming/renaming of sheet names.  I found this post where one user appears to be doing a very similar process to what you describe, though possibly addressing things at a higher level to ensure more robust project handling than is currently offered.  Can you take a look to see if the post may help provide you in addressing one or more issues you may be trying to resolve, or let me know if not helpful so we can dig into this deeper?

    Thank you,
    Bob



  • Roberts, thanks for the quick reply. Didn't see anything about Sheet Index Name on the post you mentioned. I have found something in the API that looks interesting. There is a method called Bentley.MstnPlatformNET.Internal.WorkSetClonerOptions, I haven't actually found a method to clone a workset yet (seems the ClonerOptions would be a parameter for a "Clone" method). I'm trying to figure out what the actual parameters are supposed to be used with the ClonerOptions. 

  • Hi Mike,

    DgnWorkSetInfo provides a Clone() method.

    DgnWorkSetInfo wsCopy = wsInfo.Clone();

    With the Internal prefix on WorkSetClonerOptions I am not sure how accessible/supportable it may be.  I will see if I can find any sample code and if needed check with development on it.

    Bob



  • Looking at DgnWorkSetInfo wsCopy = wsInfo.Clone(); I would assume that only clones the WorksetInfo on an existing file to another file (not cloning the complete workset itself).

  • Hi Mike,

    I was unable to find any code snips related to WorkSetClonerOptions other than the implementation code I can provide. Being classified under Internal I am afraid this would be a use at your own risk/caution scenario since it: may change, be removed or completely replaced with new or improved APIs at some point.

    Given that, I do see one WorkSpaceManager consumer of WorkSetClonerOptions: CreateWorkSet. CreateWorkSpace if used calls CreateWorkSet and cloneroptions initiated when providing input of a root folder/directory.

    Bob