Archive

Archive for February, 2010

5й подкаст Петербургской группы ALT.NET

February 28th, 2010 No comments

SOLID & TDD и прочие показатели качества кода Ведущие: dnesteruk & butaji Наши гости: Alexander Byndyu и Vitaly Stakhov

SCRUM & XP http://ru.wikipedia.org/wiki/Scrum

http://ru.wikipedia.org/wiki/Экстремальное_программирование

http://www.infoq.com/minibooks/scrum-xp-from-the-trenches

OOD: Best practicies SOLID Single Responsibility Principle
Open/Closed Principles
Liskov Substitution Principle
Interface Segregation Principle
Dependency Inversion Principle
TDD: Best Practicies Mocks aren’t stubs
xUnit.NET, NUnit, MbUnit, и та утилита, от Microsoft, которую не смог вспомнить Александр
Ещё говорили о Pain-Driven Development Spolsky vs Uncle Bob NHibernate Sources, NAnt
Подкасты Петерубргской Группы Alt.Net Подкасты о разработке в среде .Net. Ключевые слова: C#, F#, Boo, Visual Studio, .Net, PostSharp, Asp.Net личная подкаст-лента Петербургская Группа Alt.Net (подробнее, RSS-поток)

Categories: Uncategorized Tags:

Рендеринг HTML в Bitmap

February 28th, 2010 No comments

В процессе рефакторинга проекта QRPR накопал много небольших, но полезных вспомогательных блоков. Решил начать ими делиться и возобновить блог :) Ищите под тегом HOWTO :) Задача:
Есть HTML, который может содержать и картинки и текст и разлисное форматирование. необходимо получить его представление в виде Картинки (Bitmap). Решение: Для этих целей есть несколько способов: 1 – использовать Движок WebKit, собрать его, воспользоваться оберткой WebKit для .NET. На этом мы остановились, замерив время подгрузки WebKit в наше ASP.NET приложение. 2 – использовать COM-компонент WebBrowser. Базовое описание такого решения есть на CodeProject, но нам оно не подошло, т.к. нужна была отдельная библиотека и возможность использования в ASP.NET Это решение я и опишу. Шаги по использованию очень просты: Создаем объект WebBrowser
Загружаем в него HTML
Ждем окончания рендеринга
Изменяем размер компонента под наши нужды (в нашем случае необхоидмо чтобы строки не переносились без <BR>
Ждем окончания рендеринга
Сохраняем содержимое окна компонента с помощью интерфейса IHTMLElementRender2
При этом важно понимать, в каком потоке будет использоваться этот компонент, т.к. WebBrowser должен работать в потоке STA! Необходимо подключить компонент mshtml, чтобы мы могли использовать интерфейсы Затем объявляем интерфейс, которй будем использовать для рендеринга [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("3050F669-98B5-11CF-BB82-00AA00BDCE0B")]
public interface IHTMLElementRender2
{
void DrawToDC(IntPtr hdc);
void SetDocumentPrinter(string bstrPrinterName, ref _RemotableHandle hdc);
}
Затем мы реализовали варианты выхова рендеринга. Вы можете явно указать, запустить ли его в отдельном потоке и ждать окончания или же текущий уже является STAThread и можно запускать рендеринг в нем. public static Bitmap RenderHTML (string html, bool atSTAThread)
{
RenderInfo ri = new RenderInfo() { Html = html, Result=null };

if (!atSTAThread) //Запускаем в отдельном потоке
Renderer(ri);
else
{
System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(Renderer));
th.SetApartmentState(System.Threading.ApartmentState.STA);
th.Start(ri);
//Ждем окончания
if (!th.Join(55000))
{ LogWriter(ex, QRPRErrorLevel.ErrorM2); }

}
return ri.Result;

}
Подготовка к рендерингу идет в методе Renderer:
private static void Renderer(object obj)
{
try
{
RenderInfo ri = (RenderInfo)obj;
WebBrowser webBrowser = new WebBrowser();
webBrowser.ScrollBarsEnabled = true;
webBrowser.DocumentText = "" + ri.Html + "";
while (webBrowser.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
//Выставляем блииинный Web-броузер чтобы правильно отображать.
webBrowser.Size = new Size(webBrowser.Document.Body.ScrollRectangle.Width, webBrowser.Document.Body.ScrollRectangle.Height);
while (webBrowser.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
webBrowser.ScrollBarsEnabled = false;
Rectangle rec = webBrowser.Document.Body.ClientRectangle;
ri.Result = GetBitmap(webBrowser, rec.Size.Width, rec.Size.Height);
}
catch(Exception ex) {
LogWriter(ex,QRPRErrorLevel.ErrorM2);
}
}
И сам рендеринг HTML!
//Основной рендеринг HTML
private static Bitmap GetBitmap(WebBrowser webBrowser, int width, int height)
{
IHTMLDocument2 rawDoc = (IHTMLDocument2)webBrowser.Document.DomDocument;
IHTMLElement rawBody = rawDoc.body;
IHTMLElementRender2 render = (IHTMLElementRender2)rawBody;
Bitmap bitmap = new Bitmap(width, height);
Rectangle bitmapRect = new Rectangle(0, 0, width, height);
webBrowser.DrawToBitmap(bitmap, bitmapRect);

using (Graphics graphics = Graphics.FromImage(bitmap))
{
IntPtr graphicshdc = graphics.GetHdc();
render.DrawToDC(graphicshdc);
graphics.ReleaseHdc(graphicshdc);
#if DEBUG
//При отладке отрисовываем еще и прямоугольник
//graphics.DrawRectangle(Pens.Black, bitmapRect);
//graphics.Save();
#endif
graphics.Dispose();

return bitmap;
}
}

Исходный код библиотеки можно скачать с it arts

Categories: Uncategorized Tags:

Подкаст ПолДевятого #7. RIA, образование и многое другое

February 28th, 2010 No comments

В 7-ом выпуске у нас в гостях известный подкастер Будам (http://budam.rpod.ru/).
Разговариваем об образовании в США и России, поиске работы, иммиграции, и технологиях Silverlight и Flash. Темы выпуска: Стив Джобс о Flash.
RIA. Microsoft Silverlight и Adobe Flex.
Как выглядит высшее образование в США.
Как выбрать "специализацию" в программировании.
Как устроиться на работу после ВУЗа. Удалённая работа.
Английский язык и иммиграция в США.

Кроме того, авторам подкаста будет очень приятно, если Вы поддержите нас на Habrahabr

Categories: Uncategorized Tags:

Свиттер #3

February 28th, 2010 No comments

Приветствуем вас, наши дорогие читатели!

Тех, кто уже давно с интересом следит за нашим развитием, и тех, кто только что к нам присоединился. В эфире третий номер журнала «Свиттер». Встречайте!

Итак, в этом номере …

Вас ждет увлекательное погружение в мир интернет программирования. Вместе с Дмитрием Ляпиным вы, наконец, узнаете, кто же такой добрый высылает вам на почту утерянные пароли, кто способен так вежливо ответить на самые тупые из ваших вопросов, и без кого уже невозможно представить себе работу нормального сайта. Наших чудо-помошников зовут интернет-боты. И им посвящена целая серия статей нового номера. Не бойтесь, если в вашей голове до сих пор каша, и, говоря откоровенно, вы не понимаете толком, как собственно работает интернет. Специально для вас написана статья об HTTP – все будет расставлено по полочкам, не сомневайтесь 8) А завершить вашу интернет-трапезу мы предлагаем десертом от Юрия Майорова «Как написать свой собственный IM бот менее, чем за 5 минут», перевод статьи Амита Агарвала.

Далее в номере. Рассказ о технологии AJAX от нового друга журнала, который, правда, еще не счел наше знакомство настолько близким, чтобы открыть свое имя. Итак, plastun и его «Введение в AJAX» к вниманию читателей Свиттера.

Эстафетную палочку у Пластуна перехватывает Георгий Могелашвили и учит нас, как использовать технологию AJAX в ASP.NET. В наше время без асинхронности никуда.

Немного отвлечься и подумать о вечном поможет чтение статьи Джоэла Спольски «О том, чего нельзя делать ни в коем случае» в переводе Екатерины Котовой. В жизни каждого программиста рано или поздно наступает момент, когда его код начинает источать не самый приятный аромат, а в лексиконе появляется это страшное слово – рефакторинг.

Обычно мы не включаем в журнал задачки, публикуемые на сайте, но sql-загадка про дни рождения (взявшая первое место по числу комментариев) вызвала у читателей Свиттера просто невиданное волнение. В номер войдет несколько ваших решений, а также решение, добытое Ваней, и единогласно признанное лучшим. Вот что значит посмотреть на проблему под другим углом!

Я смотрю, вы уже начинаете нервничать… А где Кальвадос?! Почему нет Кальвадоса? Хотим обещанный Кальвадос!! Угадала?

Он здесь 8) Андрей Мишанин сдержал свое слово и наливает нам первую рюмку своего чудесного напитка. Так что садитесь поудобнее, дорогие читатели, рассказ из уст настоящего ценителя яблок начинается…

Ну и напоследок Свиттер хочет пожелать своим читателям новых профессиональных успехов, упорства и продуктивности. А чтобы вас как-то приободрить, Александр Никитин познакомит нас с еще несколькими парочками привлекательных гиков. Смотрите и любуйтесь! Нам есть, к чему стремиться)

Все, не смеем больше задерживать. Бегите, бегите уже читать ваш новый номер. Надеемся, что он вам понравится 8)

Скачать журнал в формате PDF

.NET и работа с неуправляемым кодом. Часть 2

February 27th, 2010 No comments

Тем, кто не читал первую часть — читать После выполнения первой части работ, создания структур, мне задачу усложнили. Библиотечка с которой мне нужно было работать — выделяла массив, размер которых не был известен до момента запуска. Он был динамический, т.е. чем больше данных — тем больше массив. Тут задача стала поинтереснее, т.к. старый способ, когда было достаточно использовать структуру, в которой этот размер был указан — уже не подходил.
Тогда я стал дальше изучать маршалинг и нашел еще несколько методов у класса Marshal, которые помогли мне решить задачу.

И так, собственно сама задача:
— библиотечка на входе принимает указатель на массив указателей (void*) o_O
— содержимое массива — этой указатели на другие массивы, например на массив символов, где указан адрес файла, указатели на массивы int, в которых содержаться некоторые данные

Т.к. мне часто пришлось бы вызывать некоторый код, я создал специальный статический класс UnMemory, его код ниже

После выполнения первой части работ, создания структур, мне задачу усложнили. Библиотечка с которой мне нужно было работать — выделяла массив, размер которых не был известен до момента запуска. Он был динамический, т.е. чем больше данных — тем больше массив. Тут задача стала поинтереснее, т.к. старый способ, когда было достаточно использовать структуру, в которой этот размер был указан — уже не подходил.

Тогда я стал дальше изучать маршалинг и нашел еще несколько методов у класса Marshal, которые помогли мне решить задачу.

И так, собственно сама задача:
— библиотечка на входе принимает указатель на массив указателей (void*) o_O
— содержимое массива — этой указатели на другие массивы, например на массив символов, где указан адрес файла, указатели на массивы int, в которых содержаться некоторые данные

Т.к. мне часто пришлось бы вызывать некоторый код, я создал специальный статический класс UnMemory, его код ниже /// <summary>
/// Класс для очистки блоков неуправляемой памяти
/// </summary>
public static class UnMemory
{
/// <summary>
/// Очередь для высвобождения блоков памяти
/// </summary>
private static Queue<IntPtr> queue = new Queue<IntPtr>();

public static void Enqueue(IntPtr ptr)
{
queue.Enqueue(ptr);
}

private static void FreeIntPtr(IntPtr ptr)
{
if (ptr != IntPtr.Zero)
Marshal.FreeCoTaskMem(ptr);
}

/// <summary>
/// Освобождение блоков памяти в неуправляемом пространстве
/// </summary>
public static void FreeMemory()
{
while (queue.Count > 0)
{
IntPtr temp = queue.Dequeue();
// освобождаем то, что записано в памяти
Marshal.FreeCoTaskMem(temp);
}
}
}

В этом классе я определил очередь, в которую буду добавлять все указатели на сгенерированную неуправляемую память, чтобы в конце вызывать его статический метод FreeMomory() для полной очистки выделенной памяти. Для добавления указателя в очередь, необходимо вызвать UnMemory.Enqueue(ptr);
Мне показалось это удобнее, чем обращаться к указателям, чтобы очистить каждый из них. Ведь так можно что-то пропустить и получим утечку памяти.

Также, мне понадобился еще один класс, который я назвад UnMemory, который будет выделять в неуправляемой памяти место и заполняеть его данными, ну и конечно читать из неё.
Его код ниже /// <summary>
/// Класс для работы неуправляемой памятью
/// </summary>
/// <typeparam name="T">Структурный тип данных</typeparam>
public static class UnMemory<T>
where T : struct
{

/// <summary>
/// Получить указатель на структуру в неуправляемом куску памяти
/// </summary>
/// <param name="memory_object">Объект для сохранения</param>
/// <param name="ptr">Указатель</param>
/// <typeparam name="T">Структурный тип данных</typeparam>
public static void SaveInMem(T memory_object, ref IntPtr ptr)
{
if (default(T).Equals(memory_object))
{
// объявляем указатель на кусок памяти
ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(T)));
UnMemory.Enqueue(ptr);
return;
}

if (ptr == IntPtr.Zero)
{
// объявляем указатель на кусок памяти
ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(T)));

// записываем в память данные структуры
Marshal.StructureToPtr(memory_object, ptr, false);
}
else
{
// записываем в память данные структуры
Marshal.StructureToPtr(memory_object, ptr, true);
}

UnMemory.Enqueue(ptr);
}

/// <typeparam name="T">IntPtr, int, float</typeparam>
/// <exception cref="System.ArgumentException">Параметр #1 должен быть массивом IntPtr, int, float</exception>
public static void SaveInMem2(T[] managedArray, ref IntPtr pnt)
{
Debug.Assert(managedArray != null, "Объект не должен быть Null");
Debug.Assert(managedArray.Length != 0, "Объект не может иметь длину массива 0");

if (pnt == IntPtr.Zero)
{
// объявляем указатель на кусок памяти. Размер = размер одного элемента * количество
//int size = Marshal.SizeOf(typeof(T)) * managedArray.Length;
int size = Marshal.SizeOf(managedArray[0]) * managedArray.Length;
pnt = Marshal.AllocCoTaskMem(size);
}

// в зависимости от типа массива, мы вызываем соответствующий метод в Marshal.Copy
if (typeof(T) == typeof(int))
{
int[] i = managedArray as int[];
Marshal.Copy(i, 0, pnt, i.Length);
}
else if (typeof(T) == typeof(byte))
{
byte[] b = managedArray as byte[];
Marshal.Copy(b, 0, pnt, b.Length);
}
else if (typeof(T) == typeof(float))
{
float[] f = managedArray as float[];
Marshal.Copy(f, 0, pnt, f.Length);
}
else if (typeof(T) == typeof(char))
{
// читаем массив байтов и переводим в текущую кодировку
byte[] b = Encoding.Default.GetBytes(managedArray as char[]);
Marshal.Copy(b, 0, pnt, b.Length);
}
else if (typeof(T) == typeof(IntPtr))
{
IntPtr[] p = managedArray as IntPtr[];
Marshal.Copy(p, 0, pnt, p.Length);
}
else
throw new ArgumentException("Параметр #1 должен быть массивом IntPtr, int, float или char");

// запоминаем указатель, чтобы потом его почистить
UnMemory.Enqueue(pnt);
}

/// <summary>
/// Чтение структуры из неуправляемой памяти
/// </summary>
/// <param name="ptr">Указатель</param>
/// <param name="type">Тип данных для чтения</param>
/// <returns>Структура из памяти</returns>
public static T ReadInMem(IntPtr ptr)
{
return (T)Marshal.PtrToStructure(ptr, typeof(T));
}

public static T[] ReadInMem2(IntPtr ptr, int size)
{
if (typeof(T) == typeof(int))
{
int[] memInt = new int[size];
Marshal.Copy(ptr, memInt, 0, size);
return memInt as T[];
}
else if (typeof(T) == typeof(byte))
{
byte[] memByte = new byte[size];
Marshal.Copy(ptr, memByte, 0, size);
return memByte as T[];
}
else if (typeof(T) == typeof(float))
{
float[] memFloat = new float[size];
Marshal.Copy(ptr, memFloat, 0, size);
return memFloat as T[];
}
else if (typeof(T) == typeof(IntPtr))
{
IntPtr[] memIntPtr = new IntPtr[size];
Marshal.Copy(ptr, memIntPtr, 0, size);
return memIntPtr as T[];
}
else
throw new ArgumentException("Параметр #1 должен быть массивом int, float или char");
}

/// <summary>
/// Класс переводит массивы
/// </summary>
public static class UnArray
{
/// <summary>
/// Перевод одномерного массива в двумерный
/// </summary>
/// <typeparam name="T">Тип исходного массива</typeparam>
/// <param name="array">Исходный массив</param>
/// <returns>Двумерный массив</returns>
public static T[,] Rank1_Rank2(T[] array, int x, int y)
{
T[,] res = new T[x, y];
int size = Buffer.ByteLength(array);
Buffer.BlockCopy(array, 0, res, 0, size);
return res;
}

/// <summary>
/// Перевод двумерного в одномерный массив
/// </summary>
/// <typeparam name="T">Тип исходного массива</typeparam>
/// <param name="array">Исходный массив</param>
/// <returns>Одномерный массив</returns>
public static T[] ToRank1(T[,] array, int x, int y)
{
T[] res = new T[x * y];
int size = Buffer.ByteLength(array);
Buffer.BlockCopy(array, 0, res, 0, size);
return res;
}

/// <summary>
/// Перевод одномерного массива в трехмерный
/// </summary>
/// <typeparam name="T">Тип исходного массива</typeparam>
/// <param name="array">Исходный массив</param>
/// <returns>Трехмерный массив</returns>
public static T[, ,] Rank1_Rank3(T[] array, int x, int y, int z)
{
T[, ,] res = new T[x, y, z];
int size = Buffer.ByteLength(array);
Buffer.BlockCopy(array, 0, res, 0, size);
return res;
}

/// <summary>
/// Перевод трехмерного массива в одномерный
/// </summary>
/// <typeparam name="T">Тип исходного массива</typeparam>
/// <param name="array">Исходный массив</param>
/// <returns>Одномерный массив</returns>
public static T[] ToRank1(T[, ,] array, int x, int y, int z)
{
T[] res = new T[x * y * z];
int size = Buffer.ByteLength(array);
Buffer.BlockCopy(array, 0, res, 0, size);
return res;
}

/// <summary>
/// Перевод одномерного массива в четырехмерный
/// </summary>
/// <typeparam name="T">Тип исходного массива</typeparam>
/// <param name="array">Исходный массив</param>
/// <returns>Четырехмерный массив</returns>
public static T[, , ,] Rank1_Rank4(T[] array, int x, int y, int z, int w)
{
T[, , ,] res = new T[x, y, z, w];
int size = Buffer.ByteLength(array);
Buffer.BlockCopy(array, 0, res, 0, size);
return res;
}

/// <summary>
/// Перевод четырехмерного массива в одномерный
/// </summary>
/// <typeparam name="T">Тип исходного массива</typeparam>
/// <param name="array">Исходный массив</param>
/// <returns>Одномерный массив</returns>
public static T[] ToRank1(T[, , ,] array, int x, int y, int z, int w)
{
T[] res = new T[x * y * z * w];
int size = Buffer.ByteLength(array);
Buffer.BlockCopy(array, 0, res, 0, size);
return res;
}
}
}

Этот класс обобщенный, что позволило не создавать множество методов для каждого типа отдельно. Смысл этого класса, он повереводит массивы из управляемой памяти в неуправляемую и наоборот. Метод SaveInMem сохраняет структуру, которую мы ему передадим, метод SaveInMem2 сохранит массив, таких как intPtr, Int, float… Ограничение представляет сам метод Marshal.Copy, в котором реализовано копирование отдельно для int, отдельно для byte и некоторых других. Поэтмоу нужно было для каждого типа реализовать свой свой вызов, такой как if (typeof(T) == typeof(int))

Методы ReadInMem и ReadInMem2 для чтения структур и массивов, аналогично SaveMem и SaveMem2.

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

И так, теперь переходим непосредственно к самому вызову.
Из С, вызов выгладит так:extern "C" int __import TkzIvc(void *mpGS[]);
Для C# этот вызов будет выглядить так:
[DllImport(@"DllTkzIvc.dll")]
private static extern int _TkzIvc([In] IntPtr mpGS);
Осталось самое простое. Сделать обертку для использования [StructLayout(LayoutKind.Sequential)]
public class mpSh_Struct : IDisposable
{
private IntPtr[] mpSh = new IntPtr[5];
private int size_vetv; // число ветвей

/// <summary>
/// Путь к файлам схемы
/// </summary>
/// <value>Строка размером до 255 символов</value>
/// <exception cref="System.ArgumentOutOfRangeException">Возникает, если строка больше, чем 255 символов</exception>
public string PathSh
{
get
{
return Marshal.PtrToStringAnsi(this.mpSh[0]);
}
set
{
if (String.IsNullOrEmpty(value) || value.Length > 255)
throw new ArgumentOutOfRangeException("Данные должны быть обязательно заданы и размером не более 255 символов");
this.mpSh[0] = Marshal.StringToHGlobalAnsi(value);
}
}

///<summary>
/// указатель массива int[]
///</summary>
public int[] TypeV
{
get { return UnMemory<int>.ReadInMem2(this.mpSh[1], this.size_vetv); }
set { UnMemory<int>.SaveInMem2(value, ref this.mpSh[1]); }
}

/// <summary>
/// указатель массива char u1[][6]
/// </summary>
/// <value>Массив из строк размером до 5 символов</value>
public string[] u1
{
get
{
// читаем массив байт из памяти
byte[] mem = UnMemory<byte>.ReadInMem2(this.mpSh[2], this.size_vetv * 6);
// определяем, каким же должен быть результирующий массив string[]
int length = this.size_vetv;
// генерируем новый массив
string[] res = new string[length];
for (int i = 0; i < length; i++)
{
// в цикле обрабатываем массив байт, который переводим в String и обрезаем символы конца
res[i] = Encoding.Default.GetString(mem, i * 6, 6).TrimEnd(&apos;\0&apos;);
}
return res;
}
set
{
// генерируем массив char[]
char[] res = new char[value.Length * 6];
for (int i = 0; i < value.Length; i++)
{
if (value[i] != null)
value[i].CopyTo(0, res, i * 6, value[i].Length);
}
// сохраняем
UnMemory<char>.SaveInMem2(res, ref this.mpSh[2]);
}
}

/// <summary>
/// указатель массива float[]
/// </summary>
public float[] EK1B1
{
get { return UnMemory<float>.ReadInMem2(this.mpSh[4], this.size_vetv); }
set { UnMemory<float>.SaveInMem2(value, ref this.mpSh[4]); }
}

public int[] ParamSh
{
get { return UnMemory<int>.ReadInMem2(this.mpSh[3], 6); }
set
{
this.size_vetv = value[0]; // число ветвей (ParamSh[0])
UnMemory<int>.SaveInMem2(value, ref this.mpSh[3]);
}
}

/// <summary>
/// Вызов библиотеки на обновление данных
/// </summary>
public bool Read(out string errorText)
{
try
{
IntPtr t = new IntPtr();
UnMemory<IntPtr>.SaveInMem2(this.mpSh, ref t);

_TkzIvc(t);

mpSh = UnMemory<IntPtr>.ReadInMem2(t, this.mpSh.Length);

int[] paramSh = this.ParamSh; // параметры схемы
this.size_vetv = paramSh[0]; // число ветвей
errorText = String.Empty;
return true;
}
catch (DllNotFoundException)
{
errorText = "Целость программы нарушена. Пожалуйста переустановите программу или обратитесь к администратору за помощью";
return false;
}
catch (Exception exp)
{
errorText = exp.Message;
return false;
}
}

public void Dispose()
{
// очищаем память для выделенных объектов
UnMemory.FreeMemory();

// очищаем то, что выделялось библиотечкой
for (int i = 1; i < 5; i++)
{
IntPtr ptr = mpSh[i];
// чистим
UnMemory.FreeIntPtr(ptr);
}
}
}
ОБРАТИТЕ ВНИМАНИЕ, что для простого текста, строки, я использовал Marshal.StringToHGlobalAnsi(text). Т.к. это специальный метод класса Marshal для чтения и записи обычной текстовой строки, которая оканчивается символом &apos;\0&apos;

Массив указателей сохраняется в mpSh, а для всех полей реализованы методы get и set, которые почти прозрачно от самого класса пишут и читают неуправляемую память. Метод Read переносит массив указателей в неуправляемую память, чтобы потом прочесть данные из неё (при вызове библиотеки, эти данные заполняются). После вызова Read, мы сможем прочесть нужную нам переменную просто обратившись к нужному полю.

Правда есть одно ограничение… лучше получить ссылку на массив ( int[] paramSh = this.ParamSh ), а уже потом работать с ним, чем обращаться к полю постоянно ( this.size_vetv = this.ParamSh[0] ). Потому как при обращении, данные заново будут читаться из неуправляемой памяти. Несмотря, что это проиходит быстро, если вызовов много, это может занять достаточное время.

PS: в рамках данной статьи не был рассмотрен CustomMarshaling. Суть его в том, что Вы можете польностью настраивать, как должна сохраняться определенная структура, с его помощью Вы можете поддерживать различные версии своих библиотек. Например, сохранять DateTime как String в определенном формате и пр. Такие случаи бывают реже, поэтому этому можно посветить отдельную статью.

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

Categories: Uncategorized Tags:

Learning SQL

February 27th, 2010 No comments

http://ecx.images-amazon.com/images/I/51TAZcFPkGL._BO2,204,203,200_PIsitb-sticker-arrow-click,-76_AA240_SH20_OU01_.jpgLearning SQL by Alan Beaulieu
O’Reilly Media; Annotated edition (April 20, 2009)
336 pages; Language: English
ISBN-10: 0596520832; ISBN-13: 978-0596520830

Updated for the latest database management systems — including MySQL 6.0, Oracle 11g, and Microsoft’s SQL Server 2008 — this introductory guide will get you up and running with SQL quickly. Whether you need to write database applications, perform administrative tasks, or generate reports, Learning SQL, Second Edition, will help you easily master all the SQL fundamentals.

Each chapter presents a self-contained lesson on a key SQL concept or technique, with numerous illustrations and annotated examples. Exercises at the end of each chapter let you practice the skills you learn. With this book, you will:

Экспорт функции из .Net dll или пишем managed функцию для rundll32

February 26th, 2010 No comments

Суть вопроса В своей работе, я несколько раз сталкивался с необходимостью экспорта функции из managed dll так, как это делалось в C++. Как правило, такая необходимость возникает при написании плагинов к unmanaged программам.

Поиск информации по данной тематике, как правило приводит к 2 вариантам:
Из managed dll функции экспортировать нельзя.
Можно, если создать обёртку в виде unmanaged dll и из неё вызывать managed по средством COM.
Первый вариант, понятное дело, не устраивает. Второй же, требует знаний по написанию unmanaged dll и работы их них с COM, что несколько повышает сложность решения.

После более детального поиска информации по данному направлению, оказалось, что есть ещё и третий вариант. Данный вариант позволят делать именно то, что требуется. Именно его я и хотел бы описать.

Что реализуем
Пример будет реализован в виде функции managed dll которую мы можем вызвать при помощи rundll32.exe. Функция должна будет выполнить простое действие – вывести MessageBox с текстом "Hello <аргумент переданный rundll32>".

Rundll32  Функция, которая вызывается rundll32.exe имеет чётко оговоренный синтакс, который описана в MSDN, выдержка:

VOID CALLBACK InstallHinfSection(
__in HWND hwnd,
__in HINSTANCE ModuleHandle,
__in PCTSTR CmdLineBuffer,
__in INT nCmdShow
);
В данном примере нас интересует только параметр CmdLineBuffer, в который будет попадать аргумент, переданный при вызове rundll32.

Managed Dll Итак, на основе данной информации реализуем следующий managed класс:
using System;
using System.Windows.Forms;

namespace HelloLib
{
public class HelloClass
{
public static void Hello(IntPtr hwnd, IntPtr moduleHandle, string cmdLineBuffer, int nCmdShow)
{
MessageBox.Show(cmdLineBuffer);
}
}
}
Компилируем.

Немного магии Наконец, мы добрались до того, ради чего собственно и писалась эта статья.
Для дальнейших манипуляций нам потребуются две утилиты: ilasm (входит в поставку .Net Framework) и ildasm (входит в поставку PlatformSDK), версии должны подходить к .Net 2.0 и выше. Самый удобный вариант их использования – Visual Studio Command Promt.

Делай раз
Дезассемблируем нашу Dll, для чего переходим в директорию с ней и выполняем следующую комманду: ildasm /OUT=HelloLib.il HelloLib.dll На выходе получаем 2 файла: HelloLib.il и HelloLib.res, нас интересует первый.
Открываем его в текстовом редакторе и видим ilasm листинг нашей dll.

Делай два Ищем в листинге следующий кусок:
// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi beforefieldinit HelloLib.HelloClass
extends [mscorlib]System.Object
{
.method public hidebysig static void Hello(native int hwnd,
native int moduleHandle,
string cmdLineBuffer,
int32 nCmdShow) cil managed
{
// Code size 8 (0×8)
.maxstack 8
IL_0000: ldarg.2
IL_0001: call valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)
IL_0006: pop
IL_0007: ret
} // end of method HelloClass::Hello

.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0×7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method HelloClass::.ctor

} // end of class HelloLib.HelloClass

// =============================================================
Это и есть наш класс, нас интересует код нашего метода: .method public hidebysig static void Hello(native int hwnd,
native int moduleHandle,
string cmdLineBuffer,
int32 nCmdShow) cil managed
{
// Code size 8 (0×8)
.maxstack 8
IL_0000: ldarg.2
IL_0001: call valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)
IL_0006: pop
IL_0007: ret
} // end of method HelloClass::Hello
Его нужно изменить следующим образом:
.method public hidebysig static void Hello(native int hwnd,
native int moduleHandle,
string cmdLineBuffer,
int32 nCmdShow) cil managed
{
.export[1] // <<– Добавили
// Code size 8 (0×8)
.maxstack 8
IL_0000: ldarg.2
IL_0001: call valuetype [System.Windows.Forms]System.Windows.Forms.DialogResult [System.Windows.Forms]System.Windows.Forms.MessageBox::Show(string)
IL_0006: pop
IL_0007: ret
} // end of method HelloClass::H А именно добавили ".export[1]" в начало метода.
Сохраняем файл.

Делай три
Осталось собрать нашу библиотеку обратно, делаем это с помощью ilasm:
ilasm /DLL /OPTIMIZE /RESOURCE=HelloLib.res HelloLib.ilНа выходе получаем нашу Dll, запускаем её при помощи rundll32 и радуемся результату:

Немного теории и особенностей
Теория
Итак, мы добавляем в тело метода следующее: .export[N]В данном коде N – это порядок функции в таблице экспорта, то есть если нам нужно экспортировать две функции, у первой N=1, у второй N=2. Если у двух разных функций значения будут одинаковыми, то экспортироваться будет только последняя.

При добавлении данного параметра ilasm делает следующие изменения:
Меняет тип сборки на mixed (содержащую managed и unmanaged код).
Создаёт таблицу экспорта и заносит в неё все экспортированные функции.

Экспорт с другим именем Есть так же возможность экспорта функции с другим именем, при этом её имя для managed кода сохраняется прежним делается заменой ".export[N]" на:.export[N] as <NewFunctioName> Calling convention Меняем calling convention функции, приводя код объявления к следующему виду:
.method public hidebysig static void modopt([mscorlib]System.Runtime.CompilerServices.CallConvThiscall) Hello(native int hwnd,
native int moduleHandle,
string cmdLineBuffer,
int32 nCmdShow) cil managed То есть добавляем перед именем метода следующее: modopt([mscorlib]System.Runtime.CompilerServices.CallConvThiscall)В данном случае calling convention меняется на Thiscall. Другие возможные варианты:
System.Runtime.CompilerServices.CallConvCdecl
System.Runtime.CompilerServices.CallConvFastcall
System.Runtime.CompilerServices.CallConvStdcall
System.Runtime.CompilerServices.CallConvThiscall
Особенности При использовании данного метода возможны проблемы со сборкой мусора, выполняемой при закрытии приложения, в которое подгружена данная dll, что может приводить к исключениям, иногда ручной вызов Dispose объектов спасает ситуацию.

В заключение Аналогичным образом можно экспортировать и другие функции, что позволяет писать плагины к umanaged программам.

P.S. Первая статья на сайте, отзывы и предложения приветствуются.

Categories: Uncategorized Tags:

Получи две Visual Studio 2010 и узнай как управлять проектами бесплатно

February 26th, 2010 No comments

12 марта Учебный Центр Luxoft совместно с Microsoft проводят семинар на тему: «MS Team System 2010: Революция в управлении проектами разработки ПО». Несмотря на то, что стоимость семинара составляет 4000 рублей, можно хорошо съэкономить. Во-первых, авторы пяти лучших вопросов по продукту MS Team System 2010 получат бесплатное приглашение на семинар (куда задавать вопросы ищите, например тут), во-вторых, участнику сообщества .NET предоставляется скидка в 500 рублей (не забывайте сообщать о том, что Вы участник сообщества, а если еще нет, то зарегистрироваться несложно), в-третьих, участникам семинара выдадут неплохой бонус, в который входит сертификат на две лицензии Visual Studio 2010 Professional (нужно будет обменять после 12 апреля на коммерческую версию), сертификат на бесплатное онлайн-обучение в Учебном Центре Luxoft, и еще приятные полезности. Регистрация на семинар тут, или пишите письма.

Этот сообщение зеркалируется. Читать далее …
Перейти в основной блог.

Добавить комментарий.

Categories: Uncategorized Tags:

Типичная начинка моих MVC-проектов

February 26th, 2010 No comments

В прошлом посте я описал несколько стандартных вещей которые я делаю при создании нового Asp.Net MVC проекта, и узнал для себя много полезного из комментариев. В этом посте я хочу рассказать про тот стек контролов и библиотек который фигурирует в большинстве моих MVC приложений, независимо от тематики. Так уж получается, что один раз попробовав один [...]

Categories: Uncategorized Tags:

ChildCast #4: Используем WorldWideTelescope для изучения устройства вселенной

February 26th, 2010 No comments

Наконец-то дошли руки выложить очередной выпуск детского технологического аудио-видео-подкаста ChildCast #4. В этом подкасте мы с Вики рассказываем про то, как мы изучаем устройство вселенной, а точнее солнечной системы. Начиная с бумажной детской энциклопедии, мы затем пользуемся программой WorldWideTelescope от Microsoft Research чтобы посмотреть "со стороны" на планеты солнечной системы, на созвездия, на планету Земля ночью (оказывается, освещённость разных участков зависит от благосостояния и от плотности населения!) и т.д. WorldWideTelescope содержит множество интерактивных туров, отталкиваясь от которых можно увидеть много интересного! Оригинал видео на YouTube
Подкаст на Russian Podcasting
RSS-лента 

Categories: Uncategorized Tags: