Dynamically Detecting the Data Directory

My Slideshow control (which I discussed in a previous post) looks in a data directory called images for the images it wishes to display. This adds a curious twist to application development and deployment. When I deploy an application using ClickOnce, I will want to include these files in the data directory for that application. (See Accessing Local and Remote Data in ClickOnce Applications for the 411 on data directories.) Under ClickOnce, the data directory is a long, garbled path, such as C:\Documents and Settings\username\Local Settings\Apps\2.0\Data\DXQV72TZ.3GR\EERQ26OC.Y91\test..tion_9d0ab10300fca701_0001.0000_edbfed2eeae795d5\Data. I can find the value of this path (deliberately obscured to foil easy discovery of my app's data) by calling Application.UserAppDataPath.

However, when I'm debugging my app in Visual Studio, the data directory path is set to C:\Documents and Settings\username\Application Data\Publisher Name\App Name\Version Number. Now, I could manually stuff my data files in here for the purposes of debugging. Indeed, for most of my project, that's what I did. But this makes it difficult to ship the project to other developers and have it work out of the box. Also, it requires that I switch the files around whenever I update the version, or alter the application or publisher name. The chance that data files will become out of sync during development is high.

To get around this mess, I wrote a small snippet of code that detects where the data directory resides for the current instance of the application. If ApplicationDeployment.IsNetworkDeployed is true, I use Application.UserAppDataPath as the data directory. If I'm not running as a ClickOnce application, I first look at Application.ExecutablePath to see if the images directory is in the same directory as the executable. If it isn't, I assume I'm running in debug mode under Visual Studio, and look two directories up for my precious data.

    Private Function GetDataDirectory() As String
        Dim _ExeDir As String
        Dim _PathStr As String

        If (ApplicationDeployment.IsNetworkDeployed) Then
            _PathStr = Application.UserAppDataPath & "\images"
        Else
            ' Check if it's in the exe directory, or in a debug folder dir.
            _ExeDir = Application.ExecutablePath.Substring(0, _
            Application.ExecutablePath.LastIndexOf("\"))
            _PathStr = _ExeDir & "\images"
           

            If (Not Directory.Exists(_PathStr)) Then
                _PathStr = _ExeDir & "\..\..\images"
                If (Not Directory.Exists(_PathStr)) Then
                    Throw New DirectoryNotFoundException("Cannot find directory " & _PathStr & ". Fatal error.")
                End If
            End If
        End If

        GetDataDirectory = _PathStr
    End Function

This code not only allows the app to switch between debug testing and ClickOnce deployment. It it also supports good ol' xcopy deployment. So long as the images directory is at the same level as the EXE, I'm good to go.

Populating the Data Directory for ClickOnce Deployment

How do I add the images directory and its files to my ClickOnce deployment? As far as Visual Studio is concerned, this is the easy part!

  1. Right-click your project and select Add->New Folder.... In my example, I name my folder images.
  2. Right-click the folder and select Add Existing Items....
  3. Add all of the items from your folder into the Visual Studio project folder.
  4. Right-click your project again and select Properties. Click the Publish tab, and then click the Application Files... button.
  5. For each of the files you just added, change Publish Status to Data File. Leave Download Group set to (Required).