AnsweredAssumed Answered

Need help with a freezing winform for ArcMap

Question asked by anjen96 on Dec 9, 2018
Latest reply on Jun 27, 2019 by TVenable-esristaff

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.

Outcomes