Friday, October 12, 2007
Posted on Friday, October 12, 2007 5:29:13 PM (Mountain Daylight Time, UTC-06:00)  Comments [3] | 
Categories: ArcGIS Server | ArcMap

I'm trying to get some maps published in ArcGIS Server, and I've run into some weird problems. When I try to publish a map, I get an error stating that the Server Object Creation failed, and that the map file can not be opened.

My first thought was security, and there were some issues there, but that did not solve everything - I still got the errors. So, as I posted the other day, I started up ArcMap as the ArcGISSOC user. The thing is that even though I can browse to the map as that user, it fails to load the map under most conditions.

I can load the mxd file into ArcMap if I first preview it in ArcCatalog (also running as ArcGISSOC). If I just try to open the mxd with ArcMap, the app just disappears. So now I'm thinking it's something with the file being corrupted (works fine as a normal user, but who knows). So I try to create a fresh new map - I fire up ArcMap as ArcGISSOC, connect to a layer, and save the file. When the save finishes, I get this helpful message

best-error-ever

Apparently no error occurred. Thanks ArcMap, but I'm still somewhat suspicious about that.

Turns out that the error is related to security. I'm working on a client's system, that's really locked down, and instead of trying a 1000 little things to see if it was security related, I took out the big hammer - I added ArcGISSOC to the local admin group. Shazam - we got maps! Now that I know it's permission based, I need to dig out the Admin guide again and review exactly what's needed

Thursday, October 04, 2007
Posted on Thursday, October 04, 2007 11:02:35 PM (Mountain Daylight Time, UTC-06:00)  Comments [0] | 
Categories: ArcGIS Devt | ArcMap | Geodatabase

We put a lot of business logic into Class Extensions tied in on the OnCreate, OnChange, and OnDelete Object Class Extension events. If an edit violates business logic, it is rolled back and the user is informed about what was wrong. This has been working quite smoothly until today when we started creating our Object Inspectors (see previous post).

What we found was that attribute edits done through our Object Inspector (and consequently ArcDAL) were not respecting the business logic. I dropped some breakpoints into the OnChange event in one of the class extensions, and found that it was not hit when we saved changes in the Object Inspector. Yet, when I opened the table editor I would see the changes, so I knew something was happening. Here's the series of events graphically...

1) Original values in Object Inspector...

original-values

2) District Name updated and saved

update-values

3) Table editor shows the updated name, but OnChange event never fired!

table-editor

4) When I try to edit the District Name via the table editor...

table-editor-change

the OnChange event does fire, and the edit is rolled back because I do not have edit rights to that feature.

table-editor-roll-back

We traced it back to using update cursors. Turns out that updating features via FeatureClass.Update in an edit session does not store changes to the base table until the edit session is saved. Thus, the ObjectClassExtension events do not fire until you "save" the edits and end the session. We need these events to fire as the edits occur so the user is informed when business rules are broken.

The solution was to just change back to using IFeature.Store - which correctly raises the events. This was just 4 simple edits, but we have 25 feature classes in our geodatabase model. Thanks to code-generation, we just made the edits in the templates, and re-generated all the classes.

Tuesday, September 11, 2007
Posted on Tuesday, September 11, 2007 4:00:23 AM (Mountain Daylight Time, UTC-06:00)  Comments [3] | 
Categories: .NET | ArcGIS Devt | ArcMap

A couple of times I've seen people asking about how to share information between forms/commands/tools etc. in ArcMap and I thought I'd create a simple example of how I've been doing it.

Problem

At a high level, the problem is pretty simple - one part of your application needs to know about the state of another part. One example could be a combo box tool control that allows the user to select an Active layer, and you have other tools/controls/forms etc that need to know what the active layer is, and need to be informed when the active layer changes.

Solution

This is an obvious case where custom events are going to come into play, since they are the standard way to provide notification of a state change to a set of "subscribing" or listening classes. In standard .NET applications, you'd create an event right on the actual source - in this case the tool control. However, since we are working in ArcMap, we have a slightly different situation. Since we (developers) do not control the load order of tools/commands etc in ArcMap, it's rather difficult to effectively sink up event handlers and sources if they are all located in your customization classes (ICommand/ITool etc). This is because we don't really know when either class is actually instantiated during the load process, and once it's instantiated, it's a pain to actually get a pointer to it.

The way to solve this is to route the events through something that's always loaded, and easily accessed - such as an application extension (IExtension).

Since application extensions (with the exception of JIT Extensions) are always loaded when ArcMap is starting up, we know it's always present. Regardless of when the event source tool / form / command is initialized, it will always be able to call a method on the extension, which then raises an event. The same it true for the classes handling the event - since the extension is loaded and accessible, they can wire up an event handler during their initialization, or any time after that.

ex-event1

While there are other ways to pass "state" around, using an event allows a whole set of classes to then subscribe to the event and be notified when changes occur.

ex-event2

Once you think about it, this is quite obvious, and it is a small scale mirror of much of the ArcObjects customization API - write a class, wire it into ArcMap, and have it respond to ArcMap events. Except in this case, it's our own custom event.

Sample

The sample is in C#, and is kept very simple, so the focus is on wiring the events.

Extension:

The extension has a public SetSelectedItem method, which in turn raises the SelectedItemChanged event.

Commands:

There are two commands - EventSourceCommand and EventSinkCommand. In the onCreate function, both commands get pointers to the event extension, and pass this into the IntializeUI methods on the forms. The Event Source Form has a simple combo box listing 5 fruits. In the OnChange event of the combo box, the form calls SetSelectedItem on the extension. The Extension raises the SelectedItemChanged event, to which the Event Sink Form has  subscribed, and a label is updated to show the currently selected fruit. Although it's a bit complex when you read it, this is very simple when you look at the code, and run it.

Since the sample is in C#, it uses event wiring involving delegates. If you are new to delegates, O'Reilly has a good article - Writing C# Custom Events, which I would recommend. The syntax is much simpler in VB.NET, and here's a CodeProject article on that. Of course you can use delegates in VB.NET if you want to, so for completeness sake, here's an article on events using delegates in VB.NET. Moving on...

To run the sample, download the code, compile it, and run ArcMap. You should get a pop-up box as ArcMap is loading which tells you that the Event Extension is loading. Once ArcMap is started, open up the Customize dialog, and look in the DaBo.Samples category.

event-tools

Drag the Event Sink and Event Source command onto a toolbar, and then run them. Both commands open forms. Use the pulldown on the Event Source Form to update the label on the Event Sink Form.

event-sample

When you dig into the code you'll see that I've used the out of the box templates for the commands and extension. The only other "cool" thing I threw in was the ArcMapWindow class I wrote about previously.

Have fun wiring up your events!

Download Sample

Wednesday, September 05, 2007
Posted on Wednesday, September 05, 2007 2:34:42 AM (Mountain Daylight Time, UTC-06:00)  Comments [1] | 
Categories: .NET | ArcGIS Devt | ArcMap

While I was at home, away from my source tree at work, I was looking to correctly parent a form to ArcMap. By parenting a form to ArcMap, it will be minimized with ArcMap, and will always stay in front of ArcMap - important if you want to have multiple forms up at the same time, and what them all to be visible!

In the past we've always used the Win32 API 's SetWindowLong function - which we wrapped up a little in a helper class. Not having the source to look at, or the helper class to use, I needed to figure out the syntax. To complicate things, I was also writing the code in C# - it's those subtle changes that will always mess you up. After finding the PInvoke site, and locating the SetWindowLong function there, and being somewhat confused about how to get an IntPtr from Application.hWnd, I did a search in the ESRI forums. Low and behold I found this recent post (July 31, 2007) by Berend Veldkamp in which he provides a very simple way to parent a form to ArcMap.

In .Net 2.0 it's possible to pass a parent window to the Show method of your form. This should be an object implementing IWin32Window, here's a basic example of how to do that in VB.Net 

Doh! Amazing what you overlook because you're used to doing things the old way - I had no idea that Form.Show had a parent option. Anyhow, I'll let you get the VB.NET version from the forum post - here's the C# version: 

 public class ArcMapWindow : System.Windows.Forms.IWin32Window

 {

    private IApplication m_app;

 

    public ArcMapWindow(IApplication application)

    {

        m_app = application;

    }

 

    public System.IntPtr Handle {

        get { return new IntPtr(m_app.hWnd); }

    }

 

 }

Now, to show a form parented to ArcMap, all I need to do is...

form.Show(new ArcMapWindow(m_application));

This is much simpler and avoids the Win32 API call. Thanks Berend!

Thursday, July 12, 2007
Posted on Thursday, July 12, 2007 10:20:28 AM (Mountain Daylight Time, UTC-06:00)  Comments [2] | 
Categories: .NET | ArcMap
This was pretty weird, and thankfully others on my team had already run into it, so it was a quick fix. Thought I'd share it...

Here's the basic scenario:

We have some base classes which contain a lot of common code for feature and object class extensions. The ObjectClassExtensionBase class implements IClassExtension



This is a very simple Interface - it has two methods Init and Shutdown. All is well, and we've been building this project for a couple of months. This morning I created a new project, in which I referenced the assembly containing the base classes.

 

Suddenly I start getting 2 compile errors in the "Base" class assembly:



How's this for wack - the first error says that I need to implement Sub Init for IClassExtension, and the second error says that my Sub Init can not implement IClassExtension.Init because it's not present in IClassExtension??! They contradict each other!

After some head scratching I asked around, and others have run into this recently. Seems that the issue is related to some referenced ESRI .NET assemblies being located in the GAC. What's even more bizzare is that the error can crop up in a different assembly than the one that has the offending reference.

In my situation, our "BaseClass" assembly had not changed. Yet that's where the compile error showed up. I checked all the ESRI references in that assembly, and they were all pointing to the expected "C:\Program Files\ArcGIS\DotNet\something.dll".

However, when I looked at the references in my "new" project, the ESRI.ArcGIS.System assembly it was referening was located in the GAC...



Conveniently I did not actually need this reference, and as soon as I dropped it, everything compiled just fine.

Interestingly - if I add this reference via the standard Visual Studio "Add Reference..." dialog, it uses a copy that's in "C:\Program Files\ArcGIS\DotNet"



But, if I add it via the ESRI ArcGIS Reference add-in, it adds a copy from the GAC and the error returns.



As for why this is an issue, I'm guessing it's an all or nothing type of thing - either all the ESRI references should come from the GAC, or all from C:\Program Files\ArcGIS\DotNet, and a mix will cause problems.

Anyhow - hope this is useful to someone else.
Sunday, May 13, 2007
Posted on Sunday, May 13, 2007 9:15:27 PM (Mountain Daylight Time, UTC-06:00)  Comments [0] | 
Categories: .NET | ArcMap

I'm guessing a few of you may have seen this dialog before...

This is what you get when there is an unhandled exception in your .NET code that's running in ArcMap. And it's not exactly handy for an end user - what does a stack trace mean to them? Not much. So the question becomes - can we replace this dialog with our own, have it inform the user gracefully that an error has occured, all the while logging the stack trace and other useful information for the developers/administrators.

I should stop here and note that what I'm trying to catch are truly unhandled exceptions. Generally issues which occur deep within ArcGIS, and result in some kind of un-anticipated failure in our custom .NET code. Even with a reasonable amount of try/catch blocks, these exceptions do occur from time to time, and they are exceptionally difficult to fix.

In this post, I'm going to tackle intercepting the unhandled exception. Once you've caught the exception and collected the information you need, there are any number of ways to log the information - custom web services, Enterprise Library Logging Block, Log4Net etc. In a future post, I'll cover some of these.

Catching "Unhandled" Exceptions
From the .NET side of things there are two ways you can "catch" unhandled exceptions. I say "catch" because you can't actually catch them in the typical try/catch/finally sort of scenario. At .NET 2.0 an unhandled exception will allways cause the application to exit. While the CLR is shutting things down, it raises events which you can provide handlers for.

1) AppDomain.UnhandledException Event
This is the king-kahuna, and the story should end right here. Basically, if an unhandled exception occurs in the AppDomain, the AppDomain.UnhandledException Event gets raised. At which time you can log whatever you want. Great - problem solved and this post is over. Only from my testing, this just does not work within ArcMap. After some head scratching, I read this curious little note in the docs on MSDN...

For certain application models, the UnhandledException event can be preempted by other events if the unhandled exception occurs in the main application thread.

No indication as to what other events these may be, and I'm not sure what's going on in ArcMap (maybe someone from ESRI can shed some light?) but the end result is that this event never fires. Thus, we move on to...


2) Application.ThreadException
While somewhat limiting since it only responds to unhandled exceptions that are on Windows.Forms threads Application.ThreadException Event provides an opportunity to log the exception before the application exits. And it does work in ArcMap, so it's part of a solution.


Example Project
Since I figured this all out by writing some "research" (read: hacker) code, I thought I'd share it - maybe someone else can figure out how to get those other unhanded exceptions.

About the code - this is VB.NET, written for ArcGIS Desktop 9.2. It has a total of 5 commands, and an Extension. My goal was to find a way to centrally handle these exceptions - specifically in my extension.

The first command I created was the NotHandledCommand - which simply throws an exception in the on_click event. This was my baseline to see if I could catch this from my extension. The answer is no - you can't catch an exception that occurs in the command class itself. More interesting is that throwing an exception in an ICommand does not cause a JIT window to appear. It looks like ArcMap is eating the exception. Since I created my first command by inheriting from BaseCommand, I created another command that directly implements ICommand - and same thing. I can see why ESRI may want to do this (kepp ArcMap stable), but if you are going to eat exceptions, please raise an event so developers can know that an exception has occured.

Anyhow - to address this, we're creating some new Command/Tool templates which will have try/catch blocks with the correct logging code baked in (I'll do a post about creating custom visual studio templates too because this is super handy).

The next set of commands, ExceptionFormCommand and ExceptionFormTwoCommand simply open up forms, which have a single button which throws an exception. As I noted above, since this code is running on a System.Windows.Forms thread, the exception is caught and the Application.ThreadException Event is raised. In the extension. I created two commands simply to test it a little more. I then took this a step further and created a dockable window (ExceptionDocWinCommand) which also just raises an exception, and yes it also works. Then, to finish it off, I created a user control (button that throws an exception), which I then put on the dockable window - and yes - that worked too.

So - you can download the code below. Compile in Visual Studio 2005, run ArcMap and you'll need to put the commands onto toolbars manually - they're all in the "AAA Research" category.

And if anyone knows how to correctly wire up the AppDomain.UnhandledException event, or another way to catch unhandled exceptions please let me know.

Download Sample Code

Sunday, April 29, 2007
Posted on Sunday, April 29, 2007 8:48:26 PM (Mountain Daylight Time, UTC-06:00)  Comments [1] | 
Categories: ArcGIS Devt | ArcGIS Server | ArcMap | Geodatabase

I recently recieved an email from a reader asking about role based security & ArcGIS Server. Specifically they were interested in restricting access to columns, based on the user's credentials.

I'd been doing some thinking on similar issues related to row based edit permissions, and I thought I'd share my ideas on possible solutions to these problems.

Fine Grained Access Permissions

Essentially, you can't use the DBMS security capabilities to restrict user access to columns or rows because ArcSDE does not support security at this fine grained level (FeatureClass, ObjectClass or FeatureDataset only). There may be some hacks, but as far as I know nothing like this is supported directly.

Next, and possibly more important, regardless of how you actually create the connections (i.e. store the connection properties in the MXD file, or use pass-through authentication and grant the SOC process permission to connect into the database) all interactions with features are done as the SOC Service user. The identity of the front end user  is not passed back to the SOC. Thus, regardless of who is running the application, the SOC identity is always the same. The diagram below shows how far you can pass back the end user's identity (assuming you setup impersonation in your web app).


 

The SOC always runs as the SOC Service user - so any use of the end user's identity to control any sort of access is not going to work.

Possible Solutions - Restricting Access to Columns

There are a variety of ways to tackle this, but none are particularly flexible - you will need to define the roles up front, and then setup the application to respect those roles explicity. Adding additional roles will require code changes. Four options come to mind

1) Let the SOC return all the data and apply the permissions in the client application. This is not a bad solution if you control the web app. It basically means re-writing a lot of the out of the box tools so that they will respect this additional business logic.

2) Setup the spatial data so that it has the minimal set of field that all users can see (including ObjectId). When doing an inquiry, the web app would get the base set of fields from the featureclass, and then use the ObjectId  to run a second query against a multi-version view to retreive the other "secured"  attributes. There would be a multi-version view for each role in the application, and the columns would be restricted as needed. The application logic would need to handle the mapping of the users role to the correct view.

3) Use Spatial Views. Similar to #2, but the SOC would load layers based on the user's role, and the layers would actually be Spatial Views with only the columns that are visible for the particular role of the user. I suspect this would be slower than the previous two options, as the SOC would need to load layers all the time, but if the application is using a non-pooled SOC anyhow, it may not bemuch more of a performance hit - just start with a minimal map (just the layers which do not have any restrictions on them). The upside is that you can likely use most of the out of the box tools since the column restrictions are being applied by the view, at the ArcSDE level. Another upside of this model is that the restricted data is never available - either in the SOC, or in the web application tier.

4) Similar to #3, you could separate out the restricted fields into separate tables, and apply joins on the fly. Same benefits as #3, but you'd still want to work with non-pooled SOCs. Not sure how well this would play with the out of the box tools, but it may be ok.

I'm sure there are other options, but I suspect they are all variations on this same theme. If the application was super simple (like 1 ID tool), you could just limit the fields returned by setting the IQueryFilter.SubFields property.


Row Based Edit Permissions

For our applications, there are typically business rules which restrict the area in which a user can edit features, and related attribute tables. In the past we have implemented this logic in feature class extensions. In the extension, we simply get the current principal (the user's Windows Login identity) lookup their access permissions from our repository and allow/block the edit. We have logic in the extension which allows for editing of shared boundaries (which I believe is one of the issues with implementing this sort of logic at the DBMS level)

This works just fine for Desktop and ArcEngine applications, but is more problematic for web applications. As I noted at the start of this post, all connections into ArcSDE from ArcGIS Server run as the SOC user. Thus we have a problem - if we apply a class extensions to the geodatabase, it's logic always fires - regardless of the client appliction. But, if all the edits from everyone using the web app effectivly run in the same context, (agssoc or whatever you call it on your system), then this logic is both moot, and a waste of processor time. But we still want to use this logic for the desktop apps, so we had a bit of a problem.

Our solution is to create feature class extensions which are themselves extensible. Basically we use  the Provider Model to add in various security providers at run-time. When the class extension spins up, it uses the ConfigurationManager to check the configuration file (ArcMap.exe.config for desktop, web.config for web apps) for "SecurityProviders". In the desktop apps, there will be a provider configured, the class will be instantiated, and the logic applied to all the edits. In the web app, we do not configure a security provider, thus there is no logic applied. 

This still begs the question of "how does the row based edit permission get applied in the web app?". At this time, there is no (easy) way to pass the user identity back into the SOC, let alone get the SOC to impersonate a user such that the class extension would be running in the context of the front end user. So, for now, the answer is - the web application must handle this logic itself. While this is not idea, at least the class extensions can be used in the desktop apps - which is where the vast majority of the edits will occur anyhow.

Monday, March 05, 2007
Posted on Monday, March 05, 2007 9:11:47 PM (Mountain Standard Time, UTC-07:00)  Comments [0] | 
Categories: .NET | ArcMap | Powershell
In my last ArcDeveloper talk, I discussed how to automate common ArcMap tasks without actually using ArcMap. The talk was based on a previous blog posting: Automating Maps: Using ArcObjects without ArcMap. I did not have time prior to the talk, but I had wanted to show how you can take the simple MapMaker class in that example, and wire it into Powershell to create a simple command line utility that could spit out maps. I found an our this evening, and whipped this up. It's my first foray into PowerShell, so be kind ;-)

For those who have not heard of it, PowerShell is the new Windows shell. It exposes "korn shell"-like power (you recall all that grepping, awking and piping fun), but mixes in objects.

Here's how the Powershell documentation describes it...

Unlike most shells, which accept and return text, Windows PowerShell is built on top of the .NET common language runtime (CLR) and the .NET Framework, and accepts and returns .NET objects. This fundamental change in the environment brings entirely new tools and methods to the management and configuration of Windows.
If that's not enough to pique your curiosity, I'll also add that pretty much anything that Scott Hanselman says is cool is worth looking at.

I'm not going to walk through all the details of installing powershell and configuring it - I'll leave that to you (resource links at the bottom will help). I will show the basic pieces, how to register the PowerShell Snap-in with PowerShell, and how to use it.

Creating a Powershell cmdlet

Create a new class library project in Visual Studio 2005, create a class and throw this in.

using System;

using System.Text;

using System.Management.Automation;

using System.Management.Automation.Provider;

using System.Diagnostics;

using System.ComponentModel;


 


    [Cmdlet(VerbsCommon.Get, "ParcelMap",DefaultParameterSetName = "")]

    public class GetParcelMapCommand : PSCmdlet

    {

        private string _pid;

        private string _outputFile;

 

        [Parameter(Position = 0)]

        [ValidateNotNullOrEmpty]

        public string PID

        {

            get { return _pid; }

            set { _pid = value; }

        }

 

        [Parameter(Position = 1)]

        [ValidateNotNullOrEmpty]

        public string OutputFile

        {

            get { return _outputFile; }

            set { _outputFile = value; }

        }

 

        /// <summary>

        /// Code here runs the commandline parameters

        /// </summary>

        protected override void BeginProcessing()

        {

            //We are hardcoding the location of the mxd file and the output type

            DaBo.MapMaker.MapMakerService service = new     DaBo.MapMaker.MapMakerService("ParcelMap",@"c:\parcelmap.mxd","Parcels","PIDN");

            DaBo.MapMaker.MapMaker mm = new DaBo.MapMaker.MapMaker();

            //we just pass in the parcel id and the output file

            mm.ExportMap(service, DaBo.MapMaker.MapTypeEnum.PDF, _outputFile, _pid, 10, 10);

        }

 

        /// <summary>

        /// If we implemented code here, it would allow pipleline type operations

        /// </summary>

        protected override void ProcessRecord()

        {}

 

    }



That's the cmdlet itself. But for Powershell to know about it, we need to create a snap-in. This is very simple code that you can look at in the source code, but basically all I did was copy/paste from the sample code at MSDN and change a few names. Here's the top few lines...

 /// We'll use the name of this class with add-pssnapin later

  [RunInstaller(true)]

  public class GetParcelMapPSSnapIn : PSSnapIn

  {

 

    public GetParcelMapPSSnapIn() : base()

    {    }

   ///code continues...



Post-Build Events
Once you have your assembly built, you'll need to put it someplace that powershell can access it. I'd suggest copying the file to a common location for all your powershell snap-ins instead of having these things scattered all over the place. An easy way to do this is via post-build events in visual studio. I chose to put my stuff into c:\powershell.

Thus my post-build event is:

copy $(ProjectDir)$(OutDir)*.dll c:\powershell

I'm copying all the dlls to ensure that any dependancies (i.e. the MapMaker assembly) are brought along for the ride.

Register the Snap-In with PowerShell
Once it's there, it's time to open PowerShell, register the Snap-In and start spitting out maps. I'm sure that this part could be automated (someone likely already automated it using powershell itself). Anyhow - here's how you do it manually.

In Powershell, cd to the folder where you have copied your assembly - c:\powershell for example.
Then issue these commands:

set-alias installutil $env:windir\Microsoft.NET\Framework\v2.0.50727\installutil
installutil dabo.powershell.dll
add-pssnapin GetParcelMapPSSnapin

That's it. You can check that it's registered by running this cmdlet



To use the cmdlet, simply feed it the parcel pin and an output file



And here's the map it cranked out (as you can tell, cartography is not my strong suit)



Summary
Now I know that this is not the most useful example in the world - the command line is much more cryptic than using a simple winforms app. But if we look past the specifics of the example, and recall all the cool things we could do when combining Workstation ArcInfo with the unix shell commands, things start to open up a little. Particularly if you work in a production environment whre you are moving lots of files, transforming them along the way - it's very common to need to string together a series of tasks in rather ad-hoc ways. By creating a library if geospatial cmdlets, you would be able to do this same style of processing. Have fun!

Resources
Source code  and data for MapMaker
Get-ParcelMap cmdlet source code
VS 2005 cmdlet template (from Scott Hanselman's blog)
MSDN: Creating a cmdlet with parameters
MSDN: Registering a cmdlet with PowerShell

Saturday, March 03, 2007
Posted on Saturday, March 03, 2007 7:57:34 PM (Mountain Standard Time, UTC-07:00)  Comments [3] | 
Categories: .NET | ArcMap

I've done a couple of posts on using configuration files in web application and WinForms applications. This week I needed to do the same thing from within ArcMap, so I thought I'd whip up a quick sample that shows how to do this.

Many of you may know that ArcMap has a config file (ArcMap.exe.config). The only thing that's in there by default is the specification of the .NET runtime that ArcMap will use.

  <startup>

    <supportedRuntime version="v2.0.50727" />

  </startup>

 

[Note: ArcCatalog, ArcGlobe and ArcScene also have config files - this same technique can be used with app these applications]

However, it can be used just like any other config file - and that's what this sample will show.

This sample is about as simple as it can possibly be - it's just going to pull a string out of a custom section. This is because I can't share the real code I built this week, and I wanted to keep this really simple.

Modify ArcMap.exe.config

Open the file in text editor of choice, and drop in your custom section handler, and the section.

  <configSections>

    <section name="simpledata" type="DaBo.ArcMapConfig.SimpleConfigHandler, DaBo.ArcMapConfig"  />

  </configSections>

  <!-- I told you it was simple! -->

  <simpledata value="Some Config Data from ArcMap.exe.config" />



A few things to note about this - in order for the framework to locate the config handler (this is not COM - there is no registry to rely on) the assembly must be local to the config file. This means that you'll need to copy the config handler assembly into C:\Program Files\ArcGIS\Bin or where ever you installed ArcGIS. I think you can also put the assembly in the GAC, but since it'sonly going to be used by ArcMap, there is no need.

During debugging, it is very easy to do this copy via a Post-Build Event. Open your Project properties and go to the Compile tab. At the bottom you'll see a Build Events button...




Click this to open a dialog that will allow you to create custom build events for this assembly. What we want to do is copy the assembly over to C:\Program Files\ArcGIS\bin, and we can do that by adding this into the Post-Build Event:

copy $(TargetPath) "c:\program files\ArcGIS\Bin"

This screen shot shows where to put this...



Accessing the Config Data
The sample code shows two ways to access the configuration data. It does this via two ArcMap commands. The first simple accesses it via the ConfigurationManger class (hint: you need to add System.Configuration as a reference to get this class!). It simply pulls the data from the config and displays the string in a messagebox.

    Public Overrides Sub OnClick()

        'Pull a value from the config file

        Dim simpleData As String = SimpleConfigHandler.GetConfig

        MessageBox.Show("This is pulled from the Config File directly: " + simpleData)

    End Sub


The second command takes this a step further - it pulls the data from an ArcMap extension which loads the data from the config when it loads. This is a better example because you'll likely be storing something useful in there (i.e. a connection string, application extension information etc). In that case you may want to expose in a more controlled manner - for example - if you are storing a connection string (which you have encrypted), you may want to hand out database connections instead of the actual connection string. In that case, having a central repository like an extension would be a very handy design decision.

When the extension starts up, it loads the data from the config, and stored it in a property...

        Public Sub Startup(ByRef initializationData As Object) Implements ESRI.ArcGIS.esriSystem.IExtension.Startup

            _simpleData = SimpleConfigHandler.GetConfig

        End Sub


Then the command simply gets a pointer to the extension, and pulls the value from a property on the extension.

 Public Overrides Sub OnClick()

        Dim simpleExt As DaBo.ArcMapExtension.SimpleExtension = m_application.FindExtensionByName("SimpleExtension")

        MessageBox.Show("This is pulled from the extension: " + simpleExt.SimpleData)

    End Sub


Summary
Although very simple, this sample should provide a starting point for using configuration sections with your desltop customizations. I would also note that you can create configuration files for class libraries - I've done this in the past for other projects, but somehow it just felt a little "wrong". Since ArcMap has a config file, it's nice to use it instead.

Download the sample code (ArcGIS 9.2, Visual Studio 2005, VB.NET)
Thursday, March 01, 2007
Posted on Thursday, March 01, 2007 8:37:12 AM (Mountain Standard Time, UTC-07:00)  Comments [0] | 
Categories: ArcMap | Security
Just a quick posting for those who get this via an RSS Reader, and may not have seen the update to the original post - I recieved confirmation from ESRI that the password returned in IWorkspace.ConnectionProperties is encrypted. And, for what should be obvious reasons, they will not discuss / disclose the algorithms used (completely reasonable).

I also thought I'd clarify why I wanted to know this information - we are building an application which will manage the connection into ArcSDE, and the end user will not know the connection information. I simply wanted to be sure that they could not get this information via a simple VBA script (like the example @ EDN for IWorkspace.ConnectionProperties). Since the password is encrypted, it would require a substantial hack that's likely on par with the other security layers in the system.

Wednesday, February 28, 2007
Posted on Wednesday, February 28, 2007 2:55:20 PM (Mountain Standard Time, UTC-07:00)  Comments [2] | 
Categories: ArcMap | Geodatabase | Security
[UPDATE 3/1/07 8:00am : After a few emails with ESRI, they agreed that discussing their public API, as it relates to the security of passwords is reasonable, and they have confirmed that the password that is returned by IWorkspace.ConnectionProperties is encrypted. For obvious reasons they will not disclose any of the technical details. Thanks to ESRI for confirming this, and for permitting me to share the answer.]

[UPDATE 2/28/07 7:50pm : I received a request from ESRI to take this posting, and my related forum post down because they discuss security and encryption related to their software. At this time, I'm taking the posting down, and will work with them to determine a reasonable update. Stay Tuned]


[ORIGINAL POSTING]
I posted a question [I deleted it's contents at ESRI's request] over at the ESRI forums about this, but thought I'd also throw it out here as well. Essentilly, I'm looking to build a secure application, and I want to know if it's possible to extract the connection password from the data returned by IWorkspace.ConnectionProperties. I posted some VBA code from an EDN sample that pulls the connection properties from the first layer in the map and displays the properties in a popup. All I added was a few lines which would actually write out the password byte array.
 


What I'd like to know (preferably from someone @ ESRI) just what that byte array is. I suspect it's the password encrypted, but it could also be a hash. If it is the password encrypted, where is the key? How safe is it? If it is a hash, is it reversible?

[Clarification: In my  original posting, I had asked a series of rhetorical questions (above) - I did not expect to get these answers from ESRI, nor do I really want them - I really just wanted to know that the password is "relatively" safe - i.e. encrypted.]

Friday, February 23, 2007
Posted on Friday, February 23, 2007 5:06:17 PM (Mountain Standard Time, UTC-07:00)  Comments [2] | 
Categories: ArcMap | Geodatabase
I had heard that raster performance in a File Geodatabase was good, and thought I'd check into it. For source data I used 4 tiles of 6 inch orthophotography. The files are TIFFs, and come in at 715MB each.

Initially I tried loading all 4 into ArcMap, and it just took too long to display, so I killed ArcMap, and re-started by just loading one.

To load and draw the file (no pyramids) took ~60 seconds (based on my watch)



Next, I used ArcCatalog to created pyramids for another of the tiles (took a few minutes and created an 80Mb RRD file). This loaded in about 5 seconds.




Finally, I loaded all 4 TIFFs into a file geodatabase (again took a few minutes). The resulting File Geodatabase is 2.87 GB in size, so it's a little bigger than the source TIFFs, which were 2.7GB. When loading into ArcMap, the layer took about 1 second to draw (again using my watch - it maybe a half second - hard to tell, other than it was FAST).




The other thing to note is that in both the TIFFs, the resulting image looks pretty choppy at this scale (compare the images above). The File Geodatabase image looks great.

As far as zooming and panning, the File Geodatabase just rocked. It's screaming fast. This next shot is at full resolution - I zoomed in 3 times drawing a rectangle each time, and the screen re-draw just about as fast as I could draw the boxes. Amazing.




The TIFF with pyramids seemed to be about the same speed with panning and zooming, but the TIFF without was consistenly a much slower 2-5 seconds for a pan or a zoom.

So - if you've got imagery that's slow, try out the file geodatabase and see if you get the same boost in speed.

Note: All these tests were done on a local disk - connecting across a network will impact performance.
Tuesday, January 30, 2007
Posted on Tuesday, January 30, 2007 9:40:56 PM (Mountain Standard Time, UTC-07:00)  Comments [2] | 
Categories: .NET | ArcMap | ESRI
[UPDATE: 1/31/07 I got a note from a member of the ArcMap team who indicated that when using the IActiveView interface on a MapDocument object, you need to call IActiveView.Activate. The sample has been updated to reflect this, and you can read more about this in the ArcObjects documentation for IActiveView.Activate]

Thought I’d cook up another simple example application that shows how you can streamline the creation of repetitive maps. In this sample I’m going to create either a PDF of the map layout, or a JPEG of the map. This sample also shows another use of the configuration section handlers I posted recently. It's written in VB.NET, and it’s based on ArcGIS 9.2.

Scenario

You work for a county GIS department and every day people come in, and ask for maps of their property. These are very simple maps – basically just the parcel outline with other base data. Since you are an industrious person, you always have ArcMap open doing some other work, and it’s a pain to close your current map, open the county’s standard Parcel Map mxd, zoom to the parcel and export the map. You want a simpler way to do this.

IMapDocument & MapDocument Class

This is the central point for getting things started. The MapDocument Class can be instantiated and used to load a map document. It’s worth noting that this happens much faster than opening ArcMap – the whole thing – open map, select feature, zoom to it, and export to PDF takes ~ 1 second. Time savings of not watching ArcMap spin up could more than pay for the effort to customize this sample!

'Load up a map into an IMapDocument

 Dim mapDoc As IMapDocument = New MapDocument

 mapDoc.Open(myMxdFile)

Once you have the document, the ArcObjects code needed to zoom to a particular area is very simple. The export to an image is somewhat complex, but the sample wraps that up in the ExportMapToImage and ExportPDF methods.

Sample Solution

The sample solution has two projects – the MapMaker class library that contains the configuration handler, and the code that actually exports the maps, and a simple WinForms app that can drive the class library, Sample data and a MXD file is included in the zip file. All you need to change to get this to run is set the path to the mxd file in the app.config of the MapMakerForm project.

 

MapMaker Class Library

The MapMaker Class library really only has one class of signifigance - the MapMaker itself. Since I like to make things flexible, it also has a configuration section handler, which is used to define the various map files that the class can work with. These are referred to as “MapMakerServices”. The class model is below

A “service” has a name, an mxd file, the layer the query will be run on, and the field that the "where clause" in the query will be applied. The following shows the app.config section for the sample.

  <mapmakerservices tempfolder="G:\temp">

    <service name="ParcelMap" mxdfile="G:\SVN\Research\Automation\Data\parcelmap.mxd" querylayer="Parcels" queryfield="pidn"/>

  </mapmakerservices>

By having a config section, you can cook up a simple application that can access a wide array of map files, without having to change the actual implementation at all. Although there is only one service defined in the sample, the list of MapMakerServices is bound to a combo box.

Functionality

Basically, you pass in a whereclause, the height and width of the map you want, the type of output (JPEG or PDF) and an output file. The MapMaker will then locate the layer specified in the service, query it for a matching feature, zoom to it, and create the map.

 

The data that comes with the sample is of parcels. So the usage in the sample is that the user passed in a PIN, the MapMaker locates and zooms to the parcel, and then exports a map image. The layout in the sample is plain as can be, but I’ll leave the cartography up to you.

 

Anyhow – this is a super simple example of how to automate ArcMap map creation without actually firing up ArcMap.

Download the source code (2.25Mb includes sample data)

Have fun!

Thursday, January 25, 2007
Posted on Thursday, January 25, 2007 9:28:09 PM (Mountain Standard Time, UTC-07:00)  Comments [0] | 
Categories: ArcGIS Devt | ArcMap

I've been working on the System Architecture for a Citrix based enterprise GIS system, and in the process have dug up a bunch of ESRI whitepapers on the topic. If you run ArcGIS in a Citrix environment, these are some good reading...

 
System Design Strategies - ESRI Whitepaper (PDF Jan 2007)

This is the mothership for all ESRI system design topics. Check pages 8-8-3 for the WTS/Citrix server sizing models (partially shown below)

According to the model, you can run 45 ArcMap sessions on an Intel Xeon with 2 Dual Core 3Ghz (5160) CPUs and 16GB of RAM. In case your were wondering - you can pick up a Dell PowerEdge 1950 with this configuration for ~$10,000. And if you want to be really cool, you could go with the quad core CPU's for about ~$2,000 more. RAM starts to get pretty pricy when you go much above 16GB, so while you likely can't stuff 90 users onto a quad core system, for $2,000 more, your 45 users will have a lot more power at their disposal.

Citrix / WTS Support Overview - Whitepaper (HTML - Feb 2006)

This covers the main topics, including how to configure multiple products (ArcView/ArcEditor) to run on the same server.

Force ArcMap to close when logging off Citrix - ESRI Tech Article

Printing from ArcGIS on Citrix - ESRI Whitepaper (PDF - Oct 2006)

System Architecture to Support Printing on Citrix - Citrix Whitepaper (PDF - Nov 2001)

Metaframe Printing Optimization Techniques - Citrix Whitepaper (PDF - Feb 2004)

 

I'll update this post as I find more resources.

Friday, October 27, 2006
Posted on Friday, October 27, 2006 10:41:41 AM (Mountain Daylight Time, UTC-06:00)  Comments [3] | 
Categories: ArcSDE | ArcMap | ArcCatalog | Security
I'm doing some testing with ArcSDE direct connections and Windows Authentication, and needed a quick way to check how the settings were working for different users. Of course I could go to another PC, and login as one of the test users, or setup a bunch of virtual machines, log into each of them as different users, and test the connections that way, but it seemed like a lot of work when there is the "run as" command in Windows XP.

Basically "Run As" allows you to start up an application or process as a different Windows login. Here's the syntax (showing how to start ArcMap)

runas /user:your-domain\testuser /profile /savecred "C:\Program Files\ArcGIS\Bin\ArcMap.exe"

So, I happliy created some test users on our domain, and whipped up a quick batch file, and ran it, expecting ArcMap to fire up as the specified user. Not quite. I got an error when ArcMap was spinning up - a totally generic "ArcMap has encountered a Problem and needs to close" error. Usually this is a really bad type of error, but before I got too wound up, I thought about things a little, and when ArcMap starts up for the first time, it writes a bunch of stuff into the users profile. But this user does not yet have a profile on my machine since they have never logged in!

So I logged myself out, and logged in as the test user - which created the profile. I then promptly logged out and back in as myself again. Now when I run the batch file - ArcMap happily starts up as the specified user. Nice.

If you are using Windows Authentication with ArcSDE, this can be a useful tool - it can allow you to run in a more restricted mode most of the time, but when you need to you can switch over to a login which has rights to make schema modifications - without having to log out of your windows session.