Need help with a freezing winform for ArcMap

547
1
12-09-2018 02:38 AM
AndersJensen3
New Contributor

I'm helping a colleague debug a tool he is creating for ArcMap. It is used to create Replicas, and is done through selecting some different inputs from dropdowns in a winform.

The issue is that when running all the code on the UI-thread, our ui freezes. This is what he wants me to solve. The code causing this is the following, the relevant code is just the button1_Click_1() method, but i provided the second class for context:

public partial class FrmReplicaAdmin : Form
 {
 private void button1_Click_1(object sender, EventArgs e)
 {
 
 DataConnectionConfig selectedDatabase = cmboxDatabase.SelectedItem as DataConnectionConfig;
 
 if (selectedDatabase.PlWorkspace == null)
 {
 statusLabel.Text = "Could not open GeoDatabase (PL)";
 return;
 }
 
 if (selectedDatabase.DataWorkspace == null)
 {
 statusLabel.Text = "Could not open GeoDatabase (NIS)";
 return;
 }
 
 int scaleBand = int.Parse(cmboxScale.SelectedItem.ToString());
 string gridName = cmboxGridNr.SelectedItem as string;
 
 IGeometry shapeOfSelectedAOI = getSelectedPolygon(gridName, scaleBand, selectedDatabase);
 
 (ArcMap.Application as IMxApplication2).PauseDrawing = true;
 
 replica.CheckOutReplica(selectedDatabase.PlWorkspace, selectedDatabase.DataWorkspace, selectedDatabase.TemplateGdb, gridName, scaleBand, shapeOfSelectedAOI, selectedDatabase.DatasetName);
 
 (ArcMap.Application as IMxApplication2).PauseDrawing = false;
 
 }
 }
 
 public class Replica
 {
 public void CheckOutReplica(IWorkspace plWorkspace, IWorkspace parentWorkspace, string pathToDatabaseTemplate, string gridName, int scaleBand, IGeometry shapeOfSelectedAOI, string datasetName = "NIS.Nautical")
 {
 try
 {
 string replicaName = string.Format("{0}_{1}_r", Environment.UserName, gridName);
 string versionName = string.Format("{0}_{1}", Environment.UserName, gridName);
 
 string pathToLocalChildDatabase = System.IO.Path.Combine(ReplicationPath, $"{replicaName}.gdb");
 
 Directory.CreateDirectory(pathToLocalChildDatabase);
 
 foreach (string newPath in Directory.GetFiles(pathToDatabaseTemplate, "*.*", SearchOption.AllDirectories))
 File.Copy(newPath, newPath.Replace(pathToDatabaseTemplate, pathToLocalChildDatabase), true);
 
 IWorkspace childWorkspace = OpenWorkspace(pathToLocalChildDatabase);
 
 // Create Version in ParentDatabase
 IEnumVersionInfo versionEnum = (parentWorkspace as IVersionedWorkspace).Versions;
 versionEnum.Reset();
 for (IVersionInfo versionInfo = versionEnum.Next(); versionInfo != null; versionInfo = versionEnum.Next())
 {
 if (versionInfo.VersionName.EndsWith(versionName))
 {
 System.Windows.Forms.MessageBox.Show("A version named '" + versionName + "' has already been created", "map...", System.Windows.Forms.MessageBoxButtons.OK);
 return;
 }
 }
 Marshal.ReleaseComObject(versionEnum);
 
 IVersion newVersion = (parentWorkspace as IVersionedWorkspace).DefaultVersion.CreateVersion(versionName);
 newVersion.Access = esriVersionAccess.esriVersionAccessPublic;
 
 string defQuery = "((IS_CONFLATE=1 AND PLTS_COMP_SCALE >= " + scaleBand + ") OR ((IS_CONFLATE=0 OR IS_CONFLATE IS NULL) AND PLTS_COMP_SCALE = " + scaleBand + "))";
 ReplicateData(parentWorkspace, newVersion.VersionInfo, replicaName, shapeOfSelectedAOI, childWorkspace, defQuery, datasetName);
 
 // Update map. Show replica data
 ILayerFile nauticalLyrFile = new LayerFileClass();
 nauticalLyrFile.Open(ReplicationPath + @"\Nautical.lyr");
 
 AddDataToMap((ArcMap.Application.Document as IMxDocument), childWorkspace as IFeatureWorkspace, nauticalLyrFile, gridName, datasetName);
 (ArcMap.Application.Document as IMxDocument).ActiveView.Extent = shapeOfSelectedAOI.Envelope;
 
 Marshal.ReleaseComObject(childWorkspace);
 }
 catch (Exception err)
 {
 System.Windows.Forms.MessageBox.Show($"Unexpected error. {Environment.NewLine}{err.Message}", "map...", System.Windows.Forms.MessageBoxButtons.OK);
 }
 }
 }
 
 
 private void ReplicateData(IWorkspace parentWorkspace, IVersionInfo versionInfo, string replicaName, IGeometry area, IWorkspace childWorkspace, string definitionQuery, string featureDataset)
 {
 if (childWorkspace == null)
 throw new ArgumentNullException("Child workspace is null.");
 if (parentWorkspace == null)
 throw new ArgumentNullException("Parent workspace is null.");
 if (versionInfo == null)
 throw new ArgumentNullException("Version name is null.");
 if (string.IsNullOrEmpty(replicaName))
 throw new ArgumentNullException("Replica name is null.");
 if (area == null)
 throw new ArgumentNullException("Area geometry is null.");
 
 IVersion oVersion = (parentWorkspace as IVersionedWorkspace).FindVersion(versionInfo.VersionName);
 IWorkspace sdeVersionWorkspace = oVersion as IWorkspace;
 
 IGeoDataServer parentGds = InitGeoDataServer(sdeVersionWorkspace),
 childGds = InitGeoDataServer(childWorkspace);
 
 CreateFeatureDatasetReplica(parentGds, childGds, versionInfo, replicaName, parentWorkspace, childWorkspace, area, definitionQuery, featureDataset);
 
 Marshal.ReleaseComObject(parentGds);
 Marshal.ReleaseComObject(childGds);
 }
 
 //Function to create the replica, based on this link http://help.arcgis.com/en/sdk/10.0/arcobjects_net/conceptualhelp/index.html#//0001000003r5000000 
 private void CreateFeatureDatasetReplica(IGeoDataServer parentGDS, IGeoDataServer childGDS, IVersionInfo versionInfo, string replicaName, IWorkspace parentWorkspace, IWorkspace childWorkspace, IGeometry geometry, string definitionQuery, string featureDatasetName)
 {
 IList<string> existingReplicas = ReadExistingReplicas(parentGDS);
 if (existingReplicas.Contains(replicaName.ToUpper()))
 {
 throw new Exception("A replica with the following name has already been created: " + replicaName);
 }
 
 IEnumDataset datasets = null;
 if (!string.IsNullOrEmpty(featureDatasetName))
 {
 IEnumDataset featureDatasets = parentWorkspace.get_Datasets(esriDatasetType.esriDTFeatureDataset);
 IFeatureDataset featureDataset;
 while ((featureDataset = featureDatasets.Next() as IFeatureDataset) != null)
 {
 if (featureDataset.Name == featureDatasetName)
 {
 datasets = featureDataset.Subsets;
 break;
 }
 }
 if (datasets == null)
 throw new Exception("Didn't find FeatureDataset " + featureDatasetName + " in the db");
 }
 else
 {
 datasets = parentWorkspace.get_Datasets(esriDatasetType.esriDTFeatureClass);
 }
 
 IGPReplicaDatasets gpReplicaDatasets = new GPReplicaDatasetsClass();
 IDataset dataset;
 while ((dataset = datasets.Next()) != null)
 {
 //temporary workaround to not include a view that is on the feature classes :^)
 if (dataset.Name.Contains("VW_") || dataset.Name.Contains("_EVW"))
 continue;
 
 if (m_ListExcludedTables.Contains(dataset.Name.Substring(dataset.Name.LastIndexOf(".") + 1).ToUpper()))
 continue;
 
 if (!(childWorkspace as IWorkspace2).NameExists[dataset.Type, dataset.Name.Substring(dataset.Name.LastIndexOf(".") + 1)])
 continue;
 
 IGPReplicaDataset gpReplicaDataset = new GPReplicaDatasetClass();
 gpReplicaDataset.DatasetType = dataset.Type;
 gpReplicaDataset.Name = dataset.Name.ToUpper();
 gpReplicaDataset.IsPrivate = false;
 gpReplicaDataset.UseGeometry = true;
 gpReplicaDataset.RowsType = esriRowsType.esriRowsTypeFilter;
 
 if ((dataset as ITable).Fields.FindField("PLTS_COMP_SCALE") != -1)
 gpReplicaDataset.DefQuery = definitionQuery; //DefQuery here
 else
 gpReplicaDataset.DefQuery = "";
 
 gpReplicaDatasets.Add(gpReplicaDataset);
 }
 
 IGPReplicaDescription gpReplicaDesc = new GPReplicaDescriptionClass();
 gpReplicaDesc.QueryGeometry = geometry;
 gpReplicaDesc.SpatialRelation = esriSpatialRelEnum.esriSpatialRelIntersects;
 gpReplicaDesc.ModelType = esriReplicaModelType.esriModelTypeSimple;
 gpReplicaDesc.SingleGeneration = true;
 gpReplicaDesc.ReplicaDatasets = gpReplicaDatasets;
 
 IGPReplicaOptions2 replicaOptions = new GPReplicaOptionsClass();
 replicaOptions.AccessType = esriReplicaAccessType.esriReplicaAccessNone;
 replicaOptions.RegisterReplicaOnly = true;
 
 ExtractData(datasets, childWorkspace, geometry, definitionQuery);
 
 IReplicationAgent replicationAgent = new ReplicationAgentClass();
 replicationAgent.CreateReplica(versionInfo.VersionName, parentGDS, childGDS, replicaName, gpReplicaDesc, replicaOptions);
 }
 }

For fixing the UI freeze i made the following changes to the button1_Click_1() method:

public partial class FrmReplicaAdmin : Form
 {
 private void button1_Click_1(object sender, EventArgs e)
 {
 
 DataConnectionConfig selectedDatabase = cmboxDatabase.SelectedItem as DataConnectionConfig;
 
 if (selectedDatabase.PlWorkspace == null)
 {
 statusLabel.Text = "Could not open GeoDatabase (PL)";
 return;
 }
 
 if (selectedDatabase.DataWorkspace == null)
 {
 statusLabel.Text = "Could not open GeoDatabase (NIS)";
 return;
 }
 
 int scaleBand = int.Parse(cmboxScale.SelectedItem.ToString());
 string gridName = cmboxGridNr.SelectedItem as string;
 
 IGeometry shapeOfSelectedAOI = getSelectedPolygon(gridName, scaleBand, selectedDatabase);
 
 // adding inputs to list i can pass onto the backgroundWorker
 List<object> arguments = new List<object>();
 arguments.Add(selectedDatabase.PlWorkspace);
 arguments.Add(selectedDatabase.DataWorkspace);
 arguments.Add(selectedDatabase.TemplateGdb);
 arguments.Add(gridName);
 arguments.Add(scaleBand);
 arguments.Add(shapeOfSelectedAOI);
 arguments.Add(selectedDatabase.DatasetName);
 
 backgroundWorker1.RunWorkerAsync(arguments);
 
 // starting progress bar
 progressBarReplica.Visible = true;
 lblReplica.Text = "Checking out replica...";
 }
 
 
 private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
 {
 List<object> genericlist = e.Argument as List<object>;
 
 IWorkspace ws = (IWorkspace)genericlist[0];
 IWorkspace pWs = (IWorkspace)genericlist[1];
 string pathToDbTemplate = genericlist[2].ToString();
 string gName = genericlist[3].ToString();
 int sBand = (int)genericlist[4];
 IGeometry shape = (IGeometry)genericlist[5];
 string dsName = genericlist[6].ToString();
 
 (ArcMap.Application as IMxApplication2).PauseDrawing = true;
 
 replica.CheckOutReplica(ws, pWs, pathToDbTemplate, gName, sBand, shape, dsName);
 
 (ArcMap.Application as IMxApplication2).PauseDrawing = false;
 }
 }

This is causing a: "RPC_E_SERVERFAULT(0X80010105)", but the UI isn't freezing anymore. My guess is that it's because i'm initiating the database in the first thread, and then using it in the second. I also sort of get that i can't use a backgroundWorker due to the entire STA and COM-object things with ArcGis, but i'm still not getting all this 100%.

Any help of a possible solution to making my UI responsive, or at least showing some sort of progressbar while the task is running would be nice. The entire process can take a few minutes at times, and the program feels like it's crashed meanwhile due to the freeze.

Edit: I'm referring to ArcMap, forgot to mention that.

0 Kudos
1 Reply
TobiasVenable
New Contributor

The ArcObjects SDK you're using is STA (single threaded apartment) and so you shouldn't access an ArcObject/COM object created in one thread from another; this violates the apartment rule.

Here's a good link explaining how to accomplish multithreading with ArcObjects. Essentially your background thread will need to instantiate all the ArcObjects it needs first (don't pass references to ArcObjects from your UI thread). You can pass simple types like strings (e.g., database connection string you want to create the replica on) to your background thread.

Another way to keep your UI responsive would be to create a TrackCancel. When you check it to continue, the UI is given some processing time. You may still experience periodic UI freezes when making an ArcObject call which takes some time (such as registering the replica), but checking to continue throughout helps mitigate it.

ESRI.ArcGIS.esriSystem.ITrackCancel ipTrackCancel = new ESRI.ArcGIS.Display.CancelTrackerClass();
ESRI.ArcGIS.Framework.IProgressDialogFactory ipProgressDlgFactory = new ESRI.ArcGIS.Framework.ProgressDialogFactoryClass();
ESRI.ArcGIS.esriSystem.IStepProgressor ipStepProgressor = ipProgressDlgFactory.Create(ipTrackCancel, Application.hWnd);
ipStepProgressor.MaxRange = ...; // How many steps do you want in your process until completion
ESRI.ArcGIS.Framework.IProgressDialog2 ipProgressDlg2 = ipStepProgressor as ESRI.ArcGIS.Framework.IProgressDialog2;
ipProgressDlg2.Title = ...;
ipProgressDlg2.Description = ...;
ipProgressDlg2.ShowDialog();

...

if (!ipTrackCancel.Continue()) { return; }

ipStepProgressor.Message = ...

ipStepProgressor.Position++; // One step closer to MaxRange

0 Kudos