2007. október havi bejegyzések

CD írás távolról

Délután úgy hagytam ott a tanszéki gépemet, hogy még egy VPC image-et tömörített, betettem egy üres lemezt az íróba, gondoltam mire hazaérek kész lesz és majd otthonról elindítom a DVD írást. Csak hogy miután remote desktoppal beléptem és elindítottam a Nerot, ezzel a barátságos hibaüzenettel fogadott:

If you are running Nero burning software via Windows remote login, you might not be able to access your drives for burning. This is a security restriction from Windows.

Próbáltam Disc Infot kérni, de azt mondta, nincs lemez a meghajtóban. Hurrá, sikerült a gépnek megvédenie magát tőlem. Lehetséges megoldások:

  • Nero indítása "eleválva": Run As Administrator.
  • Helyi házirend megpiszkálása, hogy ez többet ne forduljon elő, hiszen legközelebbre úgyis elfelejtem, mi volt a megoldás: Group Policy Editor –> Computer Configuration –> Administrative Templates –> System –> Remote Storage Access –> All Removable Storage: Allow direct access in remote sessions = Enabled.

 

Technorati tags: , ,

Hogyan toljunk ki másokkal?

Íme a legújabb tipp arra, hogyan toljunk ki felebarátainkkal: küldjük neki az anyagainkat XPS formátumban ÉS adjunk a fájlnak ékezetes fájlnevet. Míg egy ékezet nélküi fájlt simán megnyitott nálam Vista alatt az IE-be integrálódó XPS viewer, addig egy á.xps vagy egy é.xps fájl így nyílt meg:

IE XPS hiba 

Látszik, hogy nehezen tanulok, mert először a fájl tulajdonságlapján kerestem a megoldást, az ugyanis CHM fájlok esetén már többször megkeserítette az életemet, de aztán ráböktem a More Information linkre és kitárult előttem a világ, de inkább csak a lényeget másolom ide a négyszer ilyen hosszú, teljes stack trace-t és referencia listát tartalmazó hibaüzenetből:

System.Security.SecurityException: Request for the permission of type ‘System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089’ failed.
   at System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet)
   at System.Security.CodeAccessPermission.Demand()
   at System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()
   at System.Uri.GetConfig(UriIdnScope& idnScope, Boolean& iriParsing)
   at System.Uri.InitializeUriConfig()
   at System.Uri.InitializeUri(ParsingError err, UriKind uriKind, UriFormatException& e)
   at System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind)
   at System.Uri..ctor(SerializationInfo serializationInfo, StreamingContext streamingContext)
   at MS.Internal.AppModel.ApplicationProxyInternal.Run(InitData initData)

A System.Uri miatt kezdtem el gyanakodni a fájl nevére és láss csodát, bejött. Tényleg mennyi köze van a SecurityExceptionhöz… 

Ennyire fogynak a magyarok Redmondban?!

 

Kiegészítés 2007.10.30:

Nagy Levente barátom jelezte, hogy neki működik, így hát kipróbáltam más gépeken is, íme az összesített eredmény:

  • Windows Vista RTM: OK (by Levi)
  • Windows Vista RTM+SP1 béta: OK
  • Windows XP+minden patch: OK
  • Windows Vista RTM+Visual Studio 2008 és .NET 3.5 RC: hiba

Azt is kizárhatjuk, hogy a konkrét XPS fájllal van probléma, próbáltam másokkal is. Szóval lehet, hogy a hiba a Visual Studio 2008 RC változatára korlátozódik. Köszi a tippet, Levi!

 

Technorati tags: , , ,

VS 2008 Beta2 lejár szerdán!

Bizonyára többekhez eljutott már Jeff Beehler blog bejegyzése, miszerint gond van az Orcas beta 2 VPC-kkel. Eredetileg úgy tervezték ezeket az image-eket, hogy 2008. márciusig lehessen rajtuk játszani a Visual Studio 2008 bétájával, de a Windows közbeszólt, ugyans az operációs rendszer lejár 2007. november elsején! Ez egyrészt azért kínos, mert az azóta elkészült release candidate változat nem publikus, másrészt azért, mert előfordulhat, hogy valaki ezeken az image-eken nem csak a VS-t teszteli, hanem a Team Foundation Servert is és számára viszont elég zavaró, ha szerda után nem tudja visszanyerni a forráskódjait.

A kép azért nem ilyen sötét, vannak megoldások:

  • Ha nem csinálunk semmit, az operációs rendszer akkor is használható 1 órán át és utána újraindul. Azután megint lehet vele dolgozni egy órán keresztül és így tovább. Cigi- és kávéfüggők számára ideális 🙂
  • A VPC-ben meg lehet frissíteni az operációs rendszert, amihez természetesen kell legális Windows Server 2003, például MSDN előfizetésből.
  • Át lehet vinni az adatokat másik TFS szerverre, amiről már doksi is létezik. Micsoda remek trükk ez a migráció és a leírás tesztelésére 🙂

Persze akinek mindez nem tetszik várhat, a hét elején szinte biztos, hogy Microsoft kiad újabb VPC image-et. Már csak arra leszek kíváncsi, hogy továbbra is csak beta 2 lesz-e benne, hiszen az kb. 5 hónappal ezelőtti build, miközben egy hónapon belül várjuk az RTM-et…

 

Hova tűnt az Add?

A minap azzal küzdöttem, hogy Visual Studio 2008 Release Candidate alatt próbáltam működésre bírni egy LINQ to SQL-es projektet, ami Beta 2 alatt készült. Keserves küzdés volt, mert nagyon nem hagyta magát, hiányzott neki a generált entitás osztályokon az Add metódus. Reménykedtem, hogy nem azt képzeli, majd én fogom megírni?! A Beta 2-ben még biztosan volt a Table<T> típusnak Add metódusa, most meg csak ezt kántálja a fordító:

‘System.Data.Linq.Table<AccountRequest>’ does not contain a definition for ‘Add’ and no extension method ‘Add’ accepting a first argument of type ‘System.Data.Linq.Table<AccountRequest>’ could be found (are you missing a using directive or an assembly reference?)

Megjegyzem az jópofa, hogy már itt is megjelentek az extension metódusok 🙂  Irány a gugli meg a nagy koppanás, hiszen a Visual Studio RC változat nem publikus, csak egy aránylag szűk kör kapta meg, így nem túl sok sikerrel kerestem arra, hogy breaking changes.

Kereskényi Roby kellett hozzá, hogy megtudjam, a Beta 2 után bizony átneveztek néhány metódust, mert a régi név nem vot egyértelmű:

  • Add –> InsertOnSubmit
  • AddAll –> InsertAllOnSubmit
  • Remove –> DeleteOnSubmit
  • RemoveAll –> DeleteAllOnSubmit

Így valóban egyértelműbb, hogy mikor mi történik, és talán nem fogjuk elfelejteni meghívni a SubmitChanges metódust sem. De ami még jobb, hogy az OnValidate partial method végre megkapja paraméterben, hogy mikor fut, még pedig egy enumeráció formájában:

partial void OnValidate(System.Data.Linq.ChangeAction action);

ahol a ChangeAction így fest:

  namespace System.Data.Linq {
    public enum ChangeAction {
      None = 0,
      Delete,
      Insert,
      Update
    }
  }

Nem hiába, olvasott embernek párja nincs! Különösen, ha Silverlightról bloggol, lehet kapaszkodni…

 
Technorati tags: , , ,

WorkflowInstanceId kötése

Aki csinált már olyan workflowt, amelynek kommunikálnia kellett a külvilággal, annak lehet, hogy szüksége volt arra, hogy a workflow egyedi azonosítóját átadja egy activitynek vagy egy külső komponensnek. Az "átadás" nem jelenthet gondot, hiszen a Workflow Designer okos jószág, lehet benne tulajdonságokat kötögetni.

Sajnálatos módon azonban a WorkflowInstanceId tulajdonságot nem kínálja fel, hiába keresünk System.Guid típushoz párt. Mit tehet ilyenkor az egyszeri workflow programozó: definiál egy tulajdonságot, ami majd becsomagolja ezt a hívást, a tulajdonságot pedig public láthatóságra állítja, hogy köthető legyen:

    public Guid CurrentInstanceId
    {
        get
        {
            return this.WorkflowInstanceId;
        }
    }

Ettől kezdve azonban furcsa jelenségek ütik fel a fejüket, mintha minden a feje tetejére állt volna, például:

  • A workflow példány tökéletesen lefut, de az utolsó activity utáni pillanatban mégis elszáll, pedig oda nem is írtunk kódot.
  • A workflow példány az élete végén 100%-ra terheli a processzort.
  • Elszáll az alkalmazásunk, mert eddigi tökéletesen működő helyen azt a hibaüzenetet kapjuk, hogy az adott művelet csak workflow runtime threaden hajtható végre.
  • A Visual Studio debug módban nem képes megmutatni a workflowt, mert szerinte valami érvénytelen művelettel próbálkozunk, amit csak futási időben lehet elvégezni.

Mindennek az oka pedig ez a kis ártatlannak tűnő tulajdonság. De mi lehet a háttérben? Gondoljunk arra, hogy a this.WorkflowInstanceId tulajdonság kiolvasásának akkor van értelme, ha a workflow példány fut és a workflow runtime tud róla. Vannak azonban olyan pillanatok, amik nem esnek ebbe a csoportba, a Workflow Foundation mégis megpróbálja kiolvasni a workflow példány tulajdonságait. Ez történik például akkor, amikor a WorkflowRuntime.WorkflowCompleted eseménykezelőnek megpróbál átadni egy WorkflowCompletedEventArgs paramétert. Ebben az e paraméterben ugyanis van egy OutputParameters tulajdonság, ami egy szótár típusú gyűjteményen keresztül teszi elérhetővé a workflow példány tulajdonságait. Amikor a rendszer megpróbálja felépíteni ezt a dictionary-t, akkor bizony a workflow példányunk már éppen nem él.

Mit tehetünk akkor, ha mindenáron kötni szeretnénk a folyamat egyedi azonosítóját? Készíthetünk egy publikus property-t, ami nem közvetlenül a this.WorkflowInstanceId tulajdonságot olvassa ki, hanem egy olyan változó tartalmát, ahova korábban már átmásoltuk ezt az értéket. Ha biztosak vagyunk abban, hogy ezt a tulajdonságot a workflow példány futása közben valamelyik activity kiolvassa, akkor írhatjuk például ezt:

    private Guid _instanceId;

    public Guid CurrentInstanceId
    {
        get
        {
            if( this._instanceId.Equals( Guid.Empty ) )
            {
                this._instanceId = this.WorkflowInstanceId;
            }
            return this._instanceId;
        }
    }

Általában érdemes odafigyelni a tervezésnél, hogy egy döglött workflow példányról már csak a tracking service segítségével szerezhetünk információkat.

 

Technorati tags: , ,

Elbénázott workflow verzió frissítés – Nézz és láss!

A minap hozzá kellett nyúlnom egy korábbi Workflow Foundationös projekthez, aminek a verziószámát szépen meg is növeltem a módosítás után. A biztonság kedvéért nyomtam a Studioban egy Rebuild Solutiont, mégis az alkalmazás első futtatásakor ezt a hibaüzenetet kaptam:

Could not load file or assembly ‘Sample.Workflows, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ or one of its dependencies. The located assembly’s manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)

A fene, valami nem jól frissült. Nosza, újrafordítottam a solution minden egyes projektjét. Semmi javulás. Letöröltem majd újra hozzáadtam a szerelvény referenciákat, a hibaüzenet változatlan. Letöröltem a bin és obj mappákat. Ugyanaz a szöveg. Kibányásztam a Windows mappából a Temporary ASP.NET Files mappát és abból is kitakarítottam mindent, de ez sem segített.

Ekkor kezdtem el gyanakodni, hogy rossz helyen keresem a hibát és ekkor ugrott be a hibakeresés első számú alapszabálya: olvasd el a hibaüzenetet, az egészet, elejétől a végéig!

Nézzük meg, melyik sornál keletkezik a hiba! Ez volt a bűnös:

StateMachineWorkflowInstance instance = new StateMachineWorkflowInstance( runtime, workflowInstanceId );

Ezen nincs mit hibáztatni, ennek tényleg kell a workflow típus a szerelvényből. Olvassuk csak végig a hibaüzenetet, ha már rászántuk magunkat. Na de mi van egy .NET-es hibaüzenet végén? A stack trace a maga hosszú és unalmas formájában, jelen esetben ez:

[FileLoadException: Could not load file or assembly ‘Sample.Workflows, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’ or one of its dependencies. The located assembly’s manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)]
   System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) +0
   System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +211
   System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +141
   System.Reflection.Assembly.Load(String assemblyString) +25
   System.UnitySerializationHolder.GetRealObject(StreamingContext context) +355
   System.Runtime.Serialization.ObjectManager.ResolveObjectReference(ObjectHolder holder) +61
   System.Runtime.Serialization.ObjectManager.DoFixups() +2599325
   System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) +203
   System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) +190
   System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream) +12
   System.Workflow.ComponentModel.Activity.Load(Stream stream, Activity outerActivity, IFormatter formatter) +219
   System.Workflow.ComponentModel.Activity.Load(Stream stream, Activity outerActivity) +52
   System.Workflow.Runtime.Hosting.WorkflowPersistenceService.RestoreFromDefaultSerializedForm(Byte[] activityBytes, Activity outerActivity) +114
   System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService.LoadWorkflowInstanceState(Guid id) +249
   System.Workflow.Runtime.WorkflowRuntime.InitializeExecutor(Guid instanceId, CreationContext context, WorkflowExecutor executor, WorkflowInstance workflowInstance) +607
   System.Workflow.Runtime.WorkflowRuntime.Load(Guid key, CreationContext context, WorkflowInstance workflowInstance) +268
   System.Workflow.Runtime.WorkflowRuntime.GetWorkflow(Guid instanceId) +148
   System.Workflow.Activities.StateMachineWorkflowInstance..ctor(WorkflowRuntime runtime, Guid instanceId) +126
   WorkflowManager.GetCurrentState(Guid workflowInstanceId) in c:Documents and SettingsdemoDesktopAspNetWFDemoSolutionWebApp_CodeWorkflowManager.cs:187
   Admin_Default.GetState(Object comment) in c:Documents and SettingsdemoDesktopAspNetWFDemoSolutionWebAdminDefault.aspx.cs:46
   ASP.admin_default_aspx.__DataBinding__control18(Object sender, EventArgs e) in c:Documents and SettingsdemoDesktopAspNetWFDemoSolutionWebAdminDefault.aspx:41
   System.Web.UI.Control.OnDataBinding(EventArgs e) +99
   System.Web.UI.Control.DataBind(Boolean raiseOnDataBinding) +206
   System.Web.UI.Control.DataBind() +12
   System.Web.UI.Control.DataBindChildren() +216
   System.Web.UI.Control.DataBind(Boolean raiseOnDataBinding) +216
   System.Web.UI.Control.DataBind() +12
   System.Web.UI.Control.DataBindChildren() +216
   System.Web.UI.Control.DataBind(Boolean raiseOnDataBinding) +216
   System.Web.UI.Control.DataBind() +12
   System.Web.UI.WebControls.GridView.CreateRow(Int32 rowIndex, Int32 dataSourceIndex, DataControlRowType rowType, DataControlRowState rowState, Boolean dataBind, Object dataItem, DataControlField[] fields, TableRowCollection rows, PagedDataSource pagedDataSource) +221
   System.Web.UI.WebControls.GridView.CreateChildControls(IEnumerable dataSource, Boolean dataBinding) +3004
   System.Web.UI.WebControls.CompositeDataBoundControl.PerformDataBinding(IEnumerable data) +59
   System.Web.UI.WebControls.GridView.PerformDataBinding(IEnumerable data) +11
   System.Web.UI.WebControls.DataBoundControl.OnDataSourceViewSelectCallback(IEnumerable data) +111
   System.Web.UI.DataSourceView.Select(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback) +29
   System.Web.UI.WebControls.DataBoundControl.PerformSelect() +149
   System.Web.UI.WebControls.BaseDataBoundControl.DataBind() +70
   System.Web.UI.WebControls.GridView.DataBind() +4
   System.Web.UI.WebControls.BaseDataBoundControl.EnsureDataBound() +82
   System.Web.UI.WebControls.CompositeDataBoundControl.CreateChildControls() +69
   System.Web.UI.Control.EnsureChildControls() +87
   System.Web.UI.Control.PreRenderRecursiveInternal() +41
   System.Web.UI.Control.PreRenderRecursiveInternal() +161
   System.Web.UI.Control.PreRenderRecursiveInternal() +161
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1360

Ez a hívási lánc önmagában is megérne egy misét és ezt látva talán nem meglepő, hogy a fejlesztők többségéhez hasonlóan én is csak az elejét és a végét néztem meg, abból pedig nem derült ki semmi. 😦

A bőnös pedig ott van a stack trace kellős közepén: WorkflowPersistenceService.RestoreFromDefaultSerializedForm. Ahhoz, hogy egy StateMachineWorkflowInstance példányt kapjunk, a runtime-nak szüksége van a workflow példányra (lásd GetWorkflow fent), amihez természetesen be kell tölteni azt a memóriába (Load), ami jelen esetben azt jelentette, hogy a persistence store-ból vissza kellett állítani a workflow példány állapotát (LoadWorkflowInstanceState). Csakhogy a persistence service sorosításkor eltárolja a workflowt tároló szerelvény teljes nevét, így a verziószámát is, és a visszatöltéskor az alapján próbálja visszaállítani az objektumot sorosított formából.

Tehát ha megváltoztatunk egy workflow definíciót (osztályt), akkor mindig gondoljunk arra, hogy az alapján a definíció alapján már futhatnak workflow példányok, amelyek sorosítva szunnyadnak valamilyen persistence vagy tracking adatbázisban. A friss szerelvényt gond nélkül fogjuk tudni telepíteni és új workflow példányokat is simán fogunk tudni indítani. A hiba csak akkor fog előállni, amikor egy csontváz kiesik a szekrényből: feléled egy várakozó workflow.

 

Haszontalanságok

Minden fejlesztő rémálma, amikor a projekt vezetők kitalálnak olyan funkciókat egy alkalmazásba, amiről messziről látszik, hogy senki nem fogja használni. Ennél már csak az tud zavaróbb lenni, amikor a fejlesztő lelkesedik be és beépít valami olyat a szoftverbe, ami teljesen felesleges, pláne, ha még rosszul is működik.

Ezen cikk apropóját egyébként az adta, hogy a Microsoft Regional Directors levlistán már se szeri se száma az alábbi képeknek:

Vista: About 86 Days and 11 hours remaining

Sőt, van még rosszabb is:

Vista: About 44842 Days and 21 hours remaining

De a teljesség kedvéért el kell mondanunk, hogy vannak optimista pillanatai is a rendszernek:

Vista: About 5 seconds remaining

A Vista SP1 (még béta) egyébként nem sokat javít a rendszeren, de ezt a jelenséget tényleg visszaszorítja (nem azt mondtam, hogy javítja!).

Visszatérve a kezdeti témához: vajon tényleg szükség van erre? Ki a fene találta ki, hogy ebben a dialógus ablakban a  hátralévő időnek meg kell jelennie? Az egésszel nem is lenne baj, ha dolgozna mondjuk 3 másodpercig és az alapján megsaccolná, hogy mennyi van még hátra. De nem, ez vacakol cirka 20 másodpercig, hogy aztán végre füllentsen valamit, majd nagy nehezen elkezdi az érdemi munkát. Legalábbis ez látszik, és az számomra a felhasználói élmény mélypontja.

Ha nekem kellene ilyen ablakot alkotni, én nagyon erősen ragaszkodnék a százalékos megjelenítéshez: átment bájtok száma / átviendő bájtok száma = éppen 27%. Kész. Aki nagyon akarja, rajzoljon alá kígyót.

De persze olyan kígyó legyen, ami csak növekszik és nem megy össze ijedtében. Szerintem mindannyian láttuk már olyan telepítőt, ami:

  • több csíkot húz,
  • egy darabig növeli a csíkot, majd aztán az visszamegy,
  • baromira nem találja el, hogy mikor lesz vége a telepítésnek.

Nem mondom, egy telepítőt megírni nem egyszerű dolog. Még egy-egy lépésének az idejét sem lehet egyszerűen megsaccolni, azt meg még nehezebb, hogy az egész mikor lesz kész. De vajon valóban az idő a fontos? Vajon miért nem lehet oda azt írni, hogy 3. lépés a 89-ből, vagy oda is egy százalékot. Az legalább pontos lenne és elég gyakran változna ahhoz, hogy egyértelmű legyen: a szoftver dolgozik és nem fagyott le (még).

Ennyi bénázás és átverés után már egyszerűen nem hiszek ezeknek a csíkoknak, nem is érdekelnek, sőt mi több, egész egyszerűen zavarnak. Gondolt már valaki arra, hogy az animáló zöld csíkot hány napon keresztül álmodták meg a grafikusok, majd építették be a programozók, hagyták jóvá a tesztelők, szívtak vele a felhasználók, akik jelentették a terméktámogatáson keresztül, hogy aztán ott felvigyék egy bug adatbázisba, ami alapján végül a programozók kijavítják – hogy aztán kör kezdődhessen szinte elölről?

Csak én gondolom úgy, hogy az egész teljesen felesleges, vagy más szerint is mindenki jobban járt volna, ha azon a pénzen az állatvédőket vagy a Vöröskeresztet támogatják?