Первым пунктом нашего философского изложения стоит - "уникальное именование объектного поголовья". И в этой формулировке допущена одна небольшая неточность. Видите ли вы её?
А неточность в том, что, строго говоря, COM-объекты являются объектами только тогда, когда они развёрнуты в памяти. До того нет объектов, а есть статические типы. Знающие разницу могут читать дальше, не знающих просим ознакомиться.
Фактически, когда COM-объект существует в памяти, он не нуждается ни в каком имени, у него есть адрес по которому он доступен и обращение к нему такое же, как к своему внутреннему объекту. Проблема именования относится целиком к именованию статических типов. Ведь совершенно безразлично как реализован класс - как исходный текст, который требует компиляции, или как уже готовый двоичный ресурс. Существует статический тип, который описывает поведение объекта, и который должен быть отличен от всех других типов. В С++ или в любом другом языке программирования это выглядит как проблема уникальности идентификатора класса. Но такой способ не годится для именования статических типов COM.
Проблема в том, что мнемонически значимых идентификаторов в языке очень немного. А пространство имён в котором они должны быть применимы, в отличие от пространства имен проекта C++, - никак даже и не замкнуто. И работают в этом пространстве многие десятки тысяч независимых разработчиков, которые вольны давать своим поделкам произвольные имена.
При этом, если немного подумать, нам вовсе не нужно "имя" в его человеческом понимании. Нас вполне устроит и номер, лишь бы среди всего множества перенумерованных объектов эти номера были уникальными. Но "единая нумерация", очевидно, требует и "единого нумератора" - организации, которая бы выдавала несовпадающие номера. А кто будет содержать эту организацию? И сколько будет стоить получение одного уникального номера?
Выход нашла Open Software Foundation - ее программисты придумали UUID - universally unique identifier, вселенски уникальный идентификатор. Идея UUID состоит в следующем - для числа достаточной разрядности диапазон различных представимых значений значительно больше числа объектов, которые требуют перенумерования. Например, число 10100 настолько велико, что им невозможно выразить никакого известного физического понятия. Даже число атомов в видимой нам Вселенной измеряется порядком 1035, стало быть, если для такого числа определить случайнозначную хэш-функцию, которая бы более-менее равномерно выдавала значения из диапазона, то из-за огромности общего числа значений вероятность их совпадения будет невелика.
UUID и есть такое длинное двоичное 128-разрядное число и определённый к нему алгоритм вычисления хэш-функции. Алгоритм этот для большей надёжности использует ряд действительно случайных значений. Например, в подсчете используется показание внутреннего таймера машины, некоторые параметры BIOSa, уникальный идентификатор сетевой карты. Математически доказано, что вероятность того, что на достаточно большом интервале времени вычисления этой хэш-функции дадут совпадающие значения практически равна нулю. А это именно то, что и требовалось - где бы и кто бы ни вычислял UUID он обязательно получит уникальное значение среди вообще всех, могущих быть вычисленными, UUID.
Длина UUID в 128 битов не имеет особого смысла для обеспечения его уникальности и могла бы быть выбрана и чуть короче и чуть длиннее, но 128 бит = 16 байтов = 4 двойных слова, т.е. длина UUID кратна слову процессора, что старого, 16-ти разрядного, что нового 64-разрядного. Т.е. такой выбор все-таки обусловлен удобством обработки и обращения.
Компания Microsoft взяла на вооружение эту идею и этот объект без изменения, только назвала его немного по другому - GUID, т.е. globally unique identifier, глобально уникальный идентификатор.
GUID и применяются там, где в модели COM требуется уникально обозначить некое понятие. Обычно, обозначаемых ими понятий два - идентификатор класса, называемый CLSID и идентификатор интерфейса, называемый IID.
Нужно отметить - вычисление GUID всякий раз дает уникальное значение, неважно будет оно потом присвоено COM-объекту или нет. Поэтому, если в практике программиста встречается случай, когда ему нужно глобально уникально перенумеровать свои собственные объекты он с успехом может использовать GUID и для этой цели.
Для вычисления UUID/GUID платформа Win32 имеет функции API UuidCreate и CoCreateGuid, которые вычисляют значение и предоставляют его вызвавшей программе, т.е. этим сервисом может воспользоваться любая программа исполняющаяся в операционной системе.
GUID (и, соответственно, CLSID, IID и т.д) имеет несколько нотаций записи и форм представления, поскольку длинное 128-битовое число в существующих системах не может быть представлено как одна простая сущность.
Итак, внутри программы GUID канонически определяется как структура:
typedef struct _GUID
{
unsigned long Data1;
unsigned short Data2;
unsigned short Data3;
unsigned char Data4[8];
} GUID;
т.е. последовательность long-short-short-char-char-char-char-char-char-char-char.
Эта последовательность в символьной шестнадцатиричной записи (каждая шестнадцатиричная цифра заменена символом X) будет выглядеть так:
XXXXXXXX-XXXX-XXXX-XX XX XX XX XX XX XX XX
По некоторым структурным причинам последние восемь байтов делятся на два плюс шесть и последние шесть байтов GUID вычисляются как функция от идентификатора сетевой карты ethernet/tokenring установленной в машине. Поэтому в символьном виде "настоящий GUID" записывается так:
XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX,
При этом у всех GUID сгенерированных на данной машине последние шесть байтов будут одинаковыми, если на машине действительно обнаруживается сетевая карта. Примеры символьной записи GUID:
22F55881-280B-11d0-A8A9-00A0C90C2004
0c733a60-2a1c-11ce-ade5-00aa0044773d
B502D1BE-9A57-11d0-8FDE-00C04FD9189D
в записи допускаются символы и верхнего и нижнего регистров в произвольном сочетании.
Важно понимать - GUID есть простое длинное 128-битное численное значение. Его структурированность существует для удобства человеческого восприятия. Поэтому GUID в двоичном представлении можно уместить в байтовый массив длиной в 16 ячеек, либо в массив short длиной 8 ячеек и т.д. Можно представлять GUID в символьном виде как последовательность символов - шестнадцатеричных цифр, а можно представить его просто длинным десятичным числом. Но структурированная дефисами символьная форма записи шестнадцатиричными цифрами считается стандартной формой записи GUID и отступать от нее не рекомендуется. Дело в том, что Win32 реализует ряд функций API которые преобразуют GUID из одной формы представления в другую, а эти функции считают "символьной формой записи GUID" именно ту, что показана выше. Длина GUID в символьной нотации - 37 символов.
Существует и еще одна символьная форма GUID, в которой подчеркнуто единство всех групп цифр:
{B502D1BE-9A57-11d0-8FDE-00C04FD9189D},
в ней значение GUID заключено в фигурные скобки. Собственно, только эта форма записи и может считаться символьным представлением GUID, т.к. скобки и обозначают формат. Запись без фигурных скобок может быть просто интерпретирована как строковое значение. Но путаницы не возникает, поскольку случаи употребления этих нотаций различаются.
В стандартной поставке средства разработчика Visual Studio существуют две программы uuidgen.exe и guidgen.exe, которые представляют собой программную оболочку на фунцию API CoCreateGuid. Располагаются обе в каталоге "…Program Files\Microsoft Visual Studio\ Common\Tools\", первая из них управляется интерфейсом командной строки, а вторая снабжена графическим интерфейсом. Поскольку GUID генерируется системной функцией API, то и качество их работы тоже одинаково.
GUID объекту присваивает его разработчик, а не операционная система, поэтому на всех пользовательских машинах GUID объекта будет один и тот же, и GUID разных объектов разных разработчиков не пересекутся. Это позволяет, теоретически, рассматривать случай такого распределённого приложения, часть которого работает на одном континенте, а часть - на другом. И при этом это приложение «ничего не почувствует». Слово "теоретически" здесь употреблено не напрасно - хотя концептуально это возможно уже сейчас, технически это пока возможно едва ли - надежность такого распределенного приложения и его быстродействие будут пока значительно ниже среднего, но причина этого совсем не COM, а существующие технологии связи.
0 коммент.:
Отправить комментарий