<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Sky.Log(); &#187; C#</title>
	<atom:link href="http://blog.skyknight.info/category/programowanie/c/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.skyknight.info</link>
	<description></description>
	<lastBuildDate>Mon, 01 Mar 2010 20:47:06 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Niesforny statyczny konstruktor w C#</title>
		<link>http://blog.skyknight.info/2010/01/niesforny-statyczny-konstruktor-w-c/</link>
		<comments>http://blog.skyknight.info/2010/01/niesforny-statyczny-konstruktor-w-c/#comments</comments>
		<pubDate>Sun, 24 Jan 2010 16:48:55 +0000</pubDate>
		<dc:creator>SkyKnight</dc:creator>
				<category><![CDATA[C#]]></category>

		<guid isPermaLink="false">http://blog.skyknight.info/?p=167</guid>
		<description><![CDATA[Wpis bardziej &#8216;ku pamięci&#8217;, ale może komuś zaoszczędzić kłopotów ;-)
Załóżmy, że mamy taki oto kod:

    class First
    {
        static First()
        {
            Console.WriteLine(&#34;First static constructor&#34;);
 [...]]]></description>
			<content:encoded><![CDATA[<p>Wpis bardziej &#8216;ku pamięci&#8217;, ale może komuś zaoszczędzić kłopotów ;-)</p>
<p>Załóżmy, że mamy taki oto kod:</p>
<pre class="brush: csharp;">
    class First
    {
        static First()
        {
            Console.WriteLine(&quot;First static constructor&quot;);
        }

        public static void DoSth()
        {
            Console.WriteLine(&quot;Let's do sth&quot;);
        }
    }

    class Second : First
    {
        static Second()
        {
            Console.WriteLine(&quot;Second static constructor&quot;);
        }
    }
</pre>
<p>Jeśli wywołamy metodę <em>First.DoSth()</em> w outpucie otrzymamy:</p>
<p><code>First static constructor<br />
Let's do sth</code></p>
<p>Jeśli jednak spróbujemy z <em>Second.DoSth()</em> otrzymamy&#8230; to samo?! A gdzie wykonanie statycznego konstruktora klasy <em>Second</em>? Wszak w <a href="http://msdn.microsoft.com/en-us/library/k9x6w0hc%28VS.80%29.aspx">dokumentacji MSDN</a> możemy przeczytać:</p>
<blockquote><p>A static constructor is called automatically to initialize the class  before the first instance is created or any static members are referenced.</p></blockquote>
<p>I&#8230; to się zgadza! Podejrzyjmy<a href="http://www.red-gate.com/products/reflector/"> Reflectorem</a> jak kompilator zbudował aplikację:</p>
<p><a href="http://blog.skyknight.info/wp-content/uploads/2010/01/ref-sc.png"><img src="http://blog.skyknight.info/wp-content/uploads/2010/01/ref-sc.png" alt="" title="ref-sc" width="223" height="125" class="aligncenter size-full wp-image-168" /></a></p>
<p>W związku z tym, iż metoda <em>DoSth()</em> jest w klasie <em>First</em>, kompilator &#8216;olewa&#8217; nasze wywołanie przez klasę <em>Second</em>, przez co statyczny konstruktor tej drugiej się nie wykona. Jest to o tyle niebezpieczne (co sam w swoim kodzie &#8216;przetestowałem&#8217;), że jeśli metoda <em>DoSth()</em> jest w jakikolwiek sposób zależna od wyniku statycznego konstruktora (a wcześniej nic i nikt nie odwoływał się do <em>Second</em>), to możemy spędzić trochę czasu na szukaniu błędu&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.skyknight.info/2010/01/niesforny-statyczny-konstruktor-w-c/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Klient REST w WCF</title>
		<link>http://blog.skyknight.info/2009/01/klient-rest-w-wcf/</link>
		<comments>http://blog.skyknight.info/2009/01/klient-rest-w-wcf/#comments</comments>
		<pubDate>Sat, 24 Jan 2009 20:49:30 +0000</pubDate>
		<dc:creator>SkyKnight</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[csharp wcf tutorial]]></category>

		<guid isPermaLink="false">http://blog.skyknight.info/?p=111</guid>
		<description><![CDATA[Jak już się napisało, że kiedyś opiszę się tworzenie klienta RESTa w .Necie, to słowa trzeba dotrzymać. Postaram się jak najzwięźlej opisać w jak dość prosty sposób można otrzymać taką funkcjonalność. Przykładem będzie właśnie API dla Blipa, bowiem to przy nim &#8220;wypłynęło&#8221; kilka ciekawych zagadnień.

Na wstępie dla niewtajemniczonych:

REST
WCF
JSON

Całość opiera się na klasie WebChannelFactory, której to [...]]]></description>
			<content:encoded><![CDATA[<p>Jak już się napisało, że kiedyś opiszę się tworzenie klienta RESTa w .Necie, to słowa trzeba dotrzymać. Postaram się jak najzwięźlej opisać w jak dość prosty sposób można otrzymać taką funkcjonalność. Przykładem będzie właśnie <a href="http://www.blip.pl/api-0.02.html">API dla Blipa</a>, bowiem to przy nim &#8220;wypłynęło&#8221; kilka ciekawych zagadnień.<br />
<span id="more-111"></span><br />
Na wstępie dla niewtajemniczonych:</p>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Representational_State_Transfer">REST</a></li>
<li><a href="http://en.wikipedia.org/wiki/Windows_Communication_Foundation">WCF</a></li>
<li><a href="http://en.wikipedia.org/wiki/Json">JSON</a></li>
</ul>
<p>Całość opiera się na klasie <a href="http://msdn.microsoft.com/en-us/library/bb908674.aspx">WebChannelFactory<T></a>, której to dostarczamy interface mapujący restowe operacje na metody. Jest to bardziej skonkretyzowana klasa względem <a href="http://msdn.microsoft.com/en-us/library/ms576132.aspx">ChannelFactory<T></a>, nastawiona na czysto webową komunikację. WebChannelFactory<T> potrafi także mapować odpowiedź serwera na odpowiednie klasy &#8211; obsługiwany jest zarówno XML jak i JSON. Myślę, że właśnie od owego mapowania warto zacząć pracę.</p>
<p>Blip na zapytanie dot. statusu odpowiada takim kodem:</p>
<pre class="brush: jscript;">
{
  'id': 1,
  'created_at': &quot;2007-10-18 11:27:20&quot;,
  'transport': {'id': 6, 'name': 'www'},
  'body': 'foobar http://rdir.pl/jk2hg',
  'type': 'Status',
  'user_path': '/users/frania',
  'pictures_path': '/updates/1/pictures',
  'recording_path': '/updates/1/recording',
  'movie_path': '/updates/12/movie'
}
</pre>
<p>Klasa w C# może wyglądać tak:</p>
<pre class="brush: csharp;">
    [DataContract]
    public class Update
    {
        [DataMember(Name=&quot;id&quot;)]
        public int Id { get; set; }

        [DataMember(Name=&quot;created_at&quot;)]
        public string CreatedAt { get; set; } 

        [DataMember(Name=&quot;transport&quot;)]
        public Transport Transport { get; set; }

        [DataMember(Name=&quot;body&quot;)]
        public string Body { get; set; }

        private UpdateTypes type;
        [DataMember(Name = &quot;type&quot;)]
        public string Type { get; set; }

        [DataMember(Name=&quot;user_path&quot;)]
        public string UserPath { get; set; }

        [DataMember(Name=&quot;recipient_path&quot;)]
        public string RecipientPath { get; set; }

        [DataMember(Name=&quot;pictures_path&quot;)]
        public string PicturesPath { get; set; }

        [DataMember(Name=&quot;recording_path&quot;)]
        public string RecordingPath { get; set; }

        [DataMember(Name=&quot;movie_path&quot;)]
        public string MoviePath { get; set; }
    }
</pre>
<p>Jak widać do wskazania klasy na którą odpowiedź ma być deserializowana używa się atrybuty <a href="http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractattribute.aspx">DataContract</a>, natomiast dla wartości &#8211; <a href="http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datamemberattribute.aspx">DataMember</a> z opcjonalną właściwością Name. Według opisu API wartość type może mieć przyjmować tylko 4 wartości (Status, DirectMessage, PrivateMessage, Notice). Naturalne wydaje się być użycie enumeratora. <strong>Niestety</strong>, deserializer JSONa nie potrafi parsować ciągów znaków na enum &#8211; <a href="http://msdn.microsoft.com/en-us/library/bb412170.aspx">dozwolone są tylko wartości liczbowe</a>. Podobnie jest z datą &#8211; <strong>nie można</strong> w tym przykładzie dać typu DateTime właściwości CreatedAt, ponieważ odpowiedź ze strony serwera ma postać np. 2007-10-18 11:27:20, natomiast .Net wymaga Date([ilość ticków]). Problem można rozwiązać poprzez dopisanie właściwości/metod samodzielnie parsujących te dane.</p>
<p>Następnym krokiem jest utworzenie interface&#8217;u, który stanowić będzie &#8220;kontrakt&#8221; między klientem a serwerem. Tak jak dla klas danych, również i tutaj trzeba nadać odpowiedni atrybut &#8211; <a href="http://msdn.microsoft.com/en-us/library/system.servicemodel.servicecontractattribute.aspx">ServiceContract</a>.</p>
<pre class="brush: csharp;">
    [ServiceContract]
    public interface IBlipApi
    {

    }
</pre>
<p>Do każdej z metod również musimy dodać atrybuty. Zawsze musi się pojawić <a href="http://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontractattribute.aspx">OperationContract</a> oraz drugi atrybut określający sposób komunikacji</p>
<pre class="brush: csharp;">
    public interface IBlipApi
    {
        [OperationContract]
        [WebGet(UriTemplate = &quot;/updates/{id}&quot;, ResponseFormat = WebMessageFormat.Json)]
        Update GetUpdate(string id);

        [OperationContract]
        [WebInvoke(UriTemplate = &quot;/updates?update[body]={body}&quot;, Method = &quot;POST&quot;]
        void CreateUpdate(string body);

        [OperationContract]
        [WebInvoke(UriTemplate = &quot;/updates/{id}&quot;, Method=&quot;DELETE&quot;)]
        void RemoveUpdate(string id);
    }
</pre>
<p><a href="http://msdn.microsoft.com/en-us/library/system.servicemodel.web.webgetattribute.aspx">WebGet</a> jest de facto atrybutem <a href="http://msdn.microsoft.com/en-us/library/system.servicemodel.web.webinvokeattribute.aspx">WebInvoke</a> z ustawioną metodą GET. W parametrze UriTemplate podajemy ścieżkę (bez adresu serwera &#8211; o tym później) zapytania. W nawiasach klamrowych pojawią się wartości z parametrów przekazanych do metody. Niestety, owe parametry mogą być <strong>tylko</strong> typu string (nie wiem czemu przy innych typach nie może być wywoływana metoda ToString()). W metodach zwracających dane trzeba jeszcze określić jakiego są one typu &#8211; domyślnie XML.</p>
<p>Tak gotowy interface trzeba przekazać fabryce.</p>
<pre class="brush: csharp;">
            WebChannelFactory&lt;IBlipApi&gt; channelFactory = new WebChannelFactory&lt;IBlipApi&gt;(new WebHttpBinding(), new Uri(&quot;http://api.blip.pl&quot;));
            IBlipApi api = channelFactory.CreateChannel();
</pre>
<p>W teorii byłoby to na tyle. <strong>Ale jest &#8216;ale&#8217;</strong> ;-) API Blipa (tak jak pewnie wiele innych) potrzebuje w nagłówku żądania dwóch parametrów: <em>Accept: application/json</em> oraz <em>X-Blip-API: 0.02</em>. Nie znalazłem innego rozwiązania jak skorzystać z <a href="http://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontextscope.aspx">kontekstu</a> i ustawienia w nim odpowiednich nagłowków</p>
<pre class="brush: csharp;">
            WebChannelFactory&lt;IBlipApi&gt; channelFactory = new WebChannelFactory&lt;IBlipApi&gt;(new WebHttpBinding(), new Uri(&quot;http://api.blip.pl&quot;));
            IBlipApi api = channelFactory.CreateChannel();
            using(OperationContextScope context = new OperationContextScope((IContextChannel)api))
            {
             WebOperationContext.Current.OutgoingRequest.Accept = &quot;application/json&quot;;
             WebOperationContext.Current.OutgoingRequest.Headers.Add(&quot;X-Blip-API&quot;, &quot;0.02&quot;);
             WebOperationContext.Current.OutgoingRequest.Headers.Add(&quot;Authorization&quot;, string.Format(&quot;Basic {0}&quot;,
                EncodeTo64(string.Format(&quot;{0}:{1}&quot;, user, password))));
                                    Console.WriteLine(api.GetUpdate(&quot;555&quot;).Body);
            }
</pre>
<p>W powyższym przykładzie dodałem jeszcze w nagłówku dane do autoryzacji w serwisie. Drugim &#8220;ale&#8221; jest odpowiedź z serwera Blip &#8211; .Net spodziewa się otrzymać Content-Type: application/json, tymczasem dostaje text/js przez co <strong>wyrzuca wyjątkiem</strong>. Najlepiej byłoby móc ustawić, iż zawsze z tego serwera otrzymujemy dane w postaci JSONa. W tym celu musimy utworzyć własny obiekt <a href="http://msdn.microsoft.com/en-us/library/system.servicemodel.channels.binding.aspx">Binding()</a> i przekazać mu inny obiekt z metodą informującą o tym, że zwrócone przez serwer dane to obsługiwany format. Brzmi zawile ale kod powinien to wyjaśnić. Najpierw tworzymy klasę dziedziczącą po WebContentTypeMapper.</p>
<pre class="brush: csharp;">
    internal class BlipMapper : WebContentTypeMapper
    {
        public override WebContentFormat GetMessageFormatForContentType(string contentType)
        {
            return WebContentFormat.Json;
        }
    }
</pre>
<p>Czego byśmy nie dostali w odpowiedzi traktujemy to zawsze jako JSON. Oczywiście, bardziej ambitni mogą zacząć tutaj odpowiednio sprawdzać przychodzącą wartość. Następnie definiujemy taką metodę:</p>
<pre class="brush: csharp;">
        private Binding GetBinding()
        {
            CustomBinding binding = new CustomBinding(new WebHttpBinding());
            WebMessageEncodingBindingElement element = binding.Elements.Find&lt;WebMessageEncodingBindingElement&gt;();
            element.ContentTypeMapper = new BlipMapper();
            return binding;
        }
</pre>
<p>Tworzy ona nasz własny obiekt Binding() i dodaje do niego wcześniej zdefiniowanego mappera. Trzeba jeszcze tylko zmodyfikować jedną linię:</p>
<pre class="brush: csharp;">
            WebChannelFactory&lt;IBlipApi&gt; channelFactory = new WebChannelFactory&lt;IBlipApi&gt;(GetBinding(), new Uri(&quot;http://api.blip.pl&quot;));
</pre>
<p><em>Voila!</em></p>
<p>Jak widać utworzenie klienta REST w .Necie 3.5 nie jest trudne &#8211; większość operacji przejmuje na siebie WCF. Jednak <strong>nie ma róży bez kolców</strong> &#8211; nie wszystkie typy danych łatwo się mapują, przekazywane w parametrach wartości muszą być stringami i nie ma żadnego mechanizmu ich sprawdzania. Także zabawa z kontekstami przy ich dużej ilości może być kłopotliwa.</p>
<p>Ten wpis nie powstał &#8220;znikąd&#8221;. Pomogły następujące strony:</p>
<ul>
<li><a href="http://blogs.msdn.com/carlosfigueira/">Carlo&#8217;s blog</a> &#8211; kilka przykładów użycia WCF</li>
<li><a href="http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/bf7eda10-3930-4999-8f9e-b3be5ae8f2f3/">dyskusja nt. dostępu do nagłówków HTTP</a></li>
<li><a href="http://jeffbarnes.net/portal/blogs/jeff_barnes/archive/2007/11/28/wcf-3-5-webinvoke-attribute.aspx">użycie atrybutu WebInvoke</a></li>
<li><a href="http://blogs.msdn.com/bags/archive/2008/06/10/rest-in-wcf-part-v-hi-rest-exposing-a-service-via-get-the-servicecontract-and-implementation.aspx">kurs REST i WCF</a> (linki do poprzednich części w treści wpisu)</li>
<li><a href="http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/40a32404-e921-42d9-86c0-f399726de0b4/">dyskusja WCF i JSON</a></li>
</ul>
<p>Dzięki!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.skyknight.info/2009/01/klient-rest-w-wcf/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>WCF + Blip = WcfBlip</title>
		<link>http://blog.skyknight.info/2009/01/wcf-blip-wcfblip/</link>
		<comments>http://blog.skyknight.info/2009/01/wcf-blip-wcfblip/#comments</comments>
		<pubDate>Tue, 20 Jan 2009 19:28:27 +0000</pubDate>
		<dc:creator>SkyKnight</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[wcf csharp blip library]]></category>

		<guid isPermaLink="false">http://blog.skyknight.info/?p=97</guid>
		<description><![CDATA[Wyszło jak zawsze &#8211; przypadkiem. Wśród napisanych blipowych bibliotek znalazła się jedna dla .neta. Niestety, nie jest już rozwijana w związku z czym spróbowałem napisać swoją. Po kilku godzinach surfowania w Sieci udało mi się zdobyć na tyle dużo wiedzy nt. WCF by to właśnie w tej technologi wykonać bibliotekę. I myślę, że udało się, [...]]]></description>
			<content:encoded><![CDATA[<p>Wyszło jak zawsze &#8211; przypadkiem. Wśród napisanych <a href="http://blip.wikidot.com/aplikacje">blipowych bibliotek</a> znalazła się jedna dla <a href="http://sourceforge.net/projects/sharpbliplib">.neta</a>. Niestety, nie jest już rozwijana w związku z czym spróbowałem napisać swoją. Po kilku godzinach surfowania w Sieci udało mi się zdobyć na tyle dużo wiedzy nt. <acronym title="Windows Communication Foundation">WCF</acronym> by to właśnie w tej technologi wykonać bibliotekę. I myślę, że udało się, choć nie cała funkcjonalność API Blipa się w niej znajduje. </p>
<p><strong>Pliki</strong><br />
<a href="http://blog.skyknight.info/wp-content/uploads/2009/01/wcfblip.zip">kod źródłowy (Visual Studio 2008)</a><br />
<strong>UPDATE</strong> [01.01.2010]<br />
<a href="http://github.com/SkyKnight/WcfBlip">projekt na GitHub</a></p>
<p><strong>Wymagania</strong><br />
<a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=333325FD-AE52-4E35-B531-508D977D32A6&#038;displaylang=en">.Net Framework 3.5</a></p>
<p><strong>Użycie</strong></p>
<p>Generalnie wszystkie wywołania metod znajdują się we właściwości <em>Api</em> klasy <em>Blip</em>. Jest tak dlatego, że podczas pobierania owej właściwości tworzony jest kontekst z wymaganymi przez API nagłówkami HTTP. Jeśli aplikacja, która piszesz łączy się z innymi źródłami danych przez HTTP, warto dla bezpieczeństwa wywoływać metodę Blip.Dispose() lub używać klauzuli using(), by usunąć kontekst.</p>
<p>Parametry przekazywane do wywołań niestety mogą być tylko typu string (<del datetime="2009-02-18T18:43:14+00:00">wymóg WCF</del>  <strong>UPDATE</strong>: cóż, w innym kontekście może i można, ale ja dostaje wyjątek) i nie są w żaden sposób sprawdzane.</p>
<p>            using (WcfBlip.Blip blip = new WcfBlip.Blip(&#8220;uzytkownik&#8221;, &#8220;haslo&#8221;)) // logujemy się<br />
            {<br />
                Updates[] updates = blip.Api.GetUpdates(); // pobiera ostatnie statusy użytkownika<br />
                Updates[] updates2 = blip.Api.GetUpdates(&#8220;10&#8243;, &#8220;5&#8243;); // pobiera 10 ostatnich statusów zaczynając od 5<br />
                User user = blip.Api.GetUser(&#8220;jack&#8221;); // pobiera dane nt. użytkownika &#8220;jack&#8221;<br />
            }</p>
<p>Trochę więcej przykładów znajduje się <em>WcfBlipTest</em></p>
<p>Choć według <a href="http://www.blip.pl/api-0.02.html">opisu API</a> jest podział na update&#8217;y, statusy, prywatne wiadomości i uwagi, to zdecydowałem się na jedną klasę Update, która to posiada właściwość Type, po której to można rozpoznawać typ wiadomości.</p>
<p><strong>Do poprawki</strong></p>
<ul>
<li>uzupełnić brakujące metody (upload obrazków, avatarów, backgroundów)</li>
<li>dodać komentarze do kodu</li>
<li>dodać do klas encji właściwości zwracające nazwy użytkowników (a nie jak teraz &#8211; tylko ich URL)</li>
<li>wprowadzić obsługę błędów (na razie nie ma żadnej)</li>
</ul>
<p><strong>Licencja</strong><br />
<a href="http://www.opensource.org/licenses/mit-license.php">MIT</a></p>
<p>Wszelkie komentarze i uwagi mile widziane :-)</p>
<p>P.S. W najbliższym czasie postaram się napisać o tworzeniu klientów REST w .Necie za pomocą WCF.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.skyknight.info/2009/01/wcf-blip-wcfblip/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Mp3Arranger</title>
		<link>http://blog.skyknight.info/2008/12/mp3arranger/</link>
		<comments>http://blog.skyknight.info/2008/12/mp3arranger/#comments</comments>
		<pubDate>Sat, 27 Dec 2008 20:54:22 +0000</pubDate>
		<dc:creator>SkyKnight</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[c# music mp3]]></category>

		<guid isPermaLink="false">http://blog.skyknight.info/?p=75</guid>
		<description><![CDATA[Przetrząsając swój dysk w poszukiwaniu zaginionych plików muzycznych, zachciało mi się programu, który to zrobiłby porządek w katalogach układając je wg nazwy artysty i albumu. Nie doszukałem się takiej funkcjonalności w Winampie, a w Googlach nawet nie szukałem. W ramach poświątecznego ruszania mózgiem postanowiłem napisać sobie coś takiego samemu. Wybór padł oczywiście na C#.
Założenie było [...]]]></description>
			<content:encoded><![CDATA[<p>Przetrząsając swój dysk w poszukiwaniu zaginionych plików muzycznych, zachciało mi się programu, który to zrobiłby porządek w katalogach układając je wg nazwy artysty i albumu. Nie doszukałem się takiej funkcjonalności w Winampie, a w Googlach nawet nie szukałem. W ramach poświątecznego ruszania mózgiem postanowiłem napisać sobie coś takiego samemu. Wybór padł oczywiście na C#.</p>
<p>Założenie było proste: na podstawie tagów w plikach muzycznych program ma pozakładać katalogi z nazwą artysty i podkatalogi z nazwami albumów oraz przenieść tam pliki.</p>
<p>Na oficjalnej stronie <a href="http://id3.org">ID3</a> znalazłem odpowiednie <a href="http://id3.org/Implementations">implementacje dla C#</a>. Wybrałem <a href="http://developer.novell.com/wiki/index.php/TagLib_Sharp">TagLib#</a> ponieważ obsługiwał nie tylko Mp3, ale również Ogg i WMA (oraz jeszcze kilka innych formatów, których nawet nie używam). <strong>I tu chciałem zamieścić krótką informację.</strong>  Skompilowana dll&#8217;ka, która jest tam dostępna ma problemy z innymi formatami niż mp3 &#8211; należy pobrać najświeższy kod z <a href="svn://anonsvn.mono-project.com/source/trunk/taglib-sharp">ichniego svna</a>, wtedy wszystko działa.</p>
<p>Pliki:</p>
<ul>
<li><a href="http://blog.skyknight.info/wp-content/uploads/2008/12/mp3arranger.zip">Skompilowany program</a></li>
<li><a href="http://blog.skyknight.info/wp-content/uploads/2008/12/mp3arranger1.zip">Kod źródłowy</a></li>
</ul>
<p><strong>Wymagany jest <a href="http://www.microsoft.com/downloads/details.aspx?FamilyId=333325FD-AE52-4E35-B531-508D977D32A6&#038;displaylang=en">.Net 3.5</a></strong> (nie chciało mi się zmieniać projektu na dwójkę&#8230; ;-) )</p>
<p>Features:</p>
<ul>
<li>obsługa Mp3, Ogg i WMA</li>
<li>możliwość wprowadzenia własnych wzorców nazw katalogów</li>
<li>ubogi design ;-)</li>
</ul>
<p>Known bugs:</p>
<ul>
<li>niestety, TagLib# ma problemy z polskimi literami w tagach ID3v1 &#8211; rozwiązaniem jest używanie tylko IDv2 lub przymknięcie oka na &#8220;krzaczki&#8221; w nazwach katalogów ;-)</li>
</ul>
<p>Licencja: <a href="http://sam.zoy.org/wtfpl/">WTFPL</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.skyknight.info/2008/12/mp3arranger/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
