14

Given a pack:// URI, what's the best way to tell whether a compiled resource (e.g. a PNG image, compiled with a Build Action of "Resource") actually exists at that URI?

After some stumbling around, I came up with this code, which works but is clumsy:

private static bool CanLoadResource(Uri uri)
{
    try
    {
        Application.GetResourceStream(uri);
        return true;
    }
    catch (IOException)
    {
        return false;
    }
}

(Note that the Application.GetResources documentation is wrong -- it throws an exception if the resource isn't found, rather than returning null like the docs incorrectly state.) (The docs have been corrected, see comments below)

I don't like catching exceptions to detect an expected (non-exceptional) result. And besides, I don't actually want to load the stream, I just want to know whether it exists.

Is there a better way to do this, perhaps with lower-level resource APIs -- ideally without actually loading the stream and without catching an exception?

Joe White
  • 94,807
  • 60
  • 220
  • 330

1 Answers1

14

I've found a solution that I'm using which doesn't work directly with a pack Uri but instead looks up a resource by it's resource path. That being said, this example could be modified pretty easily to support a pack URI instead by just tacking on the resource path to the end of a uri which uses the Assembly to formulate the base part of the URI.

public static bool ResourceExists(string resourcePath)
{
    var assembly = Assembly.GetExecutingAssembly();

    return ResourceExists(assembly, resourcePath);
}

public static bool ResourceExists(Assembly assembly, string resourcePath)
{
    return GetResourcePaths(assembly)
        .Contains(resourcePath.ToLowerInvariant());
}

public static IEnumerable<object> GetResourcePaths(Assembly assembly)
{
    var culture = System.Threading.Thread.CurrentThread.CurrentCulture;
    var resourceName = assembly.GetName().Name + ".g";
    var resourceManager = new ResourceManager(resourceName, assembly);

    try
    {
        var resourceSet = resourceManager.GetResourceSet(culture, true, true);

        foreach(System.Collections.DictionaryEntry resource in resourceSet)
        {
            yield return resource.Key;
        }
    }
    finally
    {
        resourceManager.ReleaseAllResources();
    }
}
jpierson
  • 16,435
  • 14
  • 105
  • 149
  • 5
    It looks like the returned paths from `GetResourcePaths` is lowercased so make sure to use `.Contains(resourcePath.ToLower());` in the `ResourceExists` method. – Simon Fischer Apr 17 '12 at 11:51
  • 2
    Make that `.ToLowerInvariant()` just to be safe. – RandomEngy Oct 11 '15 at 06:03
  • Would be nice to see a general solution that searched through all loaded assemblies (maybe making an educated first-guess by parsing assemblyname from the "pack:" string.) – Kirk Kuykendall Aug 07 '19 at 14:09
  • 1
    This is the only real answer to finding if a resource exists, that you compile into an assembly using Build Action > Resource. All other GetResourceName solutions do NOT work. Thank you so much! @Jpierson – Jason Stevenson Apr 28 '22 at 23:15