ГЛАВА 8. Некоторые аспекты перехода с VB 6 на платформу .NET
Приняв решение о переходе на новую платформу хочется сохранить, приобретенные навыки программирования и возможность использовать унаследованный кода. Код, полученный при создании управляемых сборок, может работать в мире COM (служба RCW), а так же вызывать типы из DLL, написанных на неуправляемом С (служба Pinvoke, пространство System.Runtime.InteropServices). Все это платформа .NET делает так хорошо, что вы вполне можете использовать VB.NET в стиле VB, почти ничего не зная о премудростях новой платформы программирования от Microsoft. В дальнейшем при желании использовать новые объектно-ориентированные возможности, которые были добавлены в язык VB (первые версии C++ назывались – “C с классами” по аналогии можно сказать, что VB.NET это VB c классами) можно воспользоваться многочисленной литературой по данной теме. На платформе .NET (подразумевается .NET Framework = CLR + FCL при CLR = CLR/CTS), так же как и в обычном VB6 можно использовать DirectX7/8, реализация которого построена на использовании COM интерфейсов. Обратиться к COM из .NET Вам поможет служба RCW, которая играет роль мостика между этими технологиями. Служба RCW возьмет на себя всю черновую работу (управление ссылками AddRef()/Release(), добыча указателей на интерфейсы QueryInterface(), скрытие низкоуровневых интерфейсов – все, что раньше делал VB5/6). Сейчас создадим приложение на VB.NET, использующие Direct3DRM3 для вывода в своем окне вращающегося логотипа “VB.NET” синего цвета. Файл проекта для приложения VB.NET находится в папке N19/BitBlitWinDXVB. В папке N19/BitBlitWinDX находится близнец написанный на C#, который выводит логотип “C#” зеленого цвета. Так как VB.NET и C# практически равнозначные языки (что не верно для VB и VC++) для данной главы приложения будут приводиться на обоих языках. Создайте приложение типа Windows Application. После этого необходимо создать буфер (промежуточный класс) для обращения к COM серверу. Выполните команду Project/AddReference, на вкладке COM укажите путь к библиотеке типов (Рис.1).
Рис. 1
В окне Solution Explorer автоматически должна появиться ссылка DxVBLib, а в каталоге где находится исполняемый файл, после компиляции появится файл Interop.DxVBLib.dll (Рис.
2)
Рис. 2
Если открыть файл Interop.DxVBLib.dll в утилите ILDasm.exe, то Вы увидите эквиваленты типов COM, доступных для вызова в .NET.(Рис.
3)
Рис. 3
Найдите DirectX7Class, если создать объект этого класса Вы получите универсальный указатель (указатель на указатель) через который мы и будем действовать – предоставление этого указателя и есть работа службы RCW. Работа обоих приложений представлена на рисунке
4.
Рис. 4
Оба приложения, написаны на двух ключевых языках платформы .NET C# и VB.NET, но для вывода используется самый легкий способ работы с графикой 3D интерфейс Direct3D Retained Mode из седьмой версии DirectX. Точно таким же образом можно получить доступ к другим унаследованным от технологии COM объектам – например ADO/DAO. Посмотрите на окно AddReference до и после инсталляции на компьютер VB6 (.NET и Visual Studio .NET были установлены первыми)(Рис.
5).
Рис. 5
Появились такие привычные объекты DAO. Зная объектную модель ADO/DAO и применив службу RCW можно писать базы данных на VB.NET как на VB5/6. При изучении GDI+ оказалось, что новейший API не поддерживает XOR. Действительно вызов популярных блиттеров (BitBlit(); StretchDIBits()) старого GDI ни к чему не привел. В папке N20/ приводятся два приложения BitBlitVB и BitBlit, выполняющие одну и туже задачу - демонстрируют примеры простой анимации. Первое приложение написано на VB.NET, а второе на C#. Для демонстрации меж языкового взаимодействия напишем ресурсную DLL на C# ( написать такую же на VB.NET элементарная задача) в ней будет храниться картинка (проект находится в папке N21/). Для создания ресурсной DLL на C# выберите приложение типа Class Library (Рис.
6).
Рис. 6
С помощью команды Project/Add Existing Item добавьте в проект файл “Meh.jpg”(находится в папке N21/). Теперь самое главное – графический файл в окне Properties должен быть помечен, как Embedded Resource (Рис.
7).
Рис. 7
Добавьте следующий код в проект (файл BitRes.cs):
using System;
using System.Drawing;
namespace BitRes
{
public class BitRes
{
public Bitmap MMeh;
public BitRes()
{
MMeh = new Bitmap(GetType(),
"Meh.jpg");
}
}
}
После компиляции появится файл BitRes.dll. К этой DLL может обращаться код, написанный на VB.NET или C#. Переходим к коду который сможет загружать из BitRes.dll, встроенную картинку (папка N20/). Создайте на VB.NET приложение типа Windows Application (файл проекта см. папку N20/BitBlitVB, в папке N20/BitBlit находится близнец написанный на C#). После Public Class Form1 вставьте объектную переменную Dim BitfromRes As New BitRes(). В событие Form1_Paint() вставьте код:
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As _
System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
Rectangle(GetWindowDC(Me.Handle.ToInt32()), 72, 3, 429, 18)
Rectangle(GetWindowDC(Me.Handle.ToInt32()), 75, 6, 426, 15)
Rectangle(GetWindowDC(Me.Handle.ToInt32()), 78, 9, 423, 12)
Dim g As Graphics
g = e.Graphics
g.DrawImage(BitfromRes.MMeh, 5, 5)
g.DrawImage(bit1, 220, 5)
g.DrawImage(bit2, xp, yp)
End Sub
Вызовете окно для установок текущего проекта и в поле Root namespace введите – BitRes (посмотрите на строчку namespace BitRes из проекта создания DLL)(Рис.
8).
Рис. 8
После компиляции картинка из DLL, написанной на C# должна появится на форме. Обратите внимание на вызовы функций старого GDI с применением атрибутов . Для их вызова необходимо включить пространство System.Runtime.InteropServices (служба Pinvoke). Эти функции рисуют прямоугольники на заголовке формы, но с помощью этого метода не получится вызвать BitBlit(); StretchDIBits() и использовать XOR (вызвать можно, но это ничего не даст). Внимательно изучите код в событии Form1_Load() в цикле FOR. Вся суть в применении функций GetPixel()/SetPixel() – с начало читаются данные о пикселях из объекта bit1(создан на основе файла), если пиксель белый он переписывается в объект bit2 (создан как битовая карта в памяти с форматом пикселей PixelFormat.Format32bppArgb) как прозрачный – был белый стал прозрачный. Самое главное заключено в строчке bit3 = BitfromRes.Mmeh из события Button1_Click(). Данное присвоение, это ошибка и теперь bit3 указывает не на “свою память”, а на “чужую”. В профессиональном программировании на C++ таким ошибкам уделяется очень большое значение (поверхностное/полное копирование), но эта тема относится к тонкостям ООП (жизненный цикл объекта, удаление ресурсов итп ). Графически ошибка выразится в следующем - после загрузки приложения подвигайте флажком не нажимая кнопку “Сохранить” и после сохранения (Рис.
9).
Рис. 9
При создании объектов битовых карт, место для них выделяется в динамической памяти. В событии Button1_Click() Вы получаете указатель bit3 на память достаточную для размещения пустой битовой карты размером 213х265 пикселей формата Format32bppArgb. Присвоение bit3 = BitfromRes.Mmeh лишает вас этой памяти. Вместо того, чтобы по очереди скопировать в пустую память, с начало задний план механизма, а затем деталь (с прозрачными пикселями) копирование происходит на задний фон и портит его (Рис.
10).
Рис. 10
С точки зрения С++ в этой программе совершена серьезная ошибка – “дикий” указатель, но платформа .NET справляется с этой задачей методом подсчета поколений (можно почитать Д. Рихтера “Программирование на платформе .NET”.). Возможно Вы заметили отсутствие в коде вызов метода Dispose() для неуправляемых ресурсов какими являются битовые изображения. Привожу дословно текст из книги Д. Рихтера “Программирование на платформе .NET” стр. 409 - .”Настоятельно рекомендую в общем случае отказаться от методов Dispose и Close. Сборщик мусора из CLR достаточно хорошо написан, и пусть он делает свою работу сам.” Простые (структурные) типы не подвержены таким ошибкам, так как они не размещаются в динамической памяти (это основное отличие структурных и объектных типов). При операции присвоения создается копия в стеке, а этот механизм в данном случае гарантия неизменности исходных данных. В данной версии анимация сопровождается заметным “дрожанием” из за вызова перерисовки для ограничивающего прямоугольника Invalidate(MyRect). Если вас сильно раздражает дрожание можно воспользоваться DirectDraw, который обращается к самой видео карте и ему не указ есть в GDI+ блиттер или нет. Загрузите файл проекта (см. папку N22/BitBlitWinDDVB для VB.NET или N22/BitBlitWinDD для C#) сравните работу двух приложений - в версии DirectDraw все происходит гладко и красиво.