Tag Archives: Errors and Exceptions

X509Certificate2: CryptographicException: The system cannot find the file specified.

Pokud vytváříte ve webové aplikaci X509Certificate2

myCertificate = new X509Certificate2(rawData, password);
//nebo
myCertificate = new X509Certificate2(fileName, password);

pak můžete být po nasazení své funkční aplikace na produkční server (např. hosting) obdařeni výjimkou

System.Security.Cryptography.CryptographicException: The system cannot find the file specified.
at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
at System.Security.Cryptography.X509Certificates.X509Utils._LoadCertFromBlob(Byte[] rawData, IntPtr password, UInt32 dwFlags, Boolean persistKeySet, SafeCertContextHandle& pCertCtx)
at System.Security.Cryptography.X509Certificates.X509Certificate.LoadCertificateFromBlob(Byte[] rawData, Object password, X509KeyStorageFlags keyStorageFlags)
at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(Byte[] rawData, String password)

…přestože soubor s certifikátem buď zaručeně existuje (fileName), nebo se ani nepoužívá (rawData).

Problém souvisí s tím, že v produkčním prostředí Vaše aplikace beží pod uživatelským účtem, který nemá založen profil, který se má certifikát vytvořit (volně přeloženo, omlouvám se za případnou nepřesnost).

Řešením je použití přetížení constructoru, kterým zvolíte cílové uložiště certifikátu:

 

myCertificate = new X509Certificate2(rawData, password, X509KeyStorageFlags.MachineKeySet);
//nebo
myCertificate = new X509Certificate2(fileName, password, X509KeyStorageFlags.MachineKeySet);

Pozor na UpdatePanel a unikátní názvy (ID) controlů

AJAXový UpdatePanel se chová zvláštně vůči logice naming-containerů, pokud se tedy potkáte s názvy (ID) controlů, nepůjde Vaše stránka zkompilovat. Stačí zkusit následujících jednoduchý snippet:

<asp:TextBox ID="SomethingTB" runat="server" />
<asp:Repeater ID="MyRepeater" runat="server">
  <ItemTemplate>
    <asp:UpdatePanel runat="server">
      <ContentTemplate>
        <asp:TextBox ID="SomethingTB" runat="server" />
      </ContentTemplate>
    </asp:UpdatePanel>
  </ItemTemplate>
</asp:Repeater>

…při kompilaci budete obšťastněni chybovými hláškami

D:\Development\UpdatePanelCompiler\Default.aspx(18,57): error CS0102: The type '_Default' already contains a definition for 'SomethingTB'
c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\updatepanelcompiler\1c44388f\ad38e94\App_Web_r7xfjqxf.0.cs(231,59): error CS0111: Type 'ASP.default_aspx' already defines a member called '__BuildControlSomethingTB' with the same parameter types

NullReferenceException v DefaultWsdlHelpGenerator.aspx při přístupu k webovým službám

Vytváříme klasickou webovou službu publikovanou pomocí ASMX. Při pokusu o přístup k této webové službě přes internetový prohlížet obdržíme výjimku NullReferenceException – a to ještě žádnou metodu nevoláme, jen prohlížíme dostupné služby.
Debugger se nám zastavuje v souboru DefaultWsdlHelpGenerator.aspx na řádku 1335:

OperationBinding FindHttpBinding(string verb) {
        foreach (ServiceDescription description in serviceDescriptions) {
            foreach (Binding binding in description.Bindings) {
                HttpBinding httpBinding = (HttpBinding)binding.Extensions.Find(typeof(HttpBinding));

Vskutku pozoruhodné je pak řešení problému: Ve web.configu máme v sekci <pages> nastavení autoEventWireUp=“false“. Po odstranění tohoto nastavení přístup k dokumentaci webových služeb funguje.
DefaultWsdlHelpGenerator.aspx je generátor dokumentace pro zobrazení v prohlížeči. Ten se evidentně kompiluje s použitím nastavení aplikace a v kódu spoléhá na zavolání metody Page_Load pomocí automatického navázání vybraných událostí (AutoEventWireUp). K dispozici máme i zdrojový kód tohoto souboru, standardně se nachází v C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG.
(Runtime .NET Framework 2.0)

Workaround

Na vývojářské mašině se dá taky rovnou zeditovat soubor C:\Windows\Microsoft.NET\Framework\v2.0.50727\CONFIG\DefaultWsdlHelpGenerator.aspx a na první řádek přidat:

<%@ Page AutoEventWireup="true" %>

HttpException: The Controls collection cannot be modified because the control contains code blocks

Tuto krásnou výjimku můžeme znenadání dostat (mimo jiné), pokud budeme v controlu <head runat=“server“> používat code-bloky, např.

<head runat="server">
    <title>HAVIT Goran</title>
    <link rel="stylesheet" type="text/css" href="~/templates/styles/havit.css" />
    <link rel="stylesheet" type="text/css" href="~/templates/styles/global.css" />
    <script type="text/javascript" src="<%= ResolveUrl("~/templates/scripts/HavitScripts.js") %>"></script>
    <asp:ContentPlaceHolder ID="HeadtailCPH" runat="server" />
</head>

Onen ďábelský blok <%= … %> nemusí zpočátku vůbec vadit, pokud však někdy později do stránky přidáme funkcionalitu, která by chtěla měnit Page.Header, např. umístíme do stránky control, který si bude chtít přilinkovat přes Header vlastní styly a skripty, pak budeme obšťastněni krásnou výjimkou System.Web.HttpException:

The Controls collection cannot be modified because the control contains code blocks (i.e. <% … %>).

…lepší je tedy code-bloky <% %>, resp. <%= %> v <head runat=“server“> nepoužívat (ještě lépe je nepoužívat vůbec) a nahradit je jiným způsobem. V tomto konkrétním případě třeba za <%# ResolveUrl(…) %> data-bindovací konstrukci (samozřejmě pak musíme volat Page.Header.DataBind()).

AJAX: Nefunkční validátory uvnitř UpdatePanelu

Microsoftu se podařil pozoruhodný kousek, standardní validátory ASP.NET 2.0 nefungují korektně uvnitř ajaxového UpdatePanelu – při změně obsahu panelu se původní validátory neodregistrují. Např. tak nefungují validátory v GridView editaci, ve wizzardech, atp. Obvykle vyskakuje krásná JScript chyba „null is null“.

Předchozí beta verze AJAXu to řešily upravenými verzemi validátorů, které korektně používaly ScriptManager ke své přeregistraci. V RTM verzi AJAXu však již tyto validátory nejsou a Microsoft se rozhodl, že je bude aktualizovat všem přes WindowsUpdate, nezávisle na AJAXu.

Ta horší zpráva je, že tak dosud neudělal, přestože AJAX už dávno releasoval.

…takže kdo nemá nervy čekat na opravené validátory, musí sám ručně (přes <tagMapping>) použít jejich aktualizované verze (nebo odtud), tak jak tomu bylo v betách.

Podivné chování session, přegenerovávání SessionID

V aplikaci je přihlašovací dialog. Zde by měla aplikace vytvořit session a poslat její identifikátor klientovi, což se stane.

Po přihlášení se zobrazí stránka obsahující frames, každý frame (a iframe) však znovu dostává jiný identifikátor session. To je špatné, protože každému frame je potom poslána jiný identifikátor session a tudíž má jeden uživatel více session.

Problém nenastane, pokud do přihlašovacího dialogu umístím kód:

Session["some_key"] = "some_value";
Session.Clear();

Trochu mě překvapuje, že session Session.Clear() je možné provést a je možné mít tedy prázdnou session. ASP.NET tedy pravděpodobně ruší session, pokud s ní nebylo pracováno (nebylo k ní přistoupeno).

Celý problém jsem vyřešil potomkem HttpApplication (Global.asax):

private void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
    // tento kod opravuje podivne chovani Session.
    if (Context.Session != null)
    {
        Context.Session[&quot;__Application_PreRequestHandlerExecute&quot;] = &quot;some_value&quot;;
        Context.Session.Remove(&quot;__Application_PreRequestHandlerExecute&quot;);
    }
}

BUG: MasterPage zobrazuje default hodnotu ContentPlaceHolderu a ignoruje Content

Narazil jsem na zajímavý bug v ASP.NET 2.0 (2.0.50727)…

Pokud v ContentPlaceHolderu definujeme default obsah a použijeme v něm ebedded code block (starý vnořený ASP-style blok kódu <% %>, ale i <%= %>), pak ASP.NET ignoruje Content definovaný v konkrétních stránkách a stále zobrazuje pouze default obsah z MasterPage.

...
<asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
   <% %>   <-- už tohle vadí
   <%= "Tohle taky vadí" %>
   <%= ResolveUrl("~/takhle-na-to-asi-narazime/") %>
</asp:ContentPlaceHolder>
...

Zajímalo mě, kde je pravděpodobná příčina problému, takže jsem se díval, co z toho ASP.NET stvoří a jak se to liší od funkční podoby. Zásadní rozdíl je už v metodě __BuildControl, kde narozdíl od korektní podoby (ContentPlaceHolder1 s jedním Labelem):

private ContentPlaceHolder __BuildControlContentPlaceHolder1()
{
      ContentPlaceHolder holder1 = new ContentPlaceHolder();
      this.ContentPlaceHolder1 = holder1;
      holder1.ID = "ContentPlaceHolder1";
      if (base.ContentTemplates != null)
      {
            this.__Template_ContentPlaceHolder1 = (ITemplate) base.ContentTemplates["ContentPlaceHolder1"];
      }
      if (this.__Template_ContentPlaceHolder1 != null)
      {
            this.__Template_ContentPlaceHolder1.InstantiateIn(holder1);
            return holder1;
      }
      IParserAccessor accessor1 = holder1;
      accessor1.AddParsedSubObject(new LiteralControl("\r\n\t\t\t"));
      Label label1 = this.__BuildControlTest();
      accessor1.AddParsedSubObject(label1);
      accessor1.AddParsedSubObject(new LiteralControl("\r\n        "));
      return holder1;
}

…chybí řádek return holder1;

private ContentPlaceHolder __BuildControlContentPlaceHolder1()
{
      ContentPlaceHolder holder1 = new ContentPlaceHolder();
      this.ContentPlaceHolder1 = holder1;
      holder1.ID = "ContentPlaceHolder1";
      if (base.ContentTemplates != null)
      {
            this.__Template_ContentPlaceHolder1 = (ITemplate) base.ContentTemplates["ContentPlaceHolder1"];
      }
      if (this.__Template_ContentPlaceHolder1 != null)
      {
            this.__Template_ContentPlaceHolder1.InstantiateIn(holder1);
            // return holder1;  <-- pravděpodobně chybí
      }
      holder1.SetRenderMethodDelegate(new RenderMethod(this.__RenderContentPlaceHolder1));
      return holder1;
}
 
private void __RenderContentPlaceHolder1(HtmlTextWriter __w, Control parameterContainer)
{
      __w.Write("\r\n\t\t\t");
}

Jinak bug je již reportován v Microsoft Connect (Feedback center), můžete se připojit k hlasování…