8

I'm trying to move an image and a style sheet from a user control to embedded resources in the assembly. I have used Reflector to see that the image and .css file are embedded in the assembly, but when I try to access them using the URL created by ClientScript.GetWebResourceUrl(), the resource is not found. I'm stumped.

Assembly default namespace:

TestWebApp

The paths to the files (marked as BuildAction: Embedded Resource) are

TestWebApp/Resources/CSS/PaymentHistory.css
TestWebApp/Resources/Images/loading.gif

And so my resources are registered as:

[assembly: WebResource("TestWebApp.Resources.CSS.PaymentHistory.css", "text/css", PerformSubstitution = true)]
[assembly: WebResource("TestWebApp.Resources.Images.loading.gif", "image/gif")]

User Control (in the same assembly) that accesses the resources :

TestWebApp.UserControls.PaymentHistory

To simplify, I'm currently just trying to reference the image and not the stylesheet. In my user control's Page_Load, I set the ImageUrl of an Image control to the resource URL:

image1.ImageUrl = Page.ClientScript.GetWebResourceUrl(this.GetType(), "TestWebApp.Resources.Images.loading.gif");

At runtime, everything appears to work without errors but it renders a broken image. Here is the rendered image source:

<img style="border-width:0px;" src="/WebResource.axd?d=8fC_1tLPjrUCxmFc_Q2MKY0-pHAak-sTWkpLLV3D56H_c08LujXC63ia2PNICE65_i-Q4JqprAigLpbrXG-rIAr6ePO4HHcdQKgdd3szlThv2gizxOJLJsPRNe-b_M6ApTwPsH_5oZAuONTN0cumOTRr1nA1&amp;t=635133745137507721" id="ph1_image1">

If I navigate to that URL in my browser, I get a 404, The resource cannot be found. What am I doing wrong?

EDIT: There must be something fundamental I'm not understanding and/or I'm doing something really stupid. Here is a simple VS 2010 example. I have followed all of the required steps I'm aware of to embed JScript1.js and access it via WebResource.axd, but it gets the error.

7
  • 2
    In debug, verify that Assembly.GetExecutingAssembly().GetManifestResourceNames(); returns the resource names you are expecting. Also, consider using a string constant to insure that you are using the same string in your WebResource attribute and your GetWebResourceUrl command. Commented Aug 29, 2013 at 17:26
  • @Grax - Thanks, I appreciate that. Using GetManifestResourceNames(), I've verified, in multiple ways, that the strings in use are all definitely matching and correct. I also tried moving the resources out of their subfolders into the project root. I noticed I had an incorrect content type "img/gif" that I've now changed to "image/gif" but, to my surprise, didn't fix it. I also noticed the output window is logging two System.Web.HttpExceptions that state "This is an invalid webresource request.". I'm not sure why it's two...my guess was firefox or "Cassini" automatically retries. Commented Aug 29, 2013 at 18:47
  • @Grax I've updated the question with an example if you'd care to try it out Commented Aug 30, 2013 at 17:02
  • I found your answer stackoverflow.com/questions/12675520/… You need to convert this to a web application or put the embedded resource in a separate dll. Commented Aug 30, 2013 at 18:49
  • @Grax It is a Web Application. The project output type is Class Library and it compiles to a dll. I just verified the Assembly.cs file's Build Action is set to compile too. Did the example project not appear to be a Web Application? Commented Aug 30, 2013 at 19:18

4 Answers 4

12
+50

In the Default.aspx.cs file of your example project, change this.GetType() to typeof(_Default):

Page.ClientScript.RegisterClientScriptInclude("JScript1",
    Page.ClientScript.GetWebResourceUrl(typeof(_Default), "EmbeddedResources.JScript1.js"));

Similarly, in the PaymentHistory.ascx.cs file, change this.GetType() to typeof(PaymentHistory):

image1.ImageUrl = Page.ClientScript.GetWebResourceUrl(
    typeof(PaymentHistory), "TestWebApp.Resources.Images.loading.gif");

Explanation: GetWebResourceUrl looks at the type argument to determine which assembly contains the embedded resource. Specifying this.GetType() as the type is incorrect because, in an .aspx or .ascx code-behind class, this.GetType() refers not to that class but rather to the derived class that gets dynamically generated from the .aspx or .ascx markup. This derived class resides in a separate assembly, so GetWebResourceUrl can't find the embedded resource.

Sign up to request clarification or add additional context in comments.

1 Comment

Wow, I can't believe I didn't think to check this.GetType() to troubleshoot. However, I never knew a class was generated for precompiled page & controls. Thanks so much, Michael!
1

Is the resource in a separate project or in the same project as your User Control? If separate you have to substitute this.GetType() with an object's GetType() function that resides in the separate project.

If in the same project, just do Page.GetType(), because you want a reference to the page and not the user control

1 Comment

Your question is answered in my OP, but thanks for pointing that detail out. Using Page.GetType() does not fix it. All the method is supposed to need is a type in the assembly that contains the resource. In my case both the page and the user control meet that criteria so there should be no difference.
1

First of all, the type that you are passing to the GetWebResourceUrl call (or to the RegisterClientScriptResource I show below) is actually there to point to which assembly contains your resource. The problem is that "this.GetType()" is returning a type that is not in the current executing assembly (as strange as that may be).

The following 2 lines demonstrate the issue.

        Response.Write(this.GetType().Assembly.FullName + "<br>");
        Response.Write(Assembly.GetExecutingAssembly().FullName + "<br>");

The first line returns the name of a "App_Web_??????" assembly. The second returns the expected "EmbeddedResources" assembly.

In the call below, I just pass in the first type I get back from the executing assembly and the call works. Page.ClientScript.RegisterClientScriptResource(Assembly.GetExecutingAssembly().GetTypes()[0], names[0]);

this.GetType() actually returns a type that the web server creates that inherits from your type. That is why typeof(_Default) would also work to designate the correct assembly.

1 Comment

Using GetTypes()[0] works, but it seems a bit inefficient to load all the types in the assembly when all you need is one.
0

Here you can see a code in Vb.NET that uses the Reflection library to detect the current Assembly Name and the current NameSpace.

If you concatenate the namespace with embedded image name, you can use the command Page.clientScript.GetWebResourceURL to generate a link for image as you see in first function

In second function, you see a loop in all of resources name until find the complete name of embedded resouce.

Friend Class ReadResources

    ' Get our assembly.
    Private Shared executingAssembly As System.Reflection.Assembly = Reflection.Assembly.GetExecutingAssembly()

    ' Get our namespace.
    Private Shared myNamespace As String = executingAssembly.GetName().Name.ToString()

    ''' <summary>
    ''' Generate resource link
    ''' </summary>
    Friend Shared Function GetResourceLink(ByVal ref As String,
                                           ByVal obj As Object,
                                           ByVal page As Web.UI.Page) As String
        Dim out As String = Nothing
        out = Page.ClientScript.GetWebResourceUrl(obj.GetType, myNamespace & "." & ref)

        If out Is Nothing OrElse out.Length <= 0 Then
            out = FindResource(ref, obj)
        End If

        Return out
    End Function

    Friend Shared Function FindResource(ByVal reference As String,
                                        ByVal obj As Object) As String
        Dim out As String = ""
        For Each embedded In obj.GetType().Assembly.GetManifestResourceNames()
            If embedded.Contains(reference) Then
                out = embedded
                Exit For
            End If
        Next
        Return out
    End Function


End Class

2 Comments

Please avoid code-only answers. Consider adding an explanation to your code
following your recommendation

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.