пятница, 30 ноября 2007 г.

Свершилось! Parallel Extensions to the .NET FX CTP

Мы ждали, ждали и наконец - дождались :) Доступен для скачивания ParallelFX CTP Кратко о том, что это такое, можно почитать здесь.

P.S.: радость была недолгой... Для установки требуется релиз .Net Framework 3.5 или выше. Естественно - релиз не ставится рядом с беткой. Естественно - без бетки не работает бетка студии 2008 (ну и бетки экспресс-версий - тоже).

Так что кому интересно попробавать эту штуку - придется качать релиз студии (3,9 Гб) или же ее экспресс версии (800 Мб)

Еще немного о сериализации

Читая новые rss-ки увидел вот этот пост.

А привлек мое внимание вот этот абзац:

Среда сериализации присваивает каждому классу, поддерживающему сериализацию номер версии, под именем serialVersionUID. Этот номер используется при восстановлении состояния объекта, чтобы убедиться, что классы отправителя и получателя сериализованного объекта совместимы между собой. Если класс получателя имеет отличный от отправителя serialVersionUID, тогда десереилазация завершится исключением InvalidClassException. Поддерживающий сериализацию класс может явно объявить собственный serialVersionUID, объявив поле с именем «serialVersionUID», которое должно быть длинным целым с модификаторами static и final.

Должен сказать довольно удобная приблуда. Без нее API пришлось бы использовать номер версии скомпилированной сборки, который может меняться при каждой компиляции, даже если определение класса не изменялось. Но при помощи serialVersionUID разработчики могут сами решать, одинаковы ли версии их классов или нет.


Интересно, почему разработчики .Net Framework'a не реализовали подобную функциональность (да похоже и не собираются)? Фактически, предложенный способ управления сериализацией находится в промежутке между xml-сериализацией и soap (когда вместе с данными еще и хранится информация о версии сборки, в которой находится описание сериализуемого класса).

среда, 28 ноября 2007 г.

Не все DateTime одинаково полезны

Маленькое предисловие

С базами данных я практически не работал. Ну так, знаю четыре базовых SQL-команды и умею пользоваться гуглом :) В принципе, этого достаточно, учитывая возможности студии. И вот столкнулся я вчера с очень оригинальной ошибкой...




Есть у меня одна программа, предназначенная для поиска дубликатов файлов. Для сравнения файлов используется алгоритм рассчета Tiger Tree Hash. Рассчеты ТТН ведутся только для первых 5 Мб с начала файла, что позволяет резко сократить время работы программы (Естественно, что при сравнении учитывается не только ТТН).
Основная идея программы в том, чтобы запоминать уже рассчитанные ТТН. И в дальнейшем сравнивать ТТН новых файлов с запомненными - причем старый файл уже может и не существовать на диске.
Поскольку программа писалась на коленке, для, так сказать, "внутреннего использования" - все данные сохранялись в XML (о том, как сериализовать сложные коллекции - типа Dictionary и ей подобных - я писал ранее). И всё было бы ничего, да вот только сейчас xml с данными весит уже 70 Мб и собирается расти дальше :) Ну, в принципе, что-то такое я и ожидал... Так что было принято волевое решение переписать программу так, чтобы она использовала базу данных как хранилище.

Сказано - сделано.

Как база данных был выбран Access 2007 - т.к. он у меня уже установлен, а со всеми остальными базами данных надо разбираться (вообщем, лень :) )
Открываю Access, рисую таблицу вида:

код
ТТНstring
размер файлаint
полный путь к файлуstring
дата созданияDateTime


Вообщем, ничего сложного. Используя мастера студии 2008 создаю DataSetTableAdapter, добавляю в него два запроса - Select и Insert.

Тестирую:

FileInfo1TableAdapter fi=new FileInfo1TableAdapter();
string TTH="aaa";
int Length=10;
string path="filename";
DateTime date=new DateTime(2007, 11, 27, 9, 20, 10);

fi.InsertQuery(TTH, Length, path, date);
Работает! Ура :)

Прикручиваю к новой программе хранилище данных из старой программы - с целью перенести эти самые данные в базу. Пишу алгоритм переноса и... получаю ошибку приведения типов при попытке поместить данные в базу!

Потратив полчаса времени, я все-таки выяснил, что причина ошибки кроется в параметре date.

А теперь внимание! Значение date - это дата и время создания файла. Время создания дается с точностью до миллисекунд. При тестировании я использовал значение времени с точностью до секунд (напоминаю: тестовый пример - работал). Так вот, оказывается, что если вызывать, например, вот такой конструктор DateTime(2007, 11, 27, 9 /*часы*/, 20 /*минуты*/, 10 /*секунды*/, 10 /*миллисекунды*/) - мы гарантированно получаем ошибку не соответствия типов при попытке поместить значение DateTime в базу!

P.S.: вывод - если нечто выглядит как DateTime, ведет себя как DateTime, то это не значит что данное нечто - DateTime :)

понедельник, 26 ноября 2007 г.

Vista Glass для формы

Итак, в продолжение предыдущего поста рассказываю как сделать форму полупрозрачной.

Сначала добавляем в проект вот этот класс:


using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;

namespace VistaApi
{
    /// Class for enabling glass transparent mode in area
    /// You must set Color = Black in control!
    public class GlassApi
    {
        [DllImport("dwmapi.dll")]
        static extern void DwmExtendFrameIntoClientArea(System.IntPtr hWnd, ref Margins pMargins);

        [DllImport("dwmapi.dll")]
        static extern void DwmIsCompositionEnabled(ref bool isEnabled);

        struct Margins
        {
            public int Left, Right, Top, Bottom;
        }

        ///  Glass Window Handle 
        IntPtr Handle;

        /// Constructor
        /// Handle of window with glass effect
        public GlassApi(IntPtr Handle)
        {
            this.Handle = Handle;
        }

        /// Glass rectangle
        Margins margins = new Margins();

        public void SetGlassArea(Rectangle r)
        {
            //-1 because strange bug: on left corner is 1-pixel black square
            margins.Left = r.Left-1;
            margins.Top = r.Top-1;
            margins.Right = r.Right;
            margins.Bottom = r.Bottom;
            DwmExtendFrameIntoClientArea(Handle, ref margins);
        }


        bool glassEnabledCalled = false;
        private bool GetIsGlassEnabled()
        {
            glassEnabledCalled = true;
            //Checking if OS is >= Vista
            if (Environment.OSVersion.Version.Major < 6)
                return false;

            //Check if DWM is enabled
            bool isGlassSupported = false;
            DwmIsCompositionEnabled(ref isGlassSupported);
            return isGlassSupported;
        }

        bool _IsGlassEnabled=false;

        /// returns True if glass can be used
        public bool IsGlassEnabled
        {
            get
            {
                if (!glassEnabledCalled)
                    _IsGlassEnabled = GetIsGlassEnabled();
                return _IsGlassEnabled;
            }
        }
    }
}


Использование его очень просто:
1. В конструкторе передаем хэндл нашего окна.
2. Для того, чтобы узнать - можно ли использовать вистовскую прозрачность - используем свойство класса IsGlassEnabled.
3. Чтобы сделать форму прозрачной необходимо залить ее SolidBrush кистью, причем используя только основные семь цветов (белый цвет к ним не относится!). В зависимости от использованного цвета заливки будет меняться цвет полупрозрачности.

Следствие №1: все контролы, залитые основным цветом будут также полупрозрачными. Это так же касается всех надписей (по умолчанию они черного цвета).
Следствие №2: если цвет формы не менять (оставить цвет Control), а у контролов на форме - менять, то контролы будут полупрозрачными, а форма - нет.

Приключения. Или как перенести Vista на другой раздел

Сидел я, значит, никого не трогал и тут - бац! Windows Server 2008 RC0 вежливо сообщает мне, что мой 160 Гб диск помирать собирается... А ведь на нем - система! А ведь на нем - все мои проекты!! Вообщем - жуть :(
Пришлось срочно решать проблему - как запихнуть 160 гигов в 20 (именно столько у меня было свободно на втором винчестере). К счастью - на работе обнаружился свободный (новенький!) 250 гиговый винчестер...

Ну да это всё присказка. А сказка - дальше.

Итак, имеем проблему: на диске С: (второй раздел) стоит Windows Server 2008 RC0 (почти Vista :) ). На первом разделе - загрузчик. Необходимо перетащить систему на третий диск (40 Гигов, чистый).

Этап №1. Перенос системы на другой диск.

Я - существо ленивое :) Кроме того - в момент совершения данных действий физически я аходился на работе и управлял системой удаленно - через удаленный рабочий стол. Поэтому естественным для меня выбором было попробовать использовать стандартные средства новой ОС.
Первое - необходимо установить Windows Server Backup. Делается это в оснастке Server Manager в разделе Features.
Второе - собственно сам процесс бекапа описывать я думаю не имеет смысла - там три кнопки, кому надо - разберетесь (кому лень - всегда можно сделать копию из под другой ОС).
Третье - восстанавливаем бекап на другой диск. Здесь вы наверняка столкнетесь со следующей проблемой: нельзя восстановить бекап раздела, если раздел, на который мы восстанавливаем бекап, имеет другой размер. В этом случае можно произвести восстановление по файлам.

Примечание: самое удивительное, что после восстановления бекапа мало того, что все права на файлы сохранились (что логично), но сохранились так же и сжатие у файлов, и папки-ссылки на другие папки. Причем ссылки даже работали! Чудеса :)

Этап 2. Учим Vista загружаться с нового диска.

На этом этапе я потерял примерно 3 часа времени. Очень хотелось поотрывать руки разработчикам :)

Первое - записываем загрузчик на новый диск. Поиск в интернете говорит, что можно это сделать командой "Bootsect.exe /NT60 All". Упс! Нет такого файла на системном разделе. Еще немного поисков... Ага! Этот файл доступен, если загрузиться с CD-диска в режиме восстановления. Вставляем диск, грузимся, вызываем консоль, отдаем команду. Перегружаемся.

Упс... Не загружается.

Итак. Как мы все знаем, в Windows XP управление загрузками ОС с разных разделов осуществляется через файл boot.ini. В новой ОС - новый способ описания откуда грузить операционку. Вся информация теперь хранится в папке Boot куда и обращается наш загрузчик.
Главная проблема, с которой я столкнулся в том - что теперь используются не номера дисков/разделов, а уникальные идентифигаторы (GUID'ы). Естественно, что идентификатор нового диска я не знаю.
Вот тут и началось огромное количество приключений в попытках прописать правильную информацию :(
Краткий итог - все без исключения существующие на данный момент утилиты работают только с системным разделом, с которого была произведена загрузка. Естественно, что поправить я ничего не мог, потому что не мог загрузиться с нужного раздела. Получился этакий замкнутый круг.
Очередные поиски в интернете - ага! Необходимо загрузиться с CD-диска и в режиме восстановления и выбрать опцию "Исправление загрузочной информации".
Вставляю диск с сервером 2008 и... Щаз! Нет такого раздела!!
Потратив нное количество времени нахожу диск с Vista и загружаюсь с него. Ага! Есть! Жмем ссылку. Перегружаемся.

Этап 3. Учим ОС запускаться с нужного диска.

Грузимся. Логинимся. Ждем... И ждем... И ждем...
И тут понимаем - что-то не то :)
Итак: диск, с которого я загружался у меня был подмонтирован под буквой U:. Система же хотела загружаться с диска С: (который уже был физически отключен к тому времени).
Вспоминаю бурную молодость, и то, как поднимал еще Windows 2000 в аналогичной ситуации :) (Правда в тот раз я не мог залогиниться в систему - пришлось ее ломать через удаленный реестр).
Проверяем: есть возможность вызвать Диспетчер Задач (если вдруг не получается - загружаемся в безопасном режиме).
Запускаем Regedit. HKLM\System\MountedDevices.
Ага! Вот они. Меняем названия разделов \DosDevices\U: на \DosDevices\C: Перегружаемся.


Собственно, на этом и все... Система живет уже больше недели. Проблем не обнаружено.

P.S.: а винт, несмотря на бэд-блоки, всё ещё жив...

Интересный блог

Совершенно случайно обнаружил очень неплохой блог про VS2008 и C#.
Собственно, я искал как сделать красивые полупрозрачные окна в Висте. Воспользовавшись рекомендациями из вот этого поста в блоге сделал тестовый проект.

P.S.: А получилось ли у меня что-нибудь я узнаю только вечером, т.к. в пределах досягаемости висты нет :(