Debugging a function tool in background processing
Debugging in the background
- Start Visual Studio and open your tool project.
- When you run or build the .NET solution, it creates a dynamic-link library (DLL) that must be registered by the ESRIRegAsm utility. You can add a post-build command to automate the registration process. In the Project properties Build Events section, type the command on the post-build event command line. The following is one example of a post-build command to register with ArcGIS Engine:
"$(ProgramFiles)\Common Files\ArcGIS\bin\esriRegAsm.exe" $(TargetPath) /p:Engine /v:10.1 /s
- Also, in the Project properties Debug section, set ArcMap in the Start external program option by browsing to the ArcMap.exe location. The default location is <Install Directory>\bin\ArcMap.exe.
- Set a breakpoint as necessary for debugging.
- Click Debug and click Start Debugging (or press F5) to run the project. The ArcMap application starts.
- From the ArcMap menu, click Geoprocessing > Geoprocessing Options. Confirm that Enabled is checked to ensure that background processing is enabled if you want to debug in the background, then click OK on the dialog box. This starts the background processes (if not already started).
- In Visual Studio, click Tools, then click Attach to Process. See the following screen shot:
- When you click Attach to Process, the Attach to Process dialog box appears. Under the Available Processes section, there will be two of the background processes (RuntimeLocalServer.exe). Select both of the processes and click Attach. See the following screen shot:
- In ArcMap, right-click the toolbox and select Add, then click Tool to add the function tool to a custom toolbox. The Add Tool dialog box appears. On the Add Tool dialog box, you can see your function tool under a toolbox named after the category you set in your code. See the following screen shot:
- Add the data to ArcMap (if not already added), and run your tool after populating the parameters.
- At the appropriate execution point, the debugging stops at the breakpoint.
按照以上步骤,附加上进程并注册了工具后,就可以设置断点进行调试了。不过需要注意的是这种进行调试的方法不太稳定——在完成一次调试后,重新打开VS运行程序后,在附加进程的对话框中再也找不到RuntimelocalServer.exe的两个进程,没有办法继续进行调试。刚开始自己也没有很好的解决办法,后来打电话到ESRI技术支持中心,他们那边的工程师在自己的电脑上调试这类程序也是这种情况说是一个bug,也没有很好的解决办法,只是说让我在winForm中把程序调通再改。后来就是尝试各种方法,一次在程序运行中尝试附加进程,结果终于看到了RuntimelocalServer.exe!!!
也就是说,遇到程序刚开始不能附加指定进程的情况,可以在已经在程序执行对话框这中配置好输入、输出参数后(程序已经在执行Execute()函数),再次在VS工具选项中尝试附加该进程,这时候就会正常命中断点!
最后程序调通后,就是Tool的注册和调用了。Tool的注册有两种方法,第一种是在VS中执行程序,Tool会自动注册到ArcGIS Desktop中;另一种方式是在程序编译后在程序目录的Debug文件夹找到.dll动态链接库文件,然后双击该文件,选择Desktop完成Tool在ArcGIS上的注册。
然后在ArcCatalog的自定义工具箱中找到Tool目录和对应工具,双击工具即可运行程序。我的Merge Disconnect Line工具运行后的参数配置界面如下:
在配置好输入参数和输出参数后,点击OK执行程序。
------------------------------------------------------------------------------------------------------------------------
本程序的主要代码:
1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 using System.Runtime.InteropServices; 5 using System.Windows.Forms; 6 using ESRI.ArcGIS.ADF.CATIDs; 7 using ESRI.ArcGIS.DataManagementTools; 8 using ESRI.ArcGIS.esriSystem; 9 using ESRI.ArcGIS.Geodatabase; 10 using ESRI.ArcGIS.Geometry; 11 using ESRI.ArcGIS.Geoprocessing; 12 using ESRI.ArcGIS.Geoprocessor; 13 14 namespace GPMergeDisconnectLine 15 { 16 /************************************************************************************************/ 17 //定义GP工具 22 public class MergeDisjunctLineFunction : IGPFunction 23 { 24 //工具的名称 25 private string m_ToolName = "MergeDisconnectLine"; 26 private string m_metadatafile = "MergeDisconnectLine.xml"; 27 private IArray m_Parameters; // Array of Parameters 28 private IGPUtilities m_GPUtilities; 29 30 public MergeDisjunctLineFunction() 31 { 32 m_GPUtilities = new GPUtilitiesClass(); 33 } 34 35 public string Name 36 { 37 get { return m_ToolName; } 38 } 39 40 // Set the function tool Display Name as seen in ArcToolbox. 41 public string DisplayName 42 { 43 get { return "Merge Disconnect Line"; } 44 } 45 47 public void Execute(IArray paramvalues, ITrackCancel trackcancel, IGPEnvironmentManager envMgr, IGPMessages message) 48 { 49 //输入参数 50 IGPParameter parameter = (IGPParameter)paramvalues.get_Element(0); 51 IGPValue parameterValue = m_GPUtilities.UnpackGPValue(parameter); 52 // Open Input Dataset 53 IFeatureClass inputFeatureClass = null; 54 IQueryFilter qf = null; 55 IGPRecordSet gprs = null; 56 IRecordSet2 rs2 = null; 57 58 IFeatureClass outputFeatureClass = null; 60 int sumCount = 0; 61 if (parameterValue.DataType is IDEGeoDatasetType) 62 { 63 m_GPUtilities.DecodeFeatureLayer(parameterValue, out inputFeatureClass, out qf); 64 if (inputFeatureClass == null) 65 { 66 message.AddError(2, "Could not open input dataset."); 67 return; 68 } 70 sumCount = inputFeatureClass.FeatureCount(null); 71 } 72 else if (parameterValue.DataType is IGPFeatureRecordSetLayerType) 73 { 74 gprs = parameterValue as IGPRecordSet; 75 rs2 = gprs.RecordSet as IRecordSet2; 76 //SearchCursor = rs2.get_Cursor(false); 77 sumCount = rs2.Table.RowCount(null); 78 } 79 80 /*Create FeatureClass 输出参数!!!*/ 84 parameter = (IGPParameter)paramvalues.get_Element(1); 85 parameterValue = m_GPUtilities.UnpackGPValue(parameter); 86 Geoprocessor gp = new Geoprocessor(); 87 // Create the new Output Polyline Feature Class 88 CreateFeatureclass cfc = new CreateFeatureclass(); 89 90 //根据用户在output featureclass中命名的name创建新的输出要素 91 IName name = m_GPUtilities.CreateFeatureClassName(parameterValue.GetAsText()); 92 IDatasetName dsName = name as IDatasetName; 93 IFeatureClassName fcName = dsName as IFeatureClassName; 94 IFeatureDatasetName fdsName = fcName.FeatureDatasetName as IFeatureDatasetName; 95 96 // Check if output is in a FeatureDataset or not. Set the output path parameter for CreateFeatureClass tool. 97 if (fdsName != null) 98 { 99 cfc.out_path = fdsName; 100 } 101 else 102 { 103 cfc.out_path = dsName.WorkspaceName.PathName; 104 } 105 // Set the output Coordinate System for CreateFeatureClass tool. 106 IGPEnvironment env = envMgr.FindEnvironment("outputCoordinateSystem"); 107 // Same as Input 108 if (env.Value.IsEmpty()) 109 { 110 //IGeoDataset ds = inputFeatureClass as IGeoDataset; 111 //cfc.spatial_reference = ds.SpatialReference as ISpatialReference3; 112 } 113 // Use the evnviroment setting 114 else 115 { 116 IGPCoordinateSystem cs = env.Value as IGPCoordinateSystem; 117 cfc.spatial_reference = cs.SpatialReference as ISpatialReference3; 118 } 119 // Remaing properties for Create Feature Class Tool 120 cfc.out_name = dsName.Name; 121 cfc.geometry_type = "POLYLINE"; 122 gp.Execute(cfc, null); 124 125 outputFeatureClass = m_GPUtilities.OpenFeatureClassFromString(parameterValue.GetAsText()); 129 //Set the properties of the Step Progressor 130 IStepProgressor pStepPro = (IStepProgressor)trackcancel; 131 pStepPro.MinRange = 0; 132 pStepPro.MaxRange = 6; 133 pStepPro.StepValue = (1); 134 pStepPro.Message = "Merge disjunct polyline is in processing"; 135 pStepPro.Position = 0; 136 pStepPro.Show(); 137 138 //合并操作开始 139 MergeOperation mOpetation = new MergeOperation(); 140 pStepPro.Step(); 141 List<IFeature> allPolylineList = mOpetation.getAllPolyline(inputFeatureClass); 142 pStepPro.Step(); 143 List<IPoint> allNodePointList = mOpetation.GetNodePtsListByLine(allPolylineList); 144 pStepPro.Step(); 145 List<IPoint> distinctNodePointList = mOpetation.GetDistinctNodePtsList(allNodePointList); 146 pStepPro.Step(); 147 List<IFeature> unionLineList = mOpetation.MergeLineListOperate(allPolylineList, distinctNodePointList, inputFeatureClass); 148 pStepPro.Step(); 149 mOpetation.AddField(inputFeatureClass, outputFeatureClass); 150 pStepPro.Step(); 151 mOpetation.WriteUnionLineToFile(unionLineList, outputFeatureClass); 152 pStepPro.Step(); 154 System.Runtime.InteropServices.Marshal.ReleaseComObject(outputFeatureClass); 156 //合并操作结束 157 pStepPro.Hide(); 158 } 159 160 // This is the location where the parameters to the Function Tool are defined. 161 // This property returns an IArray of parameter objects (IGPParameter). 162 // These objects define the characteristics of the input and output parameters. 163 public IArray ParameterInfo 164 { 165 get 166 { 167 //Array to the hold the parameters 168 IArray parameters = new ArrayClass(); 169 170 IGPParameterEdit3 inputParameter = new GPParameterClass(); 171 inputParameter.DataType = new GPFeatureLayerTypeClass(); 172 inputParameter.Value = new GPFeatureLayerClass(); 173 174 // Set Input Parameter properties 175 inputParameter.Direction = esriGPParameterDirection.esriGPParameterDirectionInput; 176 inputParameter.DisplayName = "Input Features"; 177 inputParameter.Name = "input_features"; 178 inputParameter.ParameterType = esriGPParameterType.esriGPParameterTypeRequired; 179 parameters.Add(inputParameter); 180 198 // Output parameter (Derived) and data type is DEFeatureClass 199 IGPParameterEdit3 outputParameter = new GPParameterClass(); 200 outputParameter.DataType = new DEFeatureClassTypeClass(); 201 202 // Value object is DEFeatureClass 203 outputParameter.Value = new DEFeatureClassClass(); 204 205 // Set output parameter properties 206 outputParameter.Direction = esriGPParameterDirection.esriGPParameterDirectionOutput; 207 outputParameter.DisplayName = "Output FeatureClass"; 208 outputParameter.Name = "out_featureclass"; 209 outputParameter.ParameterType = esriGPParameterType.esriGPParameterTypeRequired; 210 211 // Create a new schema object - schema means the structure or design of the feature class (field information, geometry information, extent) 212 IGPFeatureSchema outputSchema = new GPFeatureSchemaClass(); 213 IGPSchema schema = (IGPSchema)outputSchema; 214 215 // Clone the schema from the dependency. 216 //This means update the output with the same schema as the input feature class (the dependency). 217 schema.CloneDependency = true; 218 219 // Set the schema on the output because this tool will add an additional field. 220 outputParameter.Schema = outputSchema as IGPSchema; 221 outputParameter.AddDependency("input_features"); 222 parameters.Add(outputParameter); 223 224 return parameters; 225 } 226 } 227 228 //验证合法性 229 public IGPMessages Validate(IArray paramvalues, bool updateValues, IGPEnvironmentManager envMgr) 230 { 231 if (m_Parameters == null) 232 m_Parameters = ParameterInfo; 233 if (updateValues) 234 { 235 //UpdateParameters(paramvalues, envMgr); 236 } 237 //// Call InternalValidate (Basic Validation). Are all the required parameters supplied? 238 //// Are the Values to the parameters the correct data type? 239 IGPMessages validateMsgs = m_GPUtilities.InternalValidate(m_Parameters, paramvalues, updateValues, true, envMgr); 240 241 //UpdateMessages(paramvalues, envMgr, validateMsgs); 242 return validateMsgs; 243 } 244 245 //更新参数,有些工具需要设置好一个参数后,才能设置下一个参数,例如需要选择一个矢量数据之后, 246 //才能选择数据中的字段,这样的工作可在该代码中定义,如何定义还需要查看帮助和示例 247 public void UpdateParameters(IArray paramvalues, IGPEnvironmentManager pEnvMgr) 248 { 249 //m_Parameters = paramvalues; 250 // Retrieve the input parameter value 251 //IGPValue parameterValue = m_GPUtilities.UnpackGPValue(m_Parameters.get_Element(0)); 252 } 253 254 280 // This is the function name object for the Geoprocessing Function Tool. 281 // This name object is created and returned by the Function Factory. 282 // The Function Factory must first be created before implementing this property. 283 public IName FullName 284 { 285 get 286 { 287 IGPFunctionFactory functionFactory = new MergeDisjunctLineFactory(); 288 return (IName)functionFactory.GetFunctionName(m_ToolName); 289 } 290 } 291 292 // This is used to set a custom renderer for the output of the Function Tool. 293 public object GetRenderer(IGPParameter pParam) 294 { 295 return null; 296 } 297 298 //帮助的上下文标识 返回0即可 299 public int HelpContext 300 { 301 get { return 0; } 302 } 303 304 // This is the path to a .chm file which is used to describe and explain the function and its operation. 305 public string HelpFile 306 { 307 get { return ""; } 308 } 309 310 // This is used to return whether the function tool is licensed to execute. 312 public bool IsLicensed() 313 { 314 IAoInitialize aoi = new AoInitializeClass(); 315 ILicenseInformation licInfo = (ILicenseInformation)aoi; 316 string licName = licInfo.GetLicenseProductName(aoi.InitializedProduct()); 317 if (licName == "Advanced") 318 { 319 return true; 320 } 321 else 322 { 323 return false; 324 } 325 } 326 327 //元数据文件 这个返回空字符串也可以 328 public string MetadataFile 329 { 330 get 331 { 332 string filePath; 333 filePath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); 334 filePath = System.IO.Path.Combine(filePath, m_metadatafile); 335 return filePath; 336 } 337 } 338 339 public UID DialogCLSID 340 { 341 // DO NOT USE. INTERNAL USE ONLY. 342 get { return null; } 343 } 344 } 345 346 347 /*IGPFunctionFactory*************************************************************************************************/ 348 349 [Guid("526de91e-3fe5-4a46-a7e2-4d1dc3cdb5db"), ComVisible(true)] 350 351 public class MergeDisjunctLineFactory : IGPFunctionFactory 352 { 353 // Register the Function Factory with the ESRI Geoprocessor Function Factory Component Category. 354 355 #region "Component Category Registration" 356 357 [ComRegisterFunction()] 358 private static void Reg(string regKey) 359 { 360 GPFunctionFactories.Register(regKey); 361 } 362 363 [ComUnregisterFunction()] 364 private static void Unreg(string regKey) 365 { 366 GPFunctionFactories.Unregister(regKey); 367 } 368 369 #endregion 370 371 // Utility Function added to create the function names. 372 private IGPFunctionName CreateGPFunctionNames(long index) 373 { 374 IGPFunctionName functionName = new GPFunctionNameClass(); 375 functionName.MinimumProduct = esriProductCode.esriProductCodeAdvanced; 376 IGPName name; 377 378 switch (index) 379 { 380 //工具箱中只有一个工具 381 case (0): 382 name = (IGPName) functionName; 383 name.Category = "DisconnectlineMerge"; 384 name.Description = "Merge a disconnect line list to a continuous line"; 385 name.DisplayName = "Merge Disconnectline"; 386 name.Name = "MergeDisconnectLine"; 387 name.Factory = (IGPFunctionFactory) this; 388 break; 389 } 390 391 return functionName; 392 } 393 394 // Implementation of the Function Factory 395 // This is the name of the function factory. 396 // This is used when generating the Toolbox containing the function tools of the factory. 397 public string Name 398 { 399 get { return "DisconnectlineMerge"; } 400 } 401 402 // This is the alias name of the factory. 403 public string Alias 404 { 405 get { return "lineMerge"; } 406 } 407 408 // This is the class id of the factory. 409 public UID CLSID 410 { 411 get 412 { 413 UID id = new UIDClass(); 414 id.Value = this.GetType().GUID.ToString("B"); 415 return id; 416 } 417 } 418 419 // This method will create and return a function object based upon the input name. 420 public IGPFunction GetFunction(string Name) 421 { 422 switch (Name) 423 { 424 case ("MergeDisconnectLine"): 425 IGPFunction gpFunction = new MergeDisjunctLineFunction(); 426 return gpFunction; 427 } 428 429 return null; 430 } 431 432 // This method will create and return a function name object based upon the input name. 433 public IGPName GetFunctionName(string Name) 434 { 435 IGPName gpName = new GPFunctionNameClass(); 436 437 switch (Name) 438 { 439 case ("MergeDisconnectLine"): 440 return (IGPName) CreateGPFunctionNames(0); 441 442 } 443 return null; 444 } 445 446 // This method will create and return an enumeration of function names that the factory supports. 447 public IEnumGPName GetFunctionNames() 448 { 449 IArray nameArray = new EnumGPNameClass(); 450 nameArray.Add(CreateGPFunctionNames(0)); 451 return (IEnumGPName) nameArray; 452 } 453 454 public IEnumGPEnvironment GetFunctionEnvironments() 455 { 456 return null; 457 } 458 } 459 }
本文是自己这几天做ArcGIS GP工具的一些心得体会,部分参考ESRI开发者中心文档。文章和代码如有不足之处,请和我联系,感谢!(邮箱:[email protected])
-----------------------------------------------------------------------------------------------------
本文系作者GISQZC原创文章,欢迎转载,但必须注明出处,否则将追究相关法律责任!