к оглавлению   к 4GL - визуальному программированию

Написание Windows-приложений с использованием J/Direcl

J/Direct — это новое средство Microsoft Visual J+ + , облегчающее доступ к динамически подключаемым библиотекам (DLL) Microsoft Windows. Применение J/Direct позволяет вызывать функции из стандартных системных библиотек (KERNEL32 и USER32) и библиотек других поставщиков напрямую. J/Direct гораздо проще, чем прежние интерфейсы Raw Native Interface (RNI) и Java Native Interface (JNI). Они требуют написания специализированной библиотеки-оболочки и выполнения нетривиального преобразования типов. Благодаря J/Direct можно обращаться к большинству существующих в DLL функций, просто объявив и вызвав их. J/Direct применяет директиву @dll.import, которая похожа на оператор DECLARE из Visual Basic.

Для использования J/Direct нужно установить Microsoft Visual J++ и Microsoft Internet Explorer версии 4.0 или более новой. Для быстрой сборки приложений, учитывающих преимущества J/Direct, применяйте J/Direct Call Builder, входящий в состав среды разработки Visual J+ + . В этой главе объясняется, как использовать директиву @dll.import, чтобы из Java-кода вызвать функцию, помещенную в DLL. Здесь также подробно описано, как передаются и принимаются данные каждого типа и как применять директиву @dll.struct для обмена структурами из DLL-методов.

Пример информационного окна

В этом разделе показано приложение для информационного окна, в котором используется директива @dll.import.

Применять J/Direct для вызова библиотек Win32 DLL из Java-кода очень просто. Вот короткое Java-приложение, которое выводит информационное окно:

class ShowMsgBox {

public static void main(String args[]) {

MessageBox(0, "It worked!",

"This messagebox brought to you using J/Direct", 0); }

/** @dll.import("USER32") */

private static native int MessageBox(int hwndOwner, String text,

String title, int fuStyle); }

Директива @dll.import сообщает компилятору, что метод MessageBox свяжется с USER32.DLL посредством протокола J/Direct, а не RNI, который поддерживался в предыдущих версиях. Кроме того, Microsoft Virtual Machine (VM) for Java (Виртуальная машина (ВМ) Java от Microsoft) поддерживает автоматическое размещение типов из объектов String в строки, оканчивающиеся нулем, что требуется в С. Нет необходимости указывать, должна ли вызываться функция с кодировкой ANSI (MessageBoxA) или с кодировкой Unicode (MessageBoxW). В предыдущем примере всегда будет вызываться ANSI-версия функции. В разделе «Как ВМ выбирает между ANSI и Unicode» далее в этой главе объясняется, как использовать модификатор auto для вызова подходящей версии функции в соответствии с версией Microsoft Windows, на которой выполняется приложение.

J/Direct Call Builder

Используя J/Direct Call Builder, Вы быстро создадите вызовы функций Win32 API в стандарте J/Direct. Это средство автоматически вставляет в Ваш Java-код определения для элементов Win32 API вместе с соответствующими тэгами @dll.import.

Чтобы получить доступ к Win32 API при помощи J/Direct Call Builder:

  1. Для вызова J/Direct Call Builder войдите в подменю Other Windows меню View и выберите команду J/Direct Call Builder.
  2. По умолчанию J/Direct Call Builder отображает элементы, определенные в следующих библиотеках: advapi32.dll, gdi32.dll, kernel32.dll, shell32.dll, spoolss.dll, user32.dll и winmm.dll. (Эти библиотеки заданы в файле WIN32.TXT, выделенном в Source.)
  3. В окне Target задается класс, который будет содержать вызовы J/ Direct. По умолчанию в проект добавляется класс Win32, который и содержит эти вызовы. (Если Ваш проект содержит несколько Java-подпроектов, то класс Win32 добавляется в первый из них.) Чтобы указать другой класс назначения, щелкните кнопку с многоточием (...).
  4. Чтобы отфильтровать отображение методов, структур и констант Win32, вставьте или уберите флажки Methods, Structs и Constants соответственно.
  5. Теперь выберите метод, структуру или константу, которую хотите вставить. (Можно выбрать несколько элементов клавишами SHIFT и CTRL.) Обратите внимание, что структура из Win32 будет добавлена в качестве вложенного класса в Ваш класс.

Примечание Чтобы найти элемент по начальным символам его имени, введите эти символы в поле ввода Find. Автоматически будет выбран первый элемент, имя которого начинается с этих символов.

  1. Чтобы вставить в Ваш класс связанное Java-определение, щелкните Copy To Target. (Дважды щелкнув метод, структуру или константу, Вы также вставите их в Ваш класс.)

При выборе элемента Win32 API в J/Direct Call Builder в нижней панели предварительного просмотра выводится соответствующее Java-определение. Этот текст копируется и вставляется в любой файл. Чтобы быстро получить интерактивную справочную информацию по элементу Win32 API, щелкните его правой кнопкой мыши и в появившемся контекстном меню выберите Display API Help.

Информацию о параметрах J/Direct Call Builder, установленных по умолчанию, см. в следующем разделе «Установка параметров J/Direct Call Builder».

Установка параметров J/Direct Call Builder

Visual J++ позволяет установить два параметра J/Direct Call Builder, влияющие на его функционирование.

Чтобы установить параметры J/Direct Call Builder:

  1. В меню Tools выберите команду J/Direct Call Builder Options.
  2. В диалоговом окне J/Direct Call Builder Options пометьте или очистите флажки для следующих параметров.

Параметр

Описание

Show Target window when copy to target Disable stack crawl security check

Выбран но умолчанию. При вводе вызова J/Direct в свой класс, компоновщик автоматически открывает файл .Java в текстовом редакторе (если он еще не открыт). По умолчанию сброшен. Для каждого вызова J/Direct BM будет инициировать проверку безопасности стека вызовов. Если хотя бы один из вызовов на стеке не вполне надежен, генерируется исключение по безопасности и обращения к вызову J/Direct не происходит. Это относится главным образом к Java-коду на Web-страницах. Если этот параметр выбирается для блокировки проверки безопасности, то в Ваш класс будет добавлен тэг @security(checkDHCalls = off) при следующей вставке вызова J/Direct. (Если Вы в дальнейшем очистите этот параметр, то имеющиеся тэги ©security останутся в файлах, но новые не добавятся.)

  1. Щелкните ОК, чтобы сохранить выбранные параметры.

Краткий обзор синтаксиса

В этом разделе представлен краткий обзор директив @dll.import, @dll.struct и @dll.structmap. Для каждой директивы показан и пояснен требуемый синтаксис.

Синтаксис @dll.import

Директива @dll.import должна размещаться непосредственно над объявлением метода. Синтаксис директивы таков:

/**@dll.import("LibName",<Modifier>)*/

...объявление метода...;

LibName — имя DLL, в которой содержится вызываемая функция. Modifier необязателен, а возможные значения зависят от Ваших потребностей. При объявлении метода используется то же имя функции, что и в DLL, либо методу присваивается иное имя как псевдоним. Подробнее о псевдонимах см. в разделе «Псевдонимы, (переименование методов)» далее в этой главе. Типы данных Java, выбранные Вами для параметров метода и его возвращаемого значения, должны соответствовать типам данных для параметров и возвращаемого значения функции в DLL. Дополнительную информацию о соответствии типов данных Java и системы см. в разделе «Как преобразуются типы данных» далее в этой главе.

В таблице показан синтаксис (a dll.import для нескольких ситуаций, обсуждаемых в этой главе.

Ситуация

Требуемый синтаксис

Разъяснение

Вызов Win32 DLL

/**@dll. import ("Libname")*/


Вызов OLE API '

/**@dll. import ("Libname", ole)*/


Псевдоним

/**@dll. import ("Libname", entrypoint="DLLFunction

Name")*/

В объявлении метода используйте выбранное имя Java.

Связывание по порядковому номеру

/**@dll. import("Libname", en trypoint=" #ordinal")*/

«ordinal» — это 16-разрядное целое число, заданное в десятичном виде, которое определяет импортируемую из DLL функцию.

 

Синтаксис ©dll.struct

Директива @dll.struct должна размещаться непосредственно над объявлением класса. Синтаксис ее таков:

/**@dll.struct(<LinkTypeModifier>,<pack=n>)*/ ...объявление класса...;

LinkTypeModifier сообщает компилятору, представлены типы String и char символами ANSI или Unicode. Его значениями могут быть ansi, Unicode или auto. Если LinkTypeModifier не задан, то по умолчанию используется ansi. Чтобы лучше понять, какие значения следует применять для LinkTypeModifier, см. раздел «Как ВМ делает выбор между ANSI и Unicode» далее в этой главе.

Можно также задать параметр pack=n, сообщающий компилятору размер выравнивания структуры на 1, 2, 4 или 8 в зависимости от значения п. При отсутствии модификатораpack=n размер выравнивания равен 8 по умолчанию. Подробнее об установке размера выравнивания см. в разделе «Выравнивание структур» далее в этой главе. В объявлении класса необходимо использовать поля, типы которых соответствуют типам полей в исходной структуре, и в том порядке, как они расположены в исходной структуре. Подробнее о выборе типов данных для полей см. в разделе «Соответствие между типами внутри структур» далее в этой главе.

В таблице описан синтаксис для ситуаций, в которых используется директива @dll. struct.

Ситуация

Требуемый синтаксис Разъяснение

Объявление

/**@dll.struct()*/ Если не задан

структуры

LinkTypeModifier, то предполагается, что поля типа char или String представлены символами ANSI.

Установка размера выравнивания структуры

/**@dll.struct(pack=n)*/ n может быть 1, 2, 4 или 8.

Объявление структуры, имеющей поле типа char

/**@<Ш. struct(ansi)*/ Поле представлено символами ANSI.

Объявление структуры, имеющей поле типа String, и установка размера выравнивания

/**@dll. struct(unicode, Поля типа String pack=n)*/ представлены в формате Unicode, а размер выравнивания равен 1, 2, 4 или 8 — в зависимости от значения п.

 

Синтаксис @dll.structmap

Директива @dll.structmap используется для объявления строк и массивов фиксированной длины, входящих в состав структур. Директива должна быть помещена внутри структуры, объявленной с @dll.struct, и размещаться непосредственно над объявлением поля строки или массива фиксированной длины. Подробнее об использовании @dll.structmap см. в разделах «Строки фиксированной длины внутри структур» и «Скалярные массивы фиксированной длины внутри структур» далее в этой главе. В следующей таблице описан синтаксис директивы @dll.structmap.

Ситуация

Требуемый синтаксис

Разъяснение

Структура, содержащая строку фиксиро ванной длины /**@dll. struct map ([type =TCHAR[size]])*/ Size — десятичное целое, обозначающее число символов в строке вместе с завершающим нулем.

Структура, содержащая массив фиксированной длины

/**@dll. structmap([type =FIXEDARRAY,size= n])*/

п — десятичное целое, которое обозначает размер массива.

 

Как преобразуются типы данных

Если объяснять просто, то Microsoft VM проверяет аргументы на Java и затем преобразует их в типы данных, присущие языку C++. Microsoft VM определяет принадлежность типа каждого параметра и возвращаемого значения на основе объявленного (во время компиляции) типа Java-данных. Например, параметр, объявленный в Java как целое, передается как 32-разрядное целое, параметр, объявленный в Java как объект String, передается как строка, оканчивающаяся нулем, и т. д. Нет никаких невидимых атрибутов, предоставляющих информацию о «родных» типах данных. В Java реализуется принцип полного соответствия: что видишь, то и получишь.

Краткий обзор

В следующих двух таблицах перечислены «родные» (собственные) типы данных, соответствующие каждому типу Java-данных. В первой описаны соответствия для параметров и возвращаемых значений, а во второй — для директивы @dll.struct.

Соответствия параметров и возвращаемых значений

Тип Java

«Родной» тип

Примечания/Ограничения

byte

BYTE или CHAR


short

SHORT или WORD


int

INT, UINT, LONG, ULONG или DWORD


char

TCHAR


long

_int64


float

float


double

double


Boolean

BOOL


String

LPCTSTR

Недопустимо в качестве возвращаемого значения, за исключением режима ole. В режиме ole String отображается на LPWSTR. Microsoft VM очищает память под строку, используя CoTaskMemFree.

StringBuffer

LPTSTR

Недопустимо в качестве возвращаемого значения. Установите вместимостьStringBuffer достаточной для хранения строки максимального размера, которую может сгенерировать функция из DLL.

byte[]

BYTE*


shortf]

WORD*


char[]

TCHAR*


int[]

DWORD*


float[]

float*


doublef]

double*


long[]

__int64*


Boolean[]

BOOL[]


Объект

Указатель на структуру

В режиме ole передается IUnknown*.

Интерфейс

Интерфейс COM

Для генерации файла интерфейса используйте jactivex или подобное ему средство.

com.ms.com. SafeArray

SAFEARRAY*

Недопустимо в качестве возвращаемого значения.

com.ms.com. Guid

GUID, IID, CLSID


com.ms.com.Variant

VARIANT*


Классы @dll. struct

Указатель на структуру


Классы

Указатель на структуру


@com. struct



void com.ms.dll. Callback

VOID

Указатель на функцию

Только как возвращаемое значение. Только как параметр.

Соответствия, используемые с директивой @dll.struct

Тип Java

«Родной» тип

byte

BYTE или CHAR

char

TCHAR

short

SHORT или WORD

int

INT, UINT, LONG, ULONG или DWORD

float

__int64

long

float

double

double

Boolean

ВООL[]

String

Указатель на строку или встроенная строка фиксированного размера

Класс с

Вложенная структура

директивой


@dll.struct


charf]

Вложенный массив TCHAR

byte[]

Вложенный массив BYTE

short[]

Вложенный массив SHORT

int[]

Вложенный массив LONG

loagfl

Вложенный массив int64

float[]

Вложенный массив float

double[]

Вложенный массив double

 

Базовые скалярные типы

Базовые скалярные типы отображаются следующим образом.

Тип Java Тип в DLL
int 32-разрядное целое со знаком
byte 8-разрядное целое со знаком
short 16-разрядное целое со знаком
long 64-разрядное целое со знаком
float 32-разрядное число с плавающей точкой
double 64-разрядное число с плавающей точкой

Обратите внимание: в Java нет прямого соответствия для целых типов без знака. Поскольку в Java нет беззнаковых типов, то используйте тип, имеющий тот же размер, что и нужный Вам целый тип. Например, тип int в Java можно использовать для представления распространенного типа DWORD (32-разрядное целое без знака).

Символы

Тип char в Java превращается в CHAR (8-разрядный ANSI-символ) за исключением случаев использования в @dll.struct модификаторов unicode или ole, когда он превращается в WCHAR (16-разрядный Unicode-символ).

Булев тип


Тип Boolean в Java соответствует 32-разрядному типу BOOL в Win32. В качестве параметра true отображается в 1, a false — в 0. В качестве возвращаемого значения любая ненулевая величина отображается в true. Обратите внимание, что BOOL и VARIANT_BOOL (внутренний Булев тип в Microsoft Visual Basic) не являются взаимозаменяемыми. Для передачи типа VARIANT_BOOL в DLL на Visual Basic Вы должны использовать тип short из Java и значения -1 для VARIANT_TRUE и О для VARIANT_FALSE.


Строки


Здесь рассказано, как передавать строки в форматах ANSI и Unicode в функцию, находящуюся в DLL, а также обсуждаются два способа возврата строк из таких функций.
Передача строки в функцию
Для передачи в функцию стандартной оканчивающейся нулем строки просто передайте строку типа String.
Например, для изменения текущего каталога можно использовать функцию CopyFile из КегпеШ:
class ShowCopyFile {
public static void main(String args[])
{
CopyFile("old.txt", "new.txt", true);
}
/** @dll.import("KERNEL32") */
private native static boolean CopyFile(String existingFile,
String newFile, boolean (); }

В Java строки доступны только для чтения, поэтому Microsoft VM конвертирует строку только как входной параметр. Чтобы виртуальная машина могла преобразовать строки без копирования символов, параметры типа. String не должны использоваться с функциями, которые могут изменить строку. Если функция способна изменить строку, передайте объект типа StringBuffer.
Строки преобразуются в формат ANSI за исключением случаев применения модификаторов Unicode или ole, когда строки передаются в формате Unicode.
Строки не могут быть указаны в качестве возвращаемых значений, за исключением режима ole, когда в качестве возвращаемого типа предполагается строка типа LPWSTR, память под которую выделена функцией CoTaskMemAlloc.

Получение строки из функции DLL

Есть два распространенных способа получения строки из функции: вызывающий выделяет буфер, который заполняется функцией, либо функция выделяет память под строку и возвращает строку вызывающему. Большинство функций Win32 используют первый способ, а функции OLE — второй. (В разделе «Обращение к функциям OLE API» далее в этой главе см. подробнее о специальной поддержке, которую J/Direct предоставляет для вызова функций OLE.) Одна из функций, использующих первый способ, — GetTempPath из Кегпе132, она имеет следующий прототип;

DWORD GetTempPath(DWORD sizeofbuffer, LPTSTR buffer); Эта функция возвращает путь к системному каталогу временных файлов (например, «c:\tmp\»). Аргумент buffer указывает на буфер, выделенный вызывающим, — в него будет помещен путь, a sizeofbuffer указывает количество символов, которое можно поместить в буфер. (Что не равно числу байт в случае Unicode-версии функции.) В Java строки доступны только для чтения, поэтому нельзя передать в качестве буфера объект String. Нужно использовать класс StringBuffer для создания объекта, в который можно записать строку. Вот пример обращения к функции

GetTempPath: class

ShowGetTempPath

{

static final int MAX_PATH = 260; public static void main(String args[])

{

StringBuffer temppath = new StringBuffer(MAX_PATH);

GetTempPath(temppath.capacity(), temppath);

System.out.println("Temppath = " + temppath); }

/** @dll.import("KERNEL32") */

private static native int GetTempPath(int sizeofbuffer,

StringBuffer buffer); }

Для понимания этого примера важно различать длину StringBuffer и его вместимость. Длина — это количество символов в строке, хранящейся в StringBuffer. Вместимость — объем памяти, выделенный для StringBuffer. После выполнения оператора: StringBuffer sb = new StringBuffer(259); длина (sb.length) будет равна 0, а вместимость (sb.capacity) — 259. При вызове функции из DLL с передачей в нее StringBuffer виртуальная машина проверяет вместимость StringBuffer, добавляет единицу для завершающего нуля, умножает на 2, если по умолчанию используется

Unicode, и выделяет полученное количество байт в памяти для буфера, передаваемого в функцию. Иными словами, для установки размера буфера используется не длина, а вместимость. Будьте внимательны и не сделайте следующую ошибку:

StringBuffer sb = new StringBuffer();

//Неверно!

GetTempPath(MAX_PATH, sb);

Вызов StringBuffer-конструктора без аргументов создает объект String-Buffer со значением capacity, равным 16, что, видимо, слишком мало. В функцию GetTempPath передается МАХ_РАТН, сообщая, что в буфере достаточно места для хранения 260 символов. Следовательно, GetTempPath, скорее всего, выйдет за пределы буфера. Если планируется интенсивно использовать GetTempPath, сделайте оболочку на Java следующим образом:

public static String GetTempPath() {

StringBuffer temppath = new StringBuffer(MAX_PATH-1);

int res = GetTempPath(MAX_PATH, temppath);

if (res == 0 || res > MAX_PATH) {

throw new RuntimeException("GetTempPath error!");

}

return temppath.toString(); // нельзя вернуть StringBuffer }

В этом методе сочетается удобство и безопасность, а также производится перевод ошибочного возвращаемого значения функции в Java-исключение. Обратите внимание, что нельзя возвращать объекты StringBuffer.

Массивы

J/Direct автоматически обрабатывает массивы данных скалярных типов. Вот таблица трансляции типов массивов Java в «родные» типы указателей.

Тип Java

«Родной» тип

Количество байт на элемент

byte[]

BYTE*

1

shortf]

SHORT*

2

int[]

DWORD*

4

floatt]

FLOAT*

4

double[]

DOUBLE*

8

long[]

__int64*

8

Booleanf]

BOOL*

4

Тип массива charf] отображается в CHAR*, за исключением случая использования модификатора Unicode, когда он отображается в WCHAR*. Все параметры типа скалярный массив могут быть изменены (такие, как параметры [in, out]).

Массивы не могут быть возвращаемыми значениями. Не бывает массивов объектов или массивов строк.

Обычно указатели используются функциями OLE для возврата значений. (Функции OLE используют возвращаемое функцией значение для возврата кода ошибки типа HRESULT.) В разделе «Обращение к функциям OLE API» далее в этой главе см. о получении возвращаемого значения функции OLE.

Структуры

В языке Java отсутствует непосредственная поддержка концепции структур. Хотя классы Java содержат поля и используются для эмуляции концепции структур внутри языка Java, обычные Java-объекты не могут применяться для эмуляции структур в вызовах функций из DLL. Это вызвано тем, что язык Java не гарантирует выравнивания полей, а также что сборщик мусора может свободно перемещать объект по памяти. Следовательно, чтобы обмениваться структурами с функциями в DLL, нужно использовать директиву компилятора @dll.struct. Использование ее с определением класса приводит к тому, что все экземпляры этого класса попадают в блок памяти, который не будет перемещаться во время сборки мусора. Кроме того, выравнивание полей контролируется и при помощи модификатора pack (см. раздел «Выравнивание структур» далее в этой главе). Например, структура SYSTEMTIME из Win32 описывается на языке С следующим образом:

typedef struct {

WORD wYear;

WORD wMonth;

WORD wDayOfWeek;

WORD wDay;

WORD wHour;

WORD wMinute;

WORD wSecond;

WORD wMilliseconds; } SYSTEMTIME;

Корректное объявление этой структуры на языке Java выглядит так:

/** @dll.struct() */ class SYSTEMTIME {

public short wYear;

public short wMonth;

public short wDayOfWeek;

public short wDay;

public short wHour;

public short wMinute;

public short wSecond;

public short wMilliseconds; }

Вот пример использования структуры SYSTEMTIME при вызове функции из DLL:

class ShowStruct {

/** tdll.import("KERNEL32") */

static native void GetSystemTime(SYSTEMTIME pst);

public static void main(String args[])

{

SYSTEMTIME systemtime = new SYSTEMTIMEO;

GetSystemTime(systemtime);

System.out.println("Year is " + systemtime.wYear);

System.out.println("Month is " + systemtime.wMonth);

// и т.д. } >

Примечание Классы, объявленные с директивой @dll.struct, рассматриваются как незащищенные и, следовательно, не могут использоваться ненадежными (untrusted) апплетами.

Соответствие между типами внутри структур

В таблице показано соответствие между скалярными типами внутри структур.

Тип Java

«Родной» тип

byte char

short

BYTE TCHAR (CHAR или WCHAR в зависимости от определения @dll. struct) SHORT

int

LONG

long float

_int64 float

double double

Boolean

BOOL (32-разрядный Булев)

Ссылочные типы (объекты и классы Java) обычно отображаются на встроенные структуры и массивы. В следующей таблице описано каждое поддерживаемое отображение.

Тип Java

«Родной» тип

String

Класс с директивой @dll.struct char[]

Указатель на строку или встроенная строка фиксированного размера Вложенная структура

Вложенный массив TCHAR (CHAR/WCHAR)

 

Тип Java

«Родной» тип

byte[]

Вложенный массив BYTE

short[]

Вложенный массив SHORT

int[]

Вложенный массив LONG

long[]

Вложенный массив __ int64

float[]

Вложенный массив float

double[]

Вложенный массив double

Указатели внутри структур не поддерживаются напрямую из-за огромного числа способов создания и удаления объекта, на который ссылается указатель. Для представления структур со встроенными указателями объявите поле указателя как тип int. Вам придется сделать явные вызовы функций выделения памяти из DLL и самостоятельно инициализировать блоки памяти. (Для отображения блоков на классы, объявленные с директивой @dll.struct, можно использовать DllLib.ptrToStruct.)

Вложенные структуры

Структура способна вмещать другую структуру благодаря простому объявлению последней в качестве типа поля. Например, MSG-структу-ра в Windows вмещает структуру POINT следующим образом:

typedef struct {

LONG x;

LONG у; } POINT;

typedef struct {

int hwnd;

int message;

int wParam;

int IParam;

int time;

POINT pt; } MSG; Это напрямую переводится на язык Java:

/** @dll.struct() */ class POINT {

int x;

int y; }

/** @dll.struct() */ class MSG {

public int hwnd;

public int message;

public int wParam;

public int IParam;

public int time;

public POINT pt; }

Совет Хотя вложение структур удобно, но факт остается фактом — Java поддерживает не вложение объектов, а лишь ссылки на объекты. Microsoft VM вынуждена переводить данные между этими двумя форматами при каждой передаче вложенной структуры. Следовательно, в особо важном коде можно улучшить производительность путем вложения структур вручную (копируя поля вложенной структуры в контейнерную структуру). Например, поле pt в структуре MSG можно объявить как два целых поля pt_x и pt_y.

Строки фиксированного размера, встроенные в структуры

В некоторые структуры встроены строки фиксированного размера. Например, структура LOGFONT определена следующим образом:

typedef struct {

LONG IfHeight; LONG IfWidth;

/* <другие поля для краткости не показаны> */ TCHAR lfFaceName[32]; } LOGFONT;

Она может быть объявлена на Java с использованием расширенного синтаксиса, задающего размер:

/** edll.struct() */ class LOGFONT {

int IfHeight;

int IfWidth;

/* <другие поля для краткости не показаны> */

/** <s>dll.structmap([type=TCHAR[32]]) */

String IfFaceName; }

Директива @dll.structmap описывает размер фиксированной строки в символах (включая место для нуля в конце строки). Подробнее см. раздел «Скалярные массивь( фиксированного размера, встроенные в структуры».

Скалярные массивы фиксированного размера, встроенные в структуры

Скалярные массивы фиксированного размера, встроенные в структуры, можно задать с использованием директивы @dll.structmap. Вот объявление на языке С структуры, содержащей скалярные массивы фиксированного размера:

struct EmbeddedArrays

{

BYTE b[4];

CHAR c[4];

SHORT s[4];

INT i[4];

__int64 1[4];

float f[4];

double d[4]; };

Ha Java эту структуру можно описать с использованием директивы @dll.structmap:

/** edll.struct() */ class EmbeddedArrays

{

/** @dll.structmap([type=FIXEDARRAY, size=4]) */

byte b[];

/** @dll.structmap([type=FIXEDARRAY, slze=4]) */ char c[];

/** @dll.structmap([type=FIXEDARRAY, size=4]) */ short s[];

/** @dll.structmap([type=FIXEDARRAY, size=4]) */ int i[];

/** @dll.structmap([type=FIXEDARRAY, size=4]) */ long l[];

/** @dll\structmap([type=FIXEDARRAY, size=4]) */ float f[];

/** @dll.structmap([type=FIXEDARRAY, size=4]) */ double d[];

}

Выравнивание структур

Поля структур выравниваются в соответствии с проектом (draft) ANSI С 3.5.2.1. Размер выравнивания может быть задан при помощи модификатора pack:

/** @dll.struct(pack=n) */

где п равен 1, 2, 4 или 8. По умолчанию используется 8. Для тех, кто знаком с компиляторами Microsoft Visual C++: «pack=n» эквивалентно «#pragma раск(л)».

Различие между @dll.struct и @com.struct

Директива @dll.struct очень похожа на директиву @com.struct, вставляемую посредством jactivex и неявно при помощи javatlb (javatlb, документированное в предыдущих версиях Microsoft Visual J++, заменено на jactivex). Главное различие между директивами в том, что для первой отображение типов по умолчанию соответствует вызову функций Microsoft Windows, а для второй — вызову объекта СОМ. Из этого следует, что можно сгенерировать классы @dll.struct, описав структуры в библиотеке типов и применив jactivex для генерации класса Java. Обычно, однако, быстрее создать класс вручную.

Указатели

В Java не поддерживается тип указателя данных. Однако вместо передачи указателя допустимо передавать одноэлементный массив. Можно хранить указатели в целых числах Java, а также читать и писать данные из нетипизированных (raw) указателей.

Возврат значений указателей

Функции Win32, которые возвращают несколько значений одновременно, как правило, используют для этого указатели на переменные, обновляемые внутри функции. Например, функция GetDiskFreeSpace имеет следующий прототип:

BOOL GetDiskFreeSpace(LPCTSTR szRootPathName,

DWORD *IpSectorsPerCluster,

DWORD *IpBytesPerCluster,

DWORD *IpFreeClusters,

DWORD *lpClusters); Вызывается эта функция обычно так:

DWORD sectorsPerCluster, bytesPerCluster, freeClusters, clusters; GetDiskFreeSpace(rootname, &secto rsPe rCluste r,

&bytesPerCluster, &freeClusters, &clusters);

В Java это просто особый случай передачи скалярного массива, когда размерность массива равна одному элементу. Вот пример вызова функции GetDiskFreeSpace:

class ShowGetDiskFreeSpaoe {

public static void main(String args[]) {

int sectorsPerCluster[] = {0}; int bytesPerCluster[] = {0};

int freeClusters[] = {0};

int clusters[] = {0};

GetDiskFreeSpace("c:\\", sectorsPerCluster, bytesPerCluster,

freeClusters, clusters);

System.out.println("sectors/cluster = " + sectorsPerCluster[0]);

System.out.priptln("bytes/cluster = " + bytesPerCluster[0]);

System.out.println("free clusters = " + freeClusters[0]);

System.out.println("clusters = " + clusters[0]); }

/** @dll.import("KERNEL32") */

private native static boolean GetDiskFreeSpace(String rootname, int pSectorsPerCluster[], int pBytesPerCluster[], int pFreeClusters[], int pClusters[]); }

Нетипизированные указатели

Указатель на неизвестную или очень сложную структуру может храниться в Java как простое целое. Если приложению нужно только сохранить указатель и не нужно переименовывать (dereference) его, то это самый простой и наиболее эффективный подход. Он используется для хранения указателя, который возвращен DLL-функцией выделения памяти. Нечего и говорить, что применение нетипизированных указателей исключает многие преимущества Java в области безопасности. Везде, где можно, используйте другие способы. Однако бывают ситуации, когда приходится применять нетипизированные указатели. Есть два варианта чтения/записи данных с использованием этих указателей.

Приведение к ссылке на класс, объявленный с директивой @dll.struct

Один из способов чтения/записи данных через нетипизированный указатель состоит в приведении этого указателя к ссылке на класс, объявленный с директивой @dll.struct. После этого можно читать/записывать данные, используя нормальный синтаксис обращения к полям объекта. Предположим, к примеру, что Вы хотите обращаться к нетипизированному указателю как к структуре RECT. Для этого используется системный метод DllLib.ptrToStruct следующим образом:

/** @dll.struct() */ class RECT { int left; int top; int right; int bottom; }

import com.ms.dll.*;

int rawptr = ...;

RECT reet = (RECT)DllLib.ptrToStruct(RECT.class, rawptr);

rect.left = 0;

rect.top = 0;

rect.right = 10;

rect.bottom = 10;

Метод ptrToStruct преобразует нетипизированный указатель в ссылку на экземпляр RECT. В отличие от экземпляров, созданных оператором new, этот RECT-экземпляр не будет пытаться освободить нетипизированный указатель по требованию сборщика мусора, так как объект RECT не может узнать, каким образом получен указатель:, Кроме того, поскольку системная память уже выделена к моменту вызова ptrToStruct, конструктор класса RECT не вызывается.

Использование методов копирования из DIILib

Другой способ чтения/записи данных посредством нетипизированного указателя состоит в использовании замещаемых (overloaded) методов копирования из DIILib. Они копируют данные между массивами разных типов и нетипизированными указателями. Если нужно трактовать нетипизированный указатель как указатель на строку (LPTSTR), то можно использовать один из методов ptrToStringAnsi, ptrToStringUni или ptrToString из DIILib для разбора строки и преобразования ее в объект Java. lang. String:

import com.ms.dll.*;

int rawptr =

String s = DIILib.ptrToStringAnsi (rawptr);

Внимание Все Java-объекты могут быть передвинуты в памяти по решению сборщика мусора. Следовательно, Вы не должны пытаться получить указатель на массив Java путем вызова функции из DLL, которая выполняет простое приведение типов. Вот пример неправильного способа получения указателя:

// Не делайте этого!

/** edll.import("MYDLL") */

private native static int Cast(int javaarray[ ]);

// Внутри MYDLL.DLL LPVOID Cast(LPVOID ptr) {

// He делайте этого!

return ptr; // входит как массив Java; выходит как тип int Java }

Неизменность значения ptr гарантируется только на время вызова функции Cast. Это обусловлено тем, что виртуальные машины передают массивы в функции путем копирования, и тем, что сборка мусора может привести к изменению физического положения массива после возврата из функции Cast.

Полиморфные параметры

В некоторых функциях Win32 есть параметры, чей тип зависит от значения другого параметра. Например, функция WinHelp объявлена следующим образом:

BOOL WinHelp(int hwnd, LPCTSTR szHelpFile, UINT cmd, DWORD.dwData);

В зависимости от значения параметра cmd внешне невинный параметр dwData может быть указателем на строку, указателем на структуру MULTIKEYHELP, указателем на HELPWININFO или просто целым. J/Direct предлагает два способа для объявления такого параметра:

Их сравнение — в разделе «Сравнение двух способов» далее в этой главе.

Объявление параметра типа Object

Вот пример объявления WinHelp с параметром dwData типа Object:

/** edll.import("USER32") */

static native boolean WinHelp(int hwnd, String szHelpFile,

int cmd, Object dwData);

При вызове WinHelp J/Direct использует тип времени выполнения для определения того, во что преобразовать dwData. В таблице описано это преобразование таких типов.

Тип

Преобразуется в

Java.lang.Integer

4-байтное целое

Java. lang. Boolean

4-байтное BOOL

Java. lang. Char

CHAR (или WCHAR, если действуют модификаторы Unicode или ole)

Java.lang.Short

2-байтное SHORT

Java. lang. Float

4-байтное FLOAT

Java. lang. String

LPCSTR (или LPCWSTR, если действуют модификаторы Unicode или ole)

java.lang.String

LPSTR (или LPWSTR, если действуют

Buffer

модификаторы Unicode или ole)

byte[]

BYTE*

char[]

CHAR* (или WCHAR*, если действуют модификаторы Unicode или ole)

short[]

SHORT*

int[]

INT*

longf]

_int64*

float[]

float*

doublef]

double*

@dll.struct

Указатель на структуру

 

Замещение функции

Другой способ объявления функции WinHelp состоит в замещении функции для каждого возможного типа данных:

/** @dll.import("USER32") */

static native boolean WinHelp(int hwnd, String szHelpFile,

int cmd, int dwData);

/** @dll.import("USER32") */

static native boolean WinHelp(int hwnd, String szHelpFile,

int cmd, String dwData); /** tdll.import("USER32") */

static native boolean WinHelp(int hwnd, String szHelpFile,

int cmd, MULTIKEYHELP dwData);

/** @dll.import("USER32") */

static native boolean WinHelp(int hwnd, String szHelpFile,

int cmd, HELPWININFO dwData);

При помощи замещения нельзя обработать полиморфные возвращаемые значения потому, что методы в Java не могут отличаться только возвращаемым значением. Следовательно, нужно дать каждому варианту функции отдельное имя и использовать модификатор entrypoint для привязки всех вариантов к одной функции в DLL. Подробнее о переименовании методов из DLL см. в разделе «Псевдонимы (переименование методов)» далее в этой главе.

Сравнение двух способов

В большинстве случаев замещение предпочтительнее потому, что обеспечивает как более высокую производительность во время выполнения, так и лучшую проверку типов. Кроме того, замещение делает ненужным сохранение целых аргументов внутри объекта Integer. Однако объявление параметра как тип Object стоит применять в случаях, когда в наличии более одного полиморфного параметра. Этот способ также годится, когда нужен доступ к сервису, работающему с широким спектром различных типов, например функция, которая может получать в качестве параметра любой объект, объявленный с директивой @dll. struct.

Функции обратного вызова

Некоторые функции Win32 API требуют предоставить в качестве параметра адрес функции обратного вызова (callback). Эти функции можно написать на Java, расширив системный класс com.ms.dll.Callback.

Объявление метода, получающего функцию обратного вызова

Чтобы представить на Java параметр, являющийся функцией обратного вызова, объявите его как тип com.ms.dll.Callback или как. производный от него класс. Например, функция EnumWindows в Win32 имеет следующий прототип:

BOOL EnumWinduws(WNDENUMPROC wndenumproc, LPARAM Iparam);

Соответствующий прототип на Java выглядит так:

import com.ms.dll.Callback;

/** @dll.import("USER32") */

static native boolean EnumWindows(Callback wndenumproc, int Iparam);

Вызов функции, получающей функцию обратного вызова

Для вызова функции, получающей в качестве параметра функцию обратного вызова, нужно определить класс, являющийся расширением класса Callback. Производный класс должен иметь нестатический (non-static) метод с именем callback (все символы строчные). На языке С определение WNDENUMPROC выглядит так:

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM Iparam);

Для перевода на Java нужно объявить класс, который расширяет Callback следующим образом:

class EnumWindowsProc extends Callback {

public boolean callback(int hwnd, int Iparam) {

StringBliffer text = new StringBuffer(50);

GetWindowText(hwnd, text, text.capacity()+1);

if (text.length() i= 0) {

System.out.println("hwnd = " + Integer.toHexString(hwnd) +

"h: Text = " + text); }

return true; // возвращает TRUE для продолжения перебора. }

/** @dll.import("USER32") */

private static native int GetWindowText(int hwnd, StringBuffer text, int cch);

}

Вызов EnumWindows делается так: boolean result = EnumWindows(new EnumWindowsProc0, 0);

Ограничения на типы, используемые с методом callback

Тип возвращаемого значения метода callback должен быть void, int. boolean, char или short. Единственный тип параметра, допустимый в настоящее время, — int. К счастью, это не так страшно, как кажется. Можно использовать методы ptrToStringAasi, ptrToStringUni и ptrTo-String из DllLib для преобразования параметра в LPTSTR. Допустимо применять метод ptrToStruct для преобразования параметра к указателю на класс, объявленный с директивой @dll.struct.

Передача данных с функцией обратного вызова

Часто требуется передать некоторые данные из вызывающей функции в функцию обратного вызова. Этим объясняется наличие в Enum Windows дополнительного параметра Iparam. Большинство функций Win32, работающих с функциями обратного вызова, имеют также один дополнительный 32-разрядный параметр, который без изменения передается в функцию обратного вызова. При использовании механизма на основе класса Callback нет необходимости передавать данные через аргумент Iparam. Поскольку метод callback является нестатическим, данные можно хранить как поля объекта EnumWindowsProc.

Время жизни объекта Callback

Внимательно следите за тем, чтобы объект, представляющий функцию обратного вызова, не был уничтожен сборщиком мусора до того, как системная функция закончит свою работу с ним. Если время жизни объекта мало (он вызывается только во время выполнения какой-то функции), особых действий не требуется, ибо есть уверенность в том. что объект, передаваемый в функцию из DLL, не будет уничтожен сборщиком мусора во время выполнения функции.

Если время жизни объекта велико (он используется в нескольких функциях), необходимо защитить его от уничтожения, например сохранив ссылку на него в структуре Java-данных. Ссылки сохраняются также в «родной» структуре данных, если применить класс com.ms.dll.Root для сохранения объекта внутри корневого описателя (root handle) — 32-разрядного описателя, который предотвращает уничтожение объекта до тех пор, пока явно не будет освобожден сам описатель. Например, корневой описатель для WndProc может быть сохранен в области данных приложения структуры HWND и затем явно освобожден при обработке сообщения WM_NCDESTROY.

Ссылка на функцию обратного вызова из структуры

Чтобы сослаться на функцию обратного вызова из структуры, сначала вызовите метод com.ms.dll.Root.alloc для сохранения функции внутри корневого описателя. Затем передайте корневой описатель в метод DllLib.addrOf для получения реального адреса функции обратного вызова. Затем сохраните адрес в структуре как целое. Например, структура WNDCLASS может быть объявлена на языке Java следующим образом:

/** edll.struct() */

class WNDCLASS { int style;

int IpfnWndProc; // функция обратного вызова .../* <остальные поля для краткости не показаны> */ }

Предположим, что класс Callback расширен так: class WNDPROC extends Callback {

public int callback(int hwnd, int msg, int wparam, int lparam) {

} }

Для сохранения указателя на функцию обратного вызова в классе WNDCLASS используйте следующую последовательность операторов:

import com.ms.dll. *;

WNDCLASS we = new WNDCLASS();

int callbackroot = Root.alloc(new WNDPROC());

we.IpfnWndProc = DllLib.addrOf(callbackroot);

Вызов функций OLE API

В следующих, разделах подробнее описаны функции OLE API.

Синтаксис режима OLE

Директива @dll.import имеет особый режим для импорта функций OLE API. Чтобы воспользоваться этим режимом, просто включите в директиву модификатор ole, как показано в следующем примере:

/** @dll.import("OLE32", ole) */

public class OLE32 {

}

Сравнение функций Win32 и OLE

Теоретически, функции, экспортируемые из OLE32.DLL и OLE-AUT32.DLL, ничем не отличаются от функций из других DLL. Но в действительности функции OLE имеют свой стиль вызова. Функции OLE отличаются от функций Win32 тем, что:

Сравнение кода Win32 и кода OLE

Код для вызова простой функции Add в стиле" Win32 выглядит примерно так:

Int sum;

sum = Add(10, 20);

В стиле OLE функция Add должна вызываться так:

HRESULT hr;

int sum;

hr = Add(10, 20, &sum);

if (FAILED(hr)) {

...обработка ошибки.. }

Вызов функций OLE

Достоинство режима ole — использование стиля кодирования, поддерживающего способ вызова функций OLE, привычный для Java-программистов. Обращение к функции Add в стиле OLE из Java весьма похоже на обращение к традиционным функциям Win32:

/** dll.import("OLELIKEMATHDLL", ole) */

private native static int Add(int x, int y);

int sum = Add(10, 20);

// Если мы оказались здесь, то вызов Add прошел успешно.

Как работает режим OLE

Из-за наличия модификатора ole Microsoft VM автоматически считает, что исходная функция Add возвращает HRESULT. ВМ замечает также, что функция Add возвращает целое. При вызове Add BM автоматически создает временную переменную типа int и вставляет указатель на нее в качестве третьего параметра. После возврата из .исходной функции Add BM автоматически проверяет HRESULT, и если его значение говорит об ошибке (старший бит установлен), то генерируется исключение com.ms.com.ComFailException. Если значение HRESULT не соответствует ошибке, то ВМ берет настоящее возвращаемое значение из временной переменной и возвращает его.

При такой интеграции Java и СОМ возврат значения S_FALSE не вызывает генерации ComSuecessException. Если нужно отличить один успешный результат от другого, используйте нормальный режим вызова функций из DLL и рассматривайте HRESULT как целое. Говоря вкратце, режим ole изменяет семантику вызова из DLL следующим образом.

  1. Предполагается, что все строки и символы представлены в формате Unicode.
  2. Предполагается, что возвращаемое значение исходной функции всегда имеет тип HRESULT. Если возвращенное значение означает ошибку, то Microsoft VM генерирует ComFailException.
  3. Если возвращаемое значение метода на Java не относится к типу void, то Microsoft VM предполагает, что исходная функция возвращает дополнительный результат через указатель, являющийся ее последним аргументом. ВМ предоставляет для функции этот аргумент-указатель и переименовывает его после вызова для получения дополнительного возвращаемого значения. Последнее служит возвращаемым значением метода на Java.

Передача и получение строк из функций OLE

Объявление параметра как тип String на функции режима ole приводит к передаче LPCOLESTR. Кроме того, Microsoft VM включает в строку префикс, содержащий ее длину, чтобы строка могла трактоваться как BSTR*.

Объявление в режиме ole возвращаемого значения типа String заставляет Microsoft VM передавать указатель на неинициализированное зна

чение LPCOLESTR. После возврата из собственной функции Microsoft VM преобразует возвращенное LPCOLESTR в Java String и вызывает CoTaskMemFree для очистки памяти, выделенной под строку.

Передача значений GUID (IID, CLSID)

Для представления GUID используется системный класс com.ms.com._Guid. Передача _Goid-объекта как параметра вызывает передачу в исходную функцию указателя на GUID. Объявление возвращаемого значения типа _Guid заставляет Microsoft VM передать указатель на неинициализированное значение GUID, которое будет заполнено вызывающей функцией (только в режиме ole). Например, OLE32 экспортирует функции CLSIDFromProgID и ProgIDFromCLSID для преобразования CLSID в удобочитаемое имя (и обратно), используемое функцией CreateObject из Visual Basic. Эти методы имеют следующие прототипы: HRESULT CLSIDFromProgID(LPCOLESTR szProgID, LPCLSID pclsid); HRESULT ProgIDFromCLSID(REFCLSID clsid, LPOLESTR *lpszProgId);

В Java эти методы объявляются следующим образом:

import com.ms.com._Guid; class OLE {

/** @dll.import("OLE32", ole) */

public static native _Guid CLSIDFromProgID(String szProgID);

/** @dll.import("OLE32", ole) */

public static native String ProgIDFromCLSID(_Guid clsid); }

Примечание Обратите внимание, что com.ms.com._Guid замещает com. ms.com.Guid (без подчеркивания).

Передача значений типа VARIANT

Объявление параметра типа com.ms.com.Variant приводит к передаче в исходную функцию указателя на VARIANT, Объявление возвращаемого значения типа com.ms.com.Variant (только в режиме ole) приводит к передаче в исходную функцию указателя на неинициализированное значение Variant, которое заполняется этой функцией.

Передача указателей на интерфейсы СОМ

Для передачи указателя на интерфейс СОМ нужно сгенерировать интерфейсный класс Java/COM посредством jactivex.exe или другого подобного средства. Затем можно передавать/принимать интерфейсы СОМ путем объявления параметра, имеющего тип этого класса. Например, системный класс com.ms.com.IStream является интерфейсом Java/COM, который представляет интерфейс структурированного хранилища IStream*. Функция CreateStreamOnHGlobal из OLE32 может быть объявлена следующим образом:

import com.ms.com.*;

/** @dll.import("OLE32", ole) */

public static native IStream CreateStreamOnHGlobal(int hGlobal,

boolean fDeleteOnRelease);

Псевдонимы (переименование методов)

Иногда для Java-метода требуется имя, отличное от применяемого для экспорта функции из DLL. Например, желательно, чтобы оно начиналось со строчной буквы и удовлетворяло соглашению об именовании, принятом в языке Java. Для этого просто используйте модификатор entrypoint в-директиве @dll.import, как показано в следующем примере:

/** @dll.import("USER32", entrypoint="GetSysColor") */

static native int getSysColor(int nlndex);

Псевдонимы не нужны при работе с ANSI/Unicode версиями функций Win32 API. Microsoft VM автоматически делает это за Вас (см. раздел «Как ВМ делает выбор между ANSI и Unicode» далее в этой главе). Не требуются псевдонимы и при доступе к функциям, экспортируемым при помощи файла .def. Экспорт их имен обычно сопровождается так называемой «стандартной подгонкой вызова» («stdcall mangling»). В следующем примере метод sample будет переименован в _sample@8 (где 8 — количество байт, занимаемых параметрами функции):

extern "С"

__declspec(dllexport)

VOID sample(int x, int y){

}

J/Direct автоматически связывается с именам, подвергнутым подгонке, без явного указания точки входа (entrypoint).

Microsoft VM автоматически присваивает псевдонимы для следующих функций KERNEL32 API.

Функция Кеrnе1 32

Псевдоним

CopyMemory

MoveMemory

FillMemory

ZeroMemory

RtlMoveMemory

RtlMoveMemory

RtlFillMemory

RtlZeroMemory

 

Связывание по порядковому номеру

Некоторые библиотеки экспортируют функции по порядковому номеру (16-разрядное целое), а не по имени. Для обращения к такой функ-

ции-нужно использовать директиву @dll.import на уровне метода для указания порядкового номера. Синтаксис связывания по порядковому номеру таков:

/** @dll.import("Libname", entrypoint="#ordinal") */

Обратите внимание, что ordinal задается в десятичном виде. Например, для связывания с функцией под номером 82 из DLL «MyDll.DLL» нужно написать следующий код:

/** @dll.import("MyDll", entrypoint="#82") */

public static native void MySample();

Применение @dll.import ко всему классу

Директиву @dll.import иногда указывают до определения класса, чтобы задать библиотечные имена для всех собственных (native) методов, объявленных в этом классе. Следующее объявление использует @dll.import для класса в целом:

/** edll.import("KERNEL32") */

class EnvironmentStrings {

public static native int GetEnvironmentStrings();

public static native int GetEnvironmentVariable(String name,

StringBuffer value, int ccbValue);

public static native boolean SetEnvironmentVariable(String name,
String value); }

Это эквивалентно объявлению @dll.import для каждого метода:

class EnvironmentStrings

{

/** @dll.import("KERNEL32") */

public static native int GetEnvironmentStrings();

/** §dll.import("KERNEL32") */

public static native int GetEnvironmentVariable(String name,

StringBuffer value, int ccbValue);

/** @dll.import("KERNEL32") */

public static native boolean SetEnvironmentVariable(String name,

String value); }

Использование директивы @dll.import на уровне класса экономит место в файле .class и исключает лишнюю информацию. Но такая директива не наследуется подклассами.

Как ВМ делает выбор между ANSI и Unicode

Вернемся к примеру информационного окна в начале этой главы. Отметим важный факт: USER32 не экспортирует функцию под именем MessageBox. Поскольку функция MessageBox получает строку в качестве параметра, она '(подобно другим функциям Win32, работающих со строками) существует в двух версиях: ANSI и Unicode (MessageBoxA и Mes-sageBoxW соответственно). Когда в коде на С или C++ стоит вызов «функции» MessageBox, в действительности вызывается макрос, который замещается на MessageBoxA или MessageBoxW в зависимости от того, определен макрос UNICODE или нет.

Вызов ANSI-версии функции из DLL

По умдлчанию Microsoft VM предполагает, что нужна ANSI-версия функции MessageBox. Если Вы импортируете функцию MessageBox, используя @dll.import (без модификатора):

/** @dll.import("USER32") */

static native int MessageBox(int hwnd, String text,

String title, int style);

то Microsoft VM выполняет следующие операции.

  1. Строки «text» и «title» конвертируются в ANSI-строки с нулевым символом на конце.
  2. ВМ пытается найти в USER32.DLL экспортируемую функцию MessageBox.
  3. После неудачи ВМ добавляет «А» к имени и ищет экспортируемую функцию MessageBoxA.
  4. Попытка оказываеться удачной, и ВМ вызывает функцию MessageBoxA.

Вызов Unicode-версии функции из DLL

Предположим, что вместо вызова ANSI-версии функции MessageBox Вы хотите вызвать ее Unicode-версию. Это можно сделать, используя директиву @dll.import с модификатором unicode:

/** @dll.import("USER32",Unicode) */

static native int MessageBox(int hwnd, String text, String title, int style);

Поскольку указан модификатор Unicode, то Microsoft VM выполняет следующие операции.

  1. Строки «text» и «title» конвертируются в Unicode-строки с нулевым символом на конце.
  2. ВМ пытается найти в USER32.DLL экспортируемую функцию MessageBox.
  3. Следует неудача, и ВМ добавляет «W» к имени и ищет экспортируемую функцию MessageBoxW.
  4. Попытка оказываеться удачной, и ВМ вызывает функцию Message-BoxW.

Вызов оптимальной версии функции из DLL

К сожалению, ни вызов ANSI-версии, ни вызов Unicode-версии функции из DLL не идеален при обращении к функциям Win32. Использование режима ANSI, установленного по умолчанию, позволяет выполнять код на любой Win32-платформе, но снижает производительность на системах, полностью основанных на Unicode, например на Microsoft Windows NT. При применении модификатора Unicode производительность остается на том же уровне, но возникает ограничение: код выполняется только системами, реализующими Unicode API. К счастью, с директивой @dll.import можно использовать модификатор auto, чтобы вызывать оптимальную версию функции из DLL, исходя из применяемой операционной системы.

Использование модификатора auto позволяет взять лучшее из обоих вариантов. В следующем примере показано, как вызвать оптимальную версию функции MessageBox:

/** edll.import("USER32",auto) */

static native int MessageBox(int hwnd, String text, String title, int style);

Когда присутствует модификатор auto, Microsoft VM во время выполнения определяет, поддерживает ли используемая платформа Unicode API. Если да, то ВМ действует, будто задан модификатор Unicode. В противном случае ВМ действует так, будто задан модификатор ansi. Поэтому модификатор auto позволяет сгенерировать единый двоичный код, который работает на ANSI- и Unicode-системах с использованием оптимального набора API на каждой платформе.

Как правило, модификатор auto требуется при всяком вызове функций Windows API. При вызове функций из Ваших библиотек выберите ansi (по умолчанию) или Unicode в зависимости от того, что Вам нужно. Далее детально описано, как ВМ решает, использовать ANSI или Unicode при наличии модификатора auto.

  1. ВМ открывает раздел реестра HKEY_LOCAL_MACHINE\Software\Microsoft\Java VM и ищет параметр DllImportDefaultType типа DWORD. Этот параметр может иметь следующие значения:
  2. Если параметр реестра не существует или если значение равно 4 (т. е. в зависимости от платформы), то ВМ вызывает Win32-функцию GetVersion и проверяет старший бит возвращаемого значения, чтобы определить, используется платформа Microsoft Windows 95 или Microsoft Windows NT. В первом случае применяется режим ANSI, во втором — режим Unicode.

Вам не придется самостоятельно задавать значение параметра Dlllm-portDefaultType. Он существует в основном для того, чтобы программа установки могла задать соответствующее значение для будущих платформ Windows., Вы можете программно узнать основной режим для Вашей платформы, прочитав поле com.ms.dll.DllLib.systemDefaultChar-Size. Его значение равно 1 для систем ANSI и 2 для систем Unicode. Модификаторы ansi, Unicode и auto используются также с директивой @dll. struct.

Получение кода ошибки, установленного функцией из DLL

Не обращайтесь к функции GetLastError из Win32 для получения кода ошибки, установленной при вызове другой функции из DLL. Поскольку Microsoft VM способна выполнять собственные вызовы функций при исполнении Java-кода, код ошибки может быть изменен к моменту, когда станет Вам доступен.

Для получения достоверного кода ошибки, установленного функцией из DLL, нужно использовать модификатор setLastError, чтобы ВМ сохранила код ошибки сразу после обращения к соответствующему методу. (Это не делается по умолчанию ради производительности.) Далее для получения кода ошибки можно вызвать метод com.ms.dll.DllLib.getLastWin32Error. В каждом потоке Java имеется свое место для хранения этого значения.

Например, функция FindNextFile возвращает информацию о состоянии при помощи кода ошибки и может быть объявлена следующим образом: /** @dll.import("KERNEL32",setLastError) */

static native boolean FindNextFile(int hFindFile,

WIN32_FIND_DATA wfd);

Типичный вызов выглядит так: import com.ms.dll.DHLib;

boolean f = FindNextFile(hFindFile, wfd);

if (If) {

int errorcode = DllLib.getLastWin32Error();

}

Загрузка и обращение к DLL во время выполнения

Есть случаи, когда нужен больший контроль над процессом загрузки и связывания DLL, чем тот, что обычно предоставляется директивой @dll.import, например:

Win32 API всегда предоставляет возможность контролировать загрузку и связывание. Функции LoadLibrary, LoadLibraryEx и FreeLibrary позволяют явно управлять загрузкой и выгрузкой DLL. Функция Get-ProcAddress — связаться с конкретной экспортируемой функцией. Она возвращает указатель на функцию (function pointer), и потому Вы без труда реализуете динамический вызов на любом языке программирования, допускающем обращение к функции через указатель на нее. Средствами J/Direct программисты на Java могут объявить нужные функции следующим образом.

Примечание Если Вы используете пакет com.ms.Win32, то эти объявления попадут также в класс Кегпе132.

/** @dll.import("KERNEL32",auto) */

public native static int LoadLibrary(String IpLibFileName);

/** dll. import("KERNEL32", auto) */

public native static int LoadLibraryEx(String IpLibFileName, int hFile, int dwFlags);

/** @dll.import("KERNEL32",auto) */

public native static boolean FreeLibrary(int hLibModule);

/** @dll.import("KERNEL32",ansi) */

public native static int GetProcAddress(int hModule, String lpProcName);

Обратите внимание, что GetProcAddress объявлена с модификатором ansi, а не auto. Это сделано потому, что GetProcAddress является одной из немногих функций Windows, не имеющих Unicode-эквивалента. При модификаторе auto функция не могла бы использоваться в системах Microsoft Windows NT.

Осталось решить проблему вызова функции, полученной через GetProcAddress. Для удобства msjava.dll (DLL, реализующая Microsoft VM) экспортирует специальную функцию по имени call. Первый ее аргумент —указатель на другую функцию. Все, что делает call, — это вызов другой функции с передачей ей остальных своих аргументов. Представленный далее пример показывает, как приложение загружает DLL и вызывает экспортируемую функцию AFunction.

BOOL AFunction(LPCSTR argument);

/** @dll.import("msjava") */

static native boolean call(int funcptr,String argument);

int hmod = LoadLibrary("...");

int funcaddr = GetProcAddress(hmod, "AFunction");

boolean result = call(funcaddr, "Hello");

FreeLibrary(hmod);

Сравнение J/Direct и RNI

J/Direct и RNI (Raw Native Interface) являются дополняющими друг друга технологиями. Использование RNI требует, чтобы функции DLL придерживались строгих правил именования и работали в согласии со сборщиком мусора Java. Иными словами, функции RNI должны надежно обращаться к GCEnable и GCDisable в коде, отнимающем много времени, и способном отдать управление системе, или в коде, который может заблокироваться другим потоком или ожиданием ввода пользователя. Функции RNI надо разрабатывать специально для работы в среде Java, но зато они получают быстрый доступ к внутренним данным объектов Java и к загрузчику классов Java.

J/Direct связывает Java с существующим кодом (например, с функциями Win32 API), который не предназначен для работы со сборщиком мусора Java и не учитывает других тонкостей библиотеки Java времени выполнения. Однако J/Direct автоматически вызывает GCEnable на стороне клиента, так что Вы можете вызывать функции, которые блокируют выполнение или реализуют пользовательский интерфейс, не создавая трудностей^со сборщиком мусора. Кроме того, J/Direct автоматически переводит обычные типы данных (строки и структуры) в форму, требуемую для функций на С. Поэтому Вам не нужно писать длинный «склеивающий» код и библиотеки-оболочки. Цена, которую приходится платить за это: функции из DLL не могут получить доступ к полям и методам любого объекта Java. В данной реализации им доступны только поля и методы объектов, объявленных с директивой @dll.struct. Другое ограничение в том, что функции RNI нельзя вызывать из функций в DLL, вызванных посредством J/Direct. Это происходит, потому что сборщик мусора способен работать параллельно с функциями из DLL. Следовательно, любым описателям объектов, возвращенным функциями RNI или контролируемым функциями из DLL свойственна нестабильность.

К счастью, можно использовать или RNI, или J/Direct (или и то, и другое). Компилятор и Microsoft VM позволяют смешивать J/Direct и RNI внутри одного класса в соответствии с Вашими потребностями.

Вопросы безопасности

Хотя J/Direct — очень мощное средство для отдельных Java-приложений и надежных Web-приложений для внутренней сети, ясно, что оно слишком сильно для Web в нормальных Java-апплетах. В этом разделе описывается, как J/Direct совместно с системой безопасности Microsoft VM предотвращает воздействие ненадежного кода на J/Direct.

Классы надежные и ненадежные

J/Direct делит все загруженные классы Java на две категории:

Только полностью надежным классам позволено использовать J/Direct. В Java класс считается полностью надежным, если он удовлетворяет одному из следующих критериев:

С другой стороны, апплет в Web без цифровой подписи представляет собой ненадежный Java-код.

Проверка безопасности для вызовов методов J/Direct

Microsoft VM осуществляет три проверки безопасности вызова методов J/Direct:

Попытка вызова удовлетворяется, только если пройдены все три проверки или они явно заблокированы.

Проверка безопасности во время связывания

Связывание происходит, когда один класс Java вызывает компонент из другого класса или обращается к нему (используя Reflection API). Во время связывания Microsoft VM проверяет, доступен ли вызываемый класс и соответствуют ли переданные аргументы по типу и количеству. Класс считается доступным, если он находится в том же самом пакете или если он объявлен открытым (public).

В стандартном языке Java есть два варианта доступности класса: он объявлен открытым (так что любой может связываться с ним) или закрытым, т. е. без модификатора public (и тогда с ним могут связываться только, классы из одного пакета). Однако в Microsoft Internet Explorer 4.0 появился третий вариант — класс объявляется как «public for fully trusted callers only» («открытый только для полностью надежного кода»). Этот вариант доступности возможен для любого класса, даже если тот не использует J/Direct. Чтобы объявить такой класс, поместите в начало его следующую директиву:

/** @security(checkClassLinking=on) */

Важно понять, что эта проверка безопасности предотвращает только прямой вызов «защищенного» класса со стороны ненадежного кода. Но она не предотвращает косвенные вызовы. Третий (полностью надежный) класс способен перенаправлять вызов от ненадежного кода к «защищенному» классу. Но не стоит забывать и о мерах предосторожности. Промежуточный класс надо либо установить в каталог CLASSPATH целевого компьютера, либо добавить к нему цифровую подпись и установить с помощью обозревателя.

Проверка безопасности при первом обращении

Первое обращение к методу — это первый вызов метода из какого-либо вызывающего кода. В этот момент для любого метода, помеченного ключевым словом native, Microsoft VM проверяет, является ли тот членом полностью надежного класса. Если это не так, то генерируется ис-ключение SeсulуЕхсерtion с сообщением «Only fully trusted classes can have native methods as members» («Только полностью надежные классы могут иметь native-методы»). Поскольку проверка не зависит от контекста вызова, она может быть сделана один раз. Если метод прошел проверку, то она не производится при последующих обращениях. Запретить эту проверку невозможно.

Проверка безопасности при каждом обращении

Это самый строгий тест. При каждом вызове проверяется весь стек вызовов. Если обнаруживается хотя бы один вызывающий код, не являющийся полностью надежным, генерируется исключение SecurityExcep-tion. По умолчанию все методы J Direct проходят эту проверку. Методы RNI не проходят этот тест по причинам, вызванным обратной со-

вместимостью. RNI разработан с учетом легкости переноса кода из собственного интерфейса JDK 1.0, который не рассчитан на такую проверку. Хотя эта проверка обеспечивает максимальную безопасность, Microsoft предлагает способ ее блокировки, потому что возникают два важных побочных эффекта.

Возможное снижение производительности

 

 

 

Негибкость

Все стеки вызовов просматриваются при каждом вызове метода J/Direct. Снижение производительности наиболее заметно на надежных апплетах, которые обычно выполняются в присутствии диспетчера безопасности (security manager). С другой стороны, уменьшение производительности приложений обычно незначительно. Это происходит потому, что J/Direct пропускает просмотр стека вызовов для приложений, выполняющихся без диспетчера безопасности.

Данный механизм требует использования максимальных привилегий, даже когда нужна только одна конкретная. Рассмотрим, например, надежную библиотеку, которая использует J/Direct для предоставления ненадежным апплетам одной привилегии абсолютно надежным способом. Для нее стоило бы отключить проверку безопасности во время вызова и выполнять свою собственную проверку на наличие конкретной привилегии.

Директива @security позволяет запретить проверку безопасности при каждом вызове. Синтаксис ее следующий:

/** @security(checkDllCalls=off) */

Директива @security применяется к классу в целом. Отдельные методы внутри класса не могут быть помечены таким образом. Следующий пример показывает размещение директивы @security:

/** @security(checkDllCalls=off) */

class FastJDirectMethods{ /** @dll.import(...) */

static native void func(); }

Учтите, что запрет этой проверки безопасности переносит ответственность с Microsoft VM на Вас. Помните, что даже при запрете такой проверки нужно иметь классы с цифровой подписью для обеспечения максимальной надежности. Если Вы решили использовать эту директиву, примите следующие меры предосторожности:

Примечание Вызовы из методов апплета ink, start, stop и destroy могут породить исключение SecurityExceptionEx, даже если апп-лет является надежным. Чтобы избежать этого, подтвердите свои права, выполнив следующий код:

import coin.ms.security. *; PolicyEngine.assertPermission(PermissionID.SYSTEM);

Проверка безопасности для структур J/Direct

J/Direct также налагает на классы ограничения по безопасности, если классы объявлены директивой @dll.struct. Поскольку структуры становятся незащищенными только при их реализации, эти проверки безопасности более эффективны, чем используемые для методов J/Direct. Для классов с директивой @dll.struct выполняются две проверки безопасности.

Во время загрузки

 

Во время связывания

Классы, объявленные директивой @dll.struct, загружаются, лишь если контекст указывает на полную надежность.

Не полностью надежный код не может связываться с классами, объявленными директивой @dll. struct. Если такая попытка все же делается, Microsoft VM генерирует исключение NoClassDefFoundError.

 

Безопасность и классы com.ms.win32

Для максимальной безопасности методы J/Direct, определенные в пакете com.ms.win32, проверяют стек вызовов при каждом обращении к ним. Если использовать эти классы в Java-приложениях (выполняющихся под JVIEW или WJVIEW), снижение производительности будет незначительным. Если использовать классы com.ms.win32 из надежного класса при требовании максимальной производительности, необходимо скопировать нужные объявления J/Direct в свои классы и запретить проверку безопасности при каждом вызове. Подробнее о запрете проверки безопасности при каждом вызове см. в разделе «Проверка безопасности при каждом обращении» ранее в этой главе.

Сообщения об ошибках

При использовании J/Direct Microsoft VM может генерировать несколько исключений. Далее описаны возможные причины и решения для каждой из следующих ошибок времени выполнения:

java.lang.SecurityException [класс.метод]

Класс исключения Сообщение

Возможные причины

 

 

Возможные решения

java.lang.SecurityException Класс.метод: Only fully trusted classes can have native methods as members.

Ключевое слово native используется с методом, являющимся членом класса, который загружен не со всеми привилегиями (например, апплет без подписи). Это исключение генерируется, только при попытке обращения к native-методу.

Создайте апплету цифровую подпись с запросом всех привилегий.

java.lang.lllegalAccessError

Класс исключения

Java. lang. Illegal AccessFrror

Сообщение

Class has been marked as nonpublic to untrusted code.

Возможные решения Создайте апплету цифровую подпись с запросом всех привилегий.

Возможные причины

Ненадежный класс пытается обратиться к полю или методу класса, который предназначен для использования только надежными классами. К ним относятся многие системные классы из пакетов com.ms.com и com. rns.dll. Класс можно объявить закрытым для ненадежного кода, используя директиву @security следующим образом:

/** @security(checkClassLlnWng=on) */ public class ForTrustedUseOnly{

}

 

java.lang.SecurityException

Класс исключения

java.lang.SecurityException

Сообщение

J/Direct method has not been authorized for use on behalf of an untrusted caller.

Возможные причины

Ненадежный класс обращается к надежному методу, который вызывает J/Direct. Даже если класс, вызывающий J/Direct, является надежным, диспетчер безопасности генерирует исключение SecurityException, если какой-нибудь метод в стеке вызовов принадлежит к ненадежному классу.

Возможные решения

Создайте апплету цифровую подпись с запросом всех привилегий. Или запретите проверку безопасности, объявив класс, делающий вызов J/Direct, с директивой @security, например:

/** @security(checkDllCalls=off) */ public class SafeDllCalls{

...

}

Примечание Учтите, что запрет этой проверки безопасности переносит ответственность с Microsoft VM на Вас. Помните, что даже при запрете этой проверки нужно иметь классы с цифровой подписью для достижения максимальной надежности. Если Вы решили использовать директиву @security, то убедитесь, что:

java.lang.SecurityException

Класс исключения Сообщение

Возможные причины

 

 

Возможные решения

java.lang.SecurityException J/Direct method has not been authorized for use on behalf of an untrusted caller.

Ненадежный класс обращается к надежному методу, который вызывает J/Direct. Даже если класс, вызывающий J/Direct, является надежным, диспетчер безопасности генерирует исключение SecurityException, если какой-нибудь метод в стеке вызовов принадлежит к ненадежному классу.

Создайте апплету цифровую подпись с запросом всех привилегий. Или запретите проверку безопасности, объявив класс, делающий вызов J/Direct, с директивой ©security, например:

/** @security(checkDHCalls=off) */ public class SafeDllCalls{

}

Примечание Учтите, что запрет этой проверки безопасности переносит ответственность с Microsoft VM на Вас. Помните, что даже при запрете этой проверки нужно иметь классы с цифровой подписью для достижения максимальной надежности. Если Вы решили использовать директиву @security, то убедитесь, что:

  • все методы J/Direct объявлены закрытыми (private);
  • Ваш класс предоставляет только те функциональные возможности, которые нужны клиенту;

 

java.lang.NoClassDefFoundError

Класс исключения

Сообщение

Возможные причины

 

 

Возможные решения

Java. long. NoClassDefFoundError

Отсутствует.

Ненадежный класс пытается загрузить класс, объявленный с директивой @dll. struct или созданный с использованием jactivex. Хотя это нарушение защиты (а не ошибка загрузчика классов), исключение NoClassDefFoundError генерируется по причине обратной совместимости.

Создайте апплету цифровую подпись с запросом всех привилегий.

 

com.ms.security.SecurityExceptionEx

Класс исключения

Сообщение

Возможные причины

 

 

Возможные решения

com. ms. security. SecurityExceptionEx

[host] J/Direct method has not been authorized for use on behalf of an untrusted caller.

Сделана попытка вызвать J/Direct из методов апплета mil, start, stop или destroy. Чтобы вызывать J/Direct из этих методов, апплет должен получить привилегии, даже если он имеет цифровую подпись.

Выполните следующий код:

import com. ms. security. *;

....

PolicyEngine.assertPermissiom(PermissionID. SYSTEM);

 

Советы по обнаружению и устранению трудностей

В следующих разделах описаны трудности, вероятные при использовании J/Direct, и приведены возможные решения.

UnsatisifiedLinkError при вызове метода

  1. Каталог, из которого приложение (обычно JVIEW) загружено.
  2. Текущий каталог.
  3. Системный каталог Windows.
  4. Каталог Windows.
  5. Каталоги, перечисленные в переменной окружения PATH. Microsoft VМ не пытается загрузить DLL до тех пор, пока не будет вызван метод, которому она требуется. Поэтому не следует думать, что DLL загружена успешно, так как успешно загружен класс Java.

SecurityException при вызове метода из DLL или при использовании класса с директивой @dll.struct

Вызов DLL и использование классов с директивой @dll.struct ограничены Java-приложениями и Java-апплетами с цифровой подписью.

При возврате из функции из DLL обрезаются строки в StringBuffer

Перед вызовом функции убедитесь, что вместимость (capacity) String-Buffer достаточна для нужной строки. Вместимость указана в конструкторе StringBuffer. Чтобы гарантировать минимальную вместимость перед вызовом функции из DLL, используют метод StringBuffer.ensureCapacity.

Синтаксические ошибки внутри директив @dll

Пробелы внутри директив @dll.import и @dll.struct могут вызвать синтаксические ошибки. Не используйте пробелы внутри конструкций @dll.

Компилятор не может найти пакет com.ms.dll

Используется старая версия Classes.zip. Попробуйте переименовать все старые версии Classes.zip на Вашем жестком диске и переустановите Visual J+ + .

Директивы @dll не работают с апплетами (или работают только внутри среды Microsoft Visual J++)

Поскольку использование J/Direct может нарушить систему защиты, то оно ограничено Java-приложениями и Java-апплетами с цифровой подписью.

Использование J/Direct делает класс ненадежным

Любое использование J/Direct внутри класса делает этот класс небезопасным, поэтому его нельзя употреблять для ненадежного кода, даже если методы J/Direct в реальности не вызываются.

J/Direct генерирует ParameterCountMismatchError после вызова исходной функции

Исключение ParameterCountMismatchError предупреждает, что вызванная функция использовала (вернула на стек) больше или меньше параметров, чем было передано ей от J/Direct. Эта обычно свидетельствует, что параметры в объявлении Java-метода не соответствуют параметрам, которые ожидает получить функция из DLL.

Если функция не вернула на стек ни одного аргумента (не освободила стек), предполагается, что она использует соглашения о вызовах cdecl, и исключение не генерируется, даже если в Java-методе объявлено ненулевое число аргументов.

Внимание Не пытайтесь перехватывать и обрабатывать исключение ParameterCountMismatchError. Оно предназначено для разработчиков — находить ошибки на стадии конструирования. Чтобы не снижать производительность, количество параметров проверяется, только когда приложение выполняется под отладчиком. Важно также помнить, что J/Direct осуществляет ее после завершения вызова функции. Поскольку это исключение говорит о том, что в функцию могли быть переданы неправильные параметры, нет гарантии, что процесс можно восстановить и продолжить выполнение.

J/Direct не выгружает DLL

J/Direct выгружает DLL, когда Microsoft VM перестает использовать класс Java, который импортировал эту DLL. Для Java-приложений, выполняющихся под JVIEW, это не происходит до завершения процесса. Для надежных Java-апплетов это происходит спустя неопределенное время после удаления страницы с апплетом из обозревателя. Microsoft VM пытается сохранить классы Java загруженными для нескольких последовательных страниц, чтобы оптимизировать перезагрузку апплета в случае возврата к странице.

Если нужен явный контроль над загрузкой и выгрузкой DLL, вызывайте загрузчик Windows и обращайтесь к функциям динамически. Подробнее об этом см. в разделе «Загрузка и обращение к DLL во время выполнения» ранее в этой главе.

к оглавлению   к 4GL - визуальному программированию

Знаете ли Вы, что "гравитационное линзирование" якобы наблюдаемое вблизи далеких галактик (но не в масштабе звезд, где оно должно быть по формулам ОТО!), на самом деле является термическим линзированием, связанным с изменениями плотности эфира от нагрева мириадами звезд. Подробнее читайте в FAQ по эфирной физике.

НОВОСТИ ФОРУМА

Форум Рыцари теории эфира


Рыцари теории эфира
 10.11.2021 - 12:37: ПЕРСОНАЛИИ - Personalias -> WHO IS WHO - КТО ЕСТЬ КТО - Карим_Хайдаров.
10.11.2021 - 12:36: СОВЕСТЬ - Conscience -> РАСЧЕЛОВЕЧИВАНИЕ ЧЕЛОВЕКА. КОМУ ЭТО НАДО? - Карим_Хайдаров.
10.11.2021 - 12:36: ВОСПИТАНИЕ, ПРОСВЕЩЕНИЕ, ОБРАЗОВАНИЕ - Upbringing, Inlightening, Education -> Просвещение от д.м.н. Александра Алексеевича Редько - Карим_Хайдаров.
10.11.2021 - 12:35: ЭКОЛОГИЯ - Ecology -> Биологическая безопасность населения - Карим_Хайдаров.
10.11.2021 - 12:34: ВОЙНА, ПОЛИТИКА И НАУКА - War, Politics and Science -> Проблема государственного терроризма - Карим_Хайдаров.
10.11.2021 - 12:34: ВОЙНА, ПОЛИТИКА И НАУКА - War, Politics and Science -> ПРАВОСУДИЯ.НЕТ - Карим_Хайдаров.
10.11.2021 - 12:34: ВОСПИТАНИЕ, ПРОСВЕЩЕНИЕ, ОБРАЗОВАНИЕ - Upbringing, Inlightening, Education -> Просвещение от Вадима Глогера, США - Карим_Хайдаров.
10.11.2021 - 09:18: НОВЫЕ ТЕХНОЛОГИИ - New Technologies -> Волновая генетика Петра Гаряева, 5G-контроль и управление - Карим_Хайдаров.
10.11.2021 - 09:18: ЭКОЛОГИЯ - Ecology -> ЭКОЛОГИЯ ДЛЯ ВСЕХ - Карим_Хайдаров.
10.11.2021 - 09:16: ЭКОЛОГИЯ - Ecology -> ПРОБЛЕМЫ МЕДИЦИНЫ - Карим_Хайдаров.
10.11.2021 - 09:15: ВОСПИТАНИЕ, ПРОСВЕЩЕНИЕ, ОБРАЗОВАНИЕ - Upbringing, Inlightening, Education -> Просвещение от Екатерины Коваленко - Карим_Хайдаров.
10.11.2021 - 09:13: ВОСПИТАНИЕ, ПРОСВЕЩЕНИЕ, ОБРАЗОВАНИЕ - Upbringing, Inlightening, Education -> Просвещение от Вильгельма Варкентина - Карим_Хайдаров.
Bourabai Research - Технологии XXI века Bourabai Research Institution