\


 Wednesday, 28 May 2008
Prezentace z přednášky Slasti, strasti a propasti vývoje pro Windows Mobile

Slidy z přednášky Slasti, Pasti, strasti a propasti nativního/řízeného (managed) vývoje pro zařízení s operačním systémem Windows Mobile si můžete nyní stáhnout.

Slasti_Strasti_Propasti_WindowsMobile

 

 

 



Wednesday, 28 May 2008 22:47:45 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  Compact .Net Framework | Mobilitky | Nativní kód | Net Monitor | Wifi Profiles Windows Mobile


 Sunday, 18 May 2008
Pozvánka na říjnový termín kurzu Objektovými principy a návrhovými vzory řízený design a vývoj kvalitních aplikací

Dovolte mi, abych Vás všechny pozval na podzimní termín kurzu Objektovými principy a návrhovými vzory řízený design a vývoj kvalitních aplikací.  Po vyhodnocení některých připomínek jsme se rozhodli změnit místo, kde se školení uskuteční.

Hotel VILLA Praha
Okrajní 1
100 00 Praha 10

Změna místa přinese účastníkům hlavně možnost zaparkovat auto přímo u hotelu a také to, že oběd (v ceně školení) dostaneme přímo v hotelu (oběd může prý obsluha donést až do učebny) a nebudeme tak nuceni trávit dlouhý čas čekáním na nějakého unuděného a pomalého pingla v poloprázdné pizzerii nebo podobném podniku s mizernou kuchyní.

Termín:

20. 10. - 22. 10. 2008

Organizační informace ke kurzu

Program kurzu

Zaregistrované ohlasy na školení :

http://blog.renestein.net/ct.ashx?id=10d7acf8-1026-43fe-b1f1-54fccb69105b&url=http%3a%2f%2fwww.jirifabian.net%2fwordpress%2f%3fp%3d157

http://blog.renestein.net/ct.ashx?id=10d7acf8-1026-43fe-b1f1-54fccb69105b&url=http%3a%2f%2fwww.rarous.net%2fclanek%2f143-skoleni-oop-uml-a-navrhovych-vzoru.aspx

 

Update: Nový ohlas

"Rene Stein pořádá opět svůj kurz o OOP - více na jeho blogu. Měl jsem možnost účastnit se školení od pana Kravala i školení pana Steina a doporučil bych spíše ten posledně jmenovaný. Je tak nějak více o praxi a z praxe. Oproti tomu kurz pana Kravala je spíše teoretický, Ale každému může vyhovovat něco jiného." Zdroj  - Martin's world



Sunday, 18 May 2008 12:11:09 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  Analytické drobky | Kurzy UML a OOP | Návrhové vzory | UML


 Friday, 09 May 2008
LINQ II - přetypovávání i vnořených anonymních datových typů z jiné assembly

V předchozím spotu jsem byl schopen pracovat s anonymními datovými typy, i když byly dotazy a výsledné sady dat vytvořeny v jiné assembly. Odstranění vrozené xenofobie v praxi.:)

Náš kód ale vygeneruje výjimku, jestliže anonymní datový typ z jiné assembly obsahuje další vnořené anonymní datové typy jako v následujícím upraveném příkladu. Vlastnost InnerAT vrací další anonymní datový typ, který  pro zajímavost obsahuje odkaz ještě na další anonymní datový typ.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LINQTEST
{
    public class TestAT
    {
        public static object GetResult()
        {
            string[] rows = { "Toyota", "Lexus", "Audi" };

            var test = from row in rows
                       select new
                       {
                           FirstLetter = row[0],
                           Index = 110,
                           Original = row,
                           InnerAT = new { X = row[1], B = new {A=1}}
                       };
            
            return test;

        }
    }
}

Řešení spočívá v úpravě extenzí a to tak, že přidáme privátní metodu GetTypeInstance a přeneseme do ní většinu kódu z extenze ToAnonymousType. Metoda GetTypeInstance při neshodě datového typu očekávaného parametrem "našeho - v naší assembly dostupného" konstruktoru anonymního datového typu a datového typu vlastnosti anonymního datového typu z "cizí" assembly rekurzivně přenese data z "cizího" anonymního datového typu do "našeho".

 

using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Reflection;
using System.Collections;
using LINQTEST;

namespace LINQAnonymous
{
    /// <summary>
    /// Rozšíření pro LINQ
    /// </summary>
    static class RSLinqExtensions
    {

        /// <summary>
        /// Metoda přetypuje objekt na anonymní typ, jehož struktura byla předána v parametru <paramref name="prototype"/>
        /// </summary>
        /// <typeparam name="T">Kompilátorem odvozený anonymní typ</typeparam>
        /// <param name="prototype">Prototyp se strukturou anonymního typu</param>
        /// <returns>Instanci anonymního typu, nebo null, jestliže konverzi nelze provést</returns>
        /// <remarks>Metoda se pokusí převést data z různých assembly</remarks>
        public static T ToAnonymousType<T>(this object obj, T prototype)
                                        where T: class
        {
            
            
            T atiObj = obj as T;
            
            if (atiObj == null)
            {
                
                atiObj = GetTypeInstance(obj, prototype.GetType()) as T;
               
            }
                                                     
            return (atiObj);
        }
    

        private static object GetTypeInstance(object obj, Type expected)
        {
            object atiObj = null;

            ConstructorInfo constructorInfo = expected.GetConstructors()[0];
                
                if (constructorInfo == null)
                {
                    return null;
                }

                ParameterInfo[] paramInfos = constructorInfo.GetParameters();                
                PropertyInfo[] origProperties = obj.GetType().GetProperties();
                

                if (paramInfos.Count() != origProperties.Count())
                {
                    return null;
                }

                object[] paramArgs = new object[paramInfos.Count()];


                for (int i = 0; i < paramArgs.Length; i++)
                {
                    PropertyInfo origProperty = origProperties.Where(prop => prop.Name == paramInfos[i].Name).FirstOrDefault();

                    if (origProperty == null)
                    {
                        return null;
                    }

                    object val = origProperty.GetValue(obj, null);
                    if (origProperty.PropertyType != paramInfos[i].ParameterType)
                    {
                        val = GetTypeInstance(val, paramInfos[i].ParameterType);
                    }

                    paramArgs[i] = val;
                }

                atiObj = constructorInfo.Invoke(paramArgs);
                return atiObj;
        }
        /// <summary>
        /// Metoda vrátí
        /// </summary>
        /// <typeparam name="T">Kompilátorem odvozený anonymní typ</typeparam>
        /// <param name="prototype">Prototyp se strukturou anonymního typu</param>
        /// <returns>List instancí anonymního typu, nebo null, jestliže konverzi nelze provést</returns>
        /// <remarks>Metoda se pokusí převést data z různých assembly</remarks>
        public static List<T> CastToList<T>(this object obj, T prototype)
                                 where T : class
        {
            List<T> list = new List<T>();
            IEnumerable<T> enumerable = obj as IEnumerable<T>;

            if (enumerable != null)
            {
                list.AddRange(enumerable);
            }
            else
            {
                    IEnumerable enumObjects = obj as IEnumerable;
                    if (enumObjects == null)
                    {
                        return null;
                    }
                    
                foreach (object enumObject in enumObjects)
                    {
                        T currObject = ToAnonymousType(enumObject, prototype);
                        if (currObject == null)
                        {
                            //K čistění listu by neměl být důvod, ale garantujeme, že nevrátíme částečně naplněný list
                            list.Clear();
                            return list;
                        }

                        list.Add(currObject);
                    }
                
            }

            return list;
        }
    }
    

Při přetypovávání stačí stále jen zadat prototyp anonymního datové typu.

 

//Anonymní typ z jiné assembly!
            var result2 = TestAT.GetResult().CastToList(new {FirstLetter = default(char), 
                                                        Index =default(int),
                                                        Original = default(string),
                                                        InnerAT = new { X = default(char), B = new { A = default(int) } }
            })
                                                       ;
            foreach (var res in result2)
            {
                Console.WriteLine(res.FirstLetter);
                Console.WriteLine(res.Original);
            }


            Console.WriteLine(TestAT.
                                    GetResult().
                                    CastToList(new
                                    {
                                        FirstLetter = default(char),
                                        Index = default(int),
                                        Original = default(string),
                                        InnerAT = new { X = default(char), B = new { A =default(int)} }
                                    }
                                    ).
                                    Where(car => car.FirstLetter == 'T')
                                     .FirstOrDefault()
                                     .ToString());
            Console.ReadLine();


Friday, 09 May 2008 09:09:26 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  .NET Framework | ASP.NET | Compact .Net Framework | LINQ | Windows Forms


 Thursday, 08 May 2008
LINQ - anonymní typ deklarovaný v jedné assembly dostupný v metodách další assembly?
.Net Framework

Anonymní datové typy v LINQu nelze použít jako návratový typ z metody a jediný způsob, jak anonymní typ z metody předat, je použít jako návratovou hodnotu typ object, protože v .Net Frameworku - jak je všeobecně známo - všechny třídy přímo či nepřímo dědí z třídy Object. Navíc platí, že anonymní typ je kompilátorem vždy deklarován jako internal a jeho použití je tak striktně omezeno na jednu assembly.

Jde o rozumné omezení a anonymní datové typy bychom neměli zneužívat k nesmyslům typu "hezká syntaxe pro generování objektů Dictionary", které si našly cestu i do připravovaného (a už dnes "přehypovaného") MVC frameworku pro ASP.NET.

V různých diskuzích se ale stále dokola objevuje dotaz, jak anonymní typ z metody vráti. A každé omezení se dá samozřejmě obejít - když nefunguje ani bodový systém na silnicích, proč nenajít hrubý trik ve stylu "osoby blízké" i pro erozi různých omezení u anonymního datového typu. :) Znovu alibisticky varuji všechny před zařazením následujících nehezkých triků do svého arzenálu běžných postupů při vývoji, protože všechny postupy spoléhají na chování kompilátoru C#, které není garantováno a které se může v další verzi nebo i jen při vydání service packu .Net Frameworku bez varování změnit.

Pro vrácení anonymního datového typu z metody použijeme hezký hack od Tomáše, který se ujal pod názvem "Cast By Example". Zjednodušeně řečeno - sice nemůžeme používat při přetypovávání názvy anonymních datových typů (tříd), protože anonymní datové typy jsou generovány až při kompilaci, ale můžeme kompilátoru dát při přetypování "vzor", jaký anonymní datový typ nám bude vyhovovat. Podrobnosti si můžete najít v odkazovaném článku Tomáše Petříčka  = zde jen připomenu, že technika využívá současného chování kompilátoru, který pro různé deklarace anonymních datových typů se stejnými vlastnostmi generuje v jedné assembly vždy právě jednu třídu.

Napsal jsem jednoduše použitelné extenze, které vám dovolí nejen přetypovat jednu instanci "object" na anonymní datový typ, ale můžete přetypovat množiny záznamů na (anonymně ;-)) typovou kolekci List<NějakýAnonymniTyp>, a dokonce je možné jednoduše použít anonymní datové typy z jiné assembly.

 

/// <summary>
    /// Rozšíření pro LINQ
    /// </summary>
    static class RSLinqExtensions
    {

        /// <summary>
        /// Metoda přetypuje objekt na anonymní typ, jehož struktura byla předána v parametru <paramref name="prototype"/>
        /// </summary>
        /// <typeparam name="T">Kompilátorem odvozený anonymní typ</typeparam>
        /// <param name="prototype">Prototyp se strukturou anonymního typu</param>
        /// <returns>Instanci anonymního typu, nebo null, jestliže konverzi nelze provést</returns>
        /// <remarks>Metoda se pokusí převést data z různých assembly</remarks>
        public static T ToAnonymousType<T>(this object obj, T prototype)
                                        where T: class
        {
            
            
            T atiObj = obj as T;
            
            if (atiObj == null)
            {

                ConstructorInfo constructorInfo = typeof(T).GetConstructors()[0];
                
                if (constructorInfo == null)
                {
                    return null;
                }

                ParameterInfo[] paramInfos = constructorInfo.GetParameters();                
                PropertyInfo[] origProperties = obj.GetType().GetProperties();
                

                if (paramInfos.Count() != origProperties.Count())
                {
                    return null;
                }

                object[] paramArgs = new object[paramInfos.Count()];


                for (int i = 0; i < paramArgs.Length; i++)
                {
                    PropertyInfo origProperty = origProperties.Where(prop => prop.Name == paramInfos[i].Name).FirstOrDefault();
                    
                    if (origProperty == null)
                    {
                        return null;
                    }
                                        
                    
                    paramArgs[i] = origProperty.GetValue(obj, null);                    
                }
                
                atiObj = constructorInfo.Invoke(paramArgs) as T;
            }
            
            return (atiObj);
        }
    
        /// <summary>
        /// Metoda vrátí
        /// </summary>
        /// <typeparam name="T">Kompilátorem odvozený anonymní typ</typeparam>
        /// <param name="prototype">Prototyp se strukturou anonymního typu</param>
        /// <returns>List instancí anonymního typu, nebo null, jestliže konverzi nelze provést</returns>
        /// <remarks>Metoda se pokusí převést data z různých assembly</remarks>
        public static List<T> CastToList<T>(this object obj, T prototype)
                                 where T : class
        {
            List<T> list = new List<T>();
            IEnumerable<T> enumerable = obj as IEnumerable<T>;

            if (enumerable != null)
            {
                list.AddRange(enumerable);
            }
            else
            {
                    IEnumerable enumObjects = obj as IEnumerable;
                    if (enumObjects == null)
                    {
                        return null;
                    }
                    
                foreach (object enumObject in enumObjects)
                    {
                        T currObject = ToAnonymousType(enumObject, prototype);
                        if (currObject == null)
                        {
                            //K čistění listu by neměl být důvod, ale garantujeme, že nevrátíme částečně naplněný list
                            list.Clear();
                            return list;
                        }

                        list.Add(currObject);
                    }
                
            }

            return list;
        }
    }

Komentáře u metod by měly dostatečně popisovat funkci extenzí. Metoda ToAnonymousType předpokládá, že chcete přetypovat na instanci anonymního typu (např. při použití metody Single v LINQu), metoda CastToList pracuje s množinou (IEnumerable<T>) instancí anonymního datového typu. Většina kódu v obou metodách ošetřuje situaci, kdy pracujete s anonymním datovým typem z jiné (referencované) assembly, jehož data je potřeba přenést do instance anonymního datového typu v aktuální assembly.

Použití extenzí - nejprve u anonymního datového typu deklarovaného v assembly, ve které je také náš LINQ dotaz.

using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Reflection;
using System.Collections;
using LINQTEST;

class Program
    {

        //Anonymní typ deklarovaný v této (exe) assembly
        private static object GetLetters()
        {
           string[] names = {"Rene", "Petra", "Kamilka"};

           var test = from name in names
                      select new {FirstLetter = name[0], Index=1};
           return test;
        }


        static void Main(string[] args)
        {
            var result = GetLetters().CastToList(new {FirstLetter = default(char),
                                                      Index =default(int)}
                                                 );
            foreach (var res in result)
            {
                Console.WriteLine(res.FirstLetter);
            }

}

Metodě CastToList jsme predali "vzor" anonymího datového typu (new {FirstLetter = default(char), Index =default(int)}) a hodnoty vlastností jsme u prototypu inicializovali s využitím klíčového slova default. V metodě Main v cyklu foreach je funkční intellisense a můžeme pracovat zcela typově s proměnnou res. Jenom zdůrazním, že nyní žádná reflexe nebyla použita! Metoda CastToList s využitím automatické typové inference kompilátoru pouze zkopírovala prvky v IEnumerable<T> do našeho typového generického Listu.

if (enumerable != null)
            {
                list.AddRange(enumerable);
            }

Reflexe je využita při konverzi anonymního typu deklarovaného v jiné assembly. Předpokládejme, že v jiné assembly nazvané např. LINQTest máme další metodu vracející množinu dat skrytou opět za obecným rozhraním "služebníka zcela neužitečného" neboli třídy object.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LINQTEST
{
    public class TestAT
    {
        public static object GetResult()
        {
            string[] rows = { "Toyota", "Lexus", "Audi" };

            var test = from row in rows
                       select new { FirstLetter = row[0],
                                    Index=110,
                                    Original = row
                                  };
            
            return test;

        }
    }
}

Zkompilovanou assembly LINQTest zareferencujeme v našem projektu. Kód pro práci s anonymní datovým typem v jiné assembly se z pohledu uživatele LINQ extenze nijak nezměnil od předchozího příkladu.

 

class Program
    {


        static void Main(string[] args)
        {
            //Anonymní typ z jiné assembly!
            var result2 = TestAT.GetResult().CastToList(new {FirstLetter =  default(char), 
                                                        Index =default(int),
                                                        Original = default(string)}
                                                       );
            foreach (var res in result2)
            {
                Console.WriteLine(res.FirstLetter);
                Console.WriteLine(res.Original);
            }


            Console.WriteLine(TestAT.
                                    GetResult().
                                    CastToList(new
                                    {
                                        FirstLetter = default(char),
                                        Index = default(int),
                                        Original = default(string)
                                    }).
                                    Where(car => car.FirstLetter == 'T')
                                     .FirstOrDefault()
                                     .ToString());
            Console.ReadLine();
        }
    }

Jak si můžete všimnout, po cyklu foreach si požádám o data z jiné assembly znovu a poté nad vrácenou typovou kolekci vytvořím další projekci. A ani mě nemusí zajímat, že se mi pod rukama zcela změnil typ používaných objektů. :-)

Docela zábavná záležitost ne? ;-)

LINQ II - přetypovávání i vnořených anonymních datových typů z jiné assembly



Thursday, 08 May 2008 15:00:43 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  .NET Framework | ASP.NET | Compact .Net Framework | LINQ | Windows Forms


 Wednesday, 07 May 2008
Stále neopravený bug při volání metody HttpWebRequest.BeginGetResponse?

Ještě při psaní jedné aplikace běžící na .Net Framework verze 2.0 jsem objevil podivnou chybu, kdy se při asynchronním stahování stránek pomocí třídy HttpWebRquest asynchronní přístup skoro nelišil od synchronní verze. Metoda BeginGetResponse vrátila řízení volajícímu kódu teprve poté, co došlo ke stažení celé stránky. Dle Microsoftu se problém objevoval i ve verzi 1.1 a příčinou jsou  "antipatie" mezi metodou BeginGetResponse a konfigurací DNS.

 

Myslel jsem si, že tento bug je ve verzi 3.5 odstraněn, ale dnes jsem zjistil, že bugy stejně jako kočky mají minimálně devět životů. U mě stačí -někdy-zadat adresu začínající na "www" a z asynchronního volání je volání synchronní. Zvláštní a k vzteku je, že že nelze stoprocentně napsat návod na reprodukci bugu (zkoušel jsem vyčistit i DNS cache). Bug se ale projevuje při připojení přes ADSL, O2 HSDPA i TMO GPRS/EDGE.

 

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;

namespace WebTest
{
    class Program
    {
        static void Main(string[] args)
        {
            bool done = false;
            WebRequest req = WebRequest.Create("http://www.vyvojar.cz");
            req.BeginGetResponse(delegate(IAsyncResult result)
                                      {

                                          done = true; 
                                          Console.WriteLine(String.Format("Done: {0}", DateTime.Now.ToLongTimeString()));
                                          req.EndGetResponse(result).Close();                                          
                                          Console.WriteLine();
                                          
                                      }

                                  ,null);

            while(!done)
            {
                Console.WriteLine("Not done");
            }

            Console.WriteLine("Stop");
            Console.ReadLine();
        }

        
    }
}

Jedinou obranou proti náhodným výkyvům v matrixu registrovaných služeb z předzjednaného řádu a Microsoftem sponzorované harmonie (Disclaimer: Tato obrana zabírá jen na popsaný bug, ale proti výkyvům univerza pocházejících od zeleného viru, a to i v jeho totálně dezorientované a nejméně hostilní mutaci "Džamila Stehlíková", je zcela bemocná ;-)) je po mnoha mých pokusech nahrazení názvu domény v url adrese její IP adresou (jestliže máte pod kontrolou nastavení webového serveru, na kterém běží stránky-webové služby, protože jinak nemusí samozřejmě ip adresa v hlavičce HOST dostačovat...). Ip adresu zjistíme jednoduše voláním metod z třídy DNS. Volání metod třídy DNS kupodivu žádné zpoždění nevykazuje...

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Threading;

namespace WebTest
{
    class Program
    {
        static void Main(string[] args)
        {
            bool done = false;
            
            IPHostEntry address = Dns.GetHostEntry("www.vyvojar.cz");
            HttpWebRequest req = WebRequest.Create(String.Format("http://{0}", address.AddressList[0].ToString())) as HttpWebRequest;             
            req.BeginGetResponse(delegate(IAsyncResult result)
                                      {
                                          
                                          done = true; 
                                          Console.WriteLine(String.Format("Done: {0}", DateTime.Now.ToLongTimeString()));
                                          req.EndGetResponse(result).Close();                                          
                                          Console.WriteLine();
                                          
                                      }

                                  ,null);

            while(!done)
            {
                Console.WriteLine("Not done");
            }

            Console.WriteLine("Stop");
            Console.ReadLine();
        }

        
    }
}


Wednesday, 07 May 2008 12:48:42 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  .NET Framework | Web Services


 Monday, 05 May 2008
Pozvánka na přednášku Pasti, strasti a propasti nativního/řízeného (managed) vývoje pro zařízení s operačním systémem Windows Mobile

Pozvánka na přednášku  Pasti, strasti a propasti nativního/řízeného (managed) vývoje pro zařízení s operačním systémem Windows Mobile

Ve čtvrtek 15.5.2008 přednáším na WUG v Hradci Králové o vývoji aplikací pro Windows Mobile. Jestliže vás téma zajímá a máte cestu okolo, rád vás uvidím. ;)

Místo konání: Střední škola aplikované kybernetiky, Hradecká 1151, Hradec Králové (www.ssakhk.cz)
Datum a čas akce: 15.5.2008 17:00:00
Stránka WUG s podrobnostmi o akci: http://www.wug.cz/Aktuality/tabid/36/ctl/Detail/mid/492/ItemId/124/language/cs-CZ/Default.aspx

 



Monday, 05 May 2008 17:00:08 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  Compact .Net Framework | Mobilitky | Net Monitor | Wifi Profiles Windows Mobile


GSM Net Monitor a WiFi profily mají vlastní stránky

Freewarové aplikace GSM Net Monitor a WiFi profily mají (už přibližně měsíc) své vlastní stránky, aby informace o nich nezůstaly utopeny pouze ve spotech na blogu a já jsem se zbavil dotazů na ICQ (nebo v to alespoň naivně doufám, než celé ICQ pošlu na hnojiště dějin=> na hnojišti končí produkty, které si nezaslouží místo na ordinérním smetišti dějin a mezi ně paskvil zvaný ICQ v posledních  letech beze všech pochybností patří) , kde se dá stáhnout poslední verze nebo kde je možné o aplikacích diskutovat.

WiFi profily

GSM Net Monitor

BTW: Ani úpěnlivé prosby o přidání nějaké takové nebo onaké funkcionality do programů nezrychlí vývoj aplikací, protože aplikace jsou hračky vyvinuté jen jako vedlejší efekt komerční aplikace (GSM Net Monitor), anebo mi přišlo jednodušší napsat WiFi profily, než stále dokola ručně Petře (manželce) nastavovat PDA.

Pro mé účely jsou aplikace plně funkční a práce na nich pokračuje jen ve volném čase. Žádné prosby ani hrozby vývoj neurychlí. Vývoj bude rychlejší, když použijete magické Paypal tlačítko Donate na této stránce, případně na moji adresu přímo zašlete láhev ostřejšího pití (Johnie Walker, Four Roses, Tulamore Dew), nebo nějaké výborné víno (Pálava - výběr z hroznů, Tokajske víno, Archivní Modrý Portugal, Chardonnay, Rulandské modré, Rulandské šedé ...). :-D



Monday, 05 May 2008 16:19:48 (Central Europe Standard Time, UTC+01:00)       
Comments [2]  Mobilitky | Net Monitor | Wifi Profiles Windows Mobile