Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
Бесплатный конструктор сайтов и Landing Page

Хостинг с DDoS защитой от 2.5$ + Бесплатный SSL и Домен

SSD VPS в Нидерландах под различные задачи от 2.6$

✅ Дешевый VPS-хостинг на AMD EPYC: 1vCore, 3GB DDR4, 15GB NVMe всего за €3,50!

🔥 Anti-DDoS защита 12 Тбит/с!

VPS в 21 локации

От 104 рублей в месяц

Безлимитный трафик. Защита от ДДоС.

🔥 VPS до 5.7 ГГц под любые задачи с AntiDDoS в 7 локациях

💸 Гифткод CITFORUM (250р на баланс) и попробуйте уже сейчас!

🛒 Скидка 15% на первый платеж (в течение 24ч)

32-разрядное программирование в Windows

В данном разделе кратко описывается программирование в среде Windows и Win32, и описывается, как можно перенести в эти среды программы. Такой перенос позволит компилировать ваши программы в 16- и 32-разрядной версии Windows.

32-разрядные инструментальные средства Borland C++ обеспечивают создание 32-разрядных файлов .OBJ и .EXE в формате переносимых выполняемых файлов PE. Это формат выполняемого файла для программ Win32 и Windows NT.

Win32

Win32 - это расширение операционной системы Windows 3.1, обеспечивающее поддержку разработки и выполнения 32-разрядных выполняемых файлов Windows. Win32 - это набор DLL, отображающих вызовы 32-разрядного прикладного программного интерфейса (API) в соответствующие 16-разрядные вызовы, использующие виртуальный драйвер устройства (VxD) для работы с памятью и содержащие обновленные функции API. Эти DLL и VxD обеспечивают прозрачный режим работы.

Чтобы обеспечить компиляцию и выполнение своего кода под Win32, вам следует:

  • обеспечить использование в своей программе API Win32;
  • писать переносимый программный код, используя типы и макрокоманды, предусмотренные в файлах windows.h и windowssx.h.

API Win32 расширяют большинство существующих 16-битовых API Windows до 32-битовых. Сюда добавлены и новые вызовы API, совместимые с Windows NT. API Win32 - это подмножество API Win32 для Windows NT. API Win32 состоят из 16-битовых вызовов, преобразованных и допускающих вызов в 32-разрядной среде, и 32-битовых вызовов API, реализуемых в 16-разрядной среде Windows.

Если выполняемые вызовы Win32 любой из функций API Win32 в Win32 не поддерживаются, на этапе выполнения возвращаются соответствующие коды ошибки. Если вы пишете приложения, совпадающие с API Win32 и использующие соглашения по переносимости, ваше приложение должно обладать переносимостью между 16- и 32-разрядной средой Windows.

Написание переносимого кода Windows

В данном разделе обсуждаются конструкции (введенные в Windows 3.1), обеспечивающие переносимость кода Windows. Существующий 16-разрядный код Windows можно переносить с минимальными изменениями в Win32 и Windows NT. Большинство изменений предусматривают подстановку вместо старых новых макрокоманд и типов и замену специфических 16-разрядных вызовов API аналогичными API Win32. После внесения этих изменений ваш программный код сможет компилироваться и выполняться в 16-разрядной и 32-разрядной среде Windows.

Чтобы облегчить создание переносимого кода, предусмотрена переменная среду этапа компиляции STRICT. Windows 3.1 поддерживает определение STRICT в windows.h. Например, если не определена переменная STRICT, то передача HWND функции, требующей HDC, не приведет к выводу компилятором предупреждающего сообщения. Если вы определите STRICT, то получите ошибку компиляции.

Использование STRICT позволяет:

  • выполнять строгую проверку типов;
  • корректировать и согласовывать описания типа параметра и возвращаемого значения;
  • создавать прототипы определений типов для функций обратного вызова (оконные, диалоговые и специальные процедуры);
  • согласовывать с ANSI описания структур COMM, DCB и COMSTAT.

STRICT обладает обратной совместимостью с Windows 3.0, то есть ее можно использовать для создания приложений, работающих в Windows 3.0. Определение STRICT поможет вам находить и корректировать несовместимость типов при переносе программ в 32-разрядную среду и поможет обеспечить переносимость между 16- и 32-разрядной Windows.

Чтобы вы могли изменить свою программу в соответствии со STRICT, предусмотрены новые типы, константы и макрокоманды

Типы и константы Описание
CALLBACKИспользуется вместо FAR PASCAL в подпрограммах обратного вызова (например, оконных и диалоговых процедурах).
LPARAM Описывает все 32-разрядные полиморфические параметры.
LPCSTR То же, что LPSTR, но используется для доступ- ных только по чтению строковых указателей.
LRESULT Описывает все 32-разрядные возвращаемые значения.
UINT Переносимый беззнаковый целочисленный тип, размер которого определяется целевой средой. (В Windows 3.1 представляет 16-битовое значение, а в Win32 - 32-битовое.)
WINAPI Используется вместо FAR PASCAL для описаний API. Если вы пишете DLL с экспортируемыми точками входа API, то можете использовать ее для описаний API.
WPARAM Описывает 16-битовые полиморфические параметры.

Макрокоманда Описание
RELDOFFSET(тип, поле) Вычисляет смещение поля в структуре. "Тип" - это тип структуры, а "поле" - это имя поля.
WAKELP(селект,смещ) Воспринимая селектор и смещение, создает FAR VOID*.
WAKELPARAM(мин,макс) Из двух 16-битовых значений создает LPARAM.
WAKELRESULT(мин,макс) Из двух 16-битовых значений создает LRESULT.
OFFSETOF(указ) Выделяет из дальнего указателя смещение и возвращает UINT.
SELECTOROF(указ) Выделяет из дальнего указателя селектор и возвращает UINT.

Описатели Значение
HACCEL Описатель таблицы акселератора.
HDRVR Описатель драйвера (Windows 3.1).
HDWP Описатель DeferWindowPost().
HFILE Описатель файла.
HGDIOBJ Общий описатель объекта GDI.
HGLOBAL Глобальный описатель.
HINSTANCE Описатель экземпляра.
HLOCAL Локальный описатель.
HMETAFILE Описатель метафайла.
HMODULE Описатель модуля.
HPSRC Описатель ресурса.
HTASK Описатель задачи.

Чтобы сделать ваше приложение согласованным со STRICT, нужно:

  1. Определить, какой программный код вы хотите согласовать со STRICT.
  2. Включить наивысший уровень вывода ошибок/предупреждений. В IDE используйте команду Options Compiler Messages Display All. В BCC32 укажите параметр -w.
  3. Перед включением windows.h и компиляцией определить STRICT с помощью #define или использовать в командной строке параметр -DSTRICT.

Перечислим некоторые рекомендации, которые могут оказаться полезными при преобразовании вашего программного кода в соответствии со STRICT:

  • Измените HANDLE на соответствующий тип описателя, например, HMODULE, HINSTANCE и др.
  • Измените WORD на UINT (за исключением тех мест, где вы хотите получить 16-битовое значение на 32-разрядной платформе).
  • Измените WORD на WPARAM.
  • Измените LONG на LPARAM или LRESULT.
  • Измените FARPROC на WNDPROC, DLGPROC или HOOKPROC.
  • В 16-разрядной Windows всегда описывайте указатели функций с помощью подходящего типа функции, а не с помощью FARPROC. При использовании MakeProcInstance, FreeProcInstance и других функций, воспринимающих или возвращающих FARPROC, вам нужно приводить типы указателей функции, например:
    
                      BOOL CALLBACK DlgProc(HWND hwnd, UINT msg,
    
    
    
                                            WPARAM wParam,
    
    
    
                                            LPARAM lParam);
    
    
    
                                            DLGPROC lpfnDlg;
    
    
    
                      lpfnDlg=(DLGPROC)MakeProcInstance(DlgProc, hinst);
    
    
    
                      ...
    
    
    
                      FreeProcInstance((FARPROC)lpfnDlg);
    
    
    
    
    
    
  • Особое внимание обратите на HMODULE и HINSTANCE. Функции ядра, осуществляющие управление модулями, обычно используют HINSTANCE, но некоторые API возвращают или воспринимают только HMODULE.
  • Если вы копируете какие-то описания функций API из WINDOWS.H, они могут быть изменены, и ваши описания могут оказаться устаревшими. Удалите локальные описания.
  • Приведите тип результата LocalLock и GlobalLock к соответствующему виду указателя данных. Параметры этих и других функций управления памятью должны при необходимости приводиться к LOCALHANDLE или GLOBALHADLE.
  • Приведите результат GetWindowWord и GetWindowLong и параметры к SetWindowWord и SetWindowsLong.
  • При приведении типа SendMessage, DefWinmdowProc и SendDlgItemMsg или любых других функций, которые возвращают LRESULT или LONG к какому-либо описателю вы должны сначала привести результат к UINT:
    
                      HBRUSH hbr;
    
    
    
                      hbr = (HBRUSH)(UINR)
    
    
    
                              SendMessage(hwnd WM_CTLCOLOR, ..., ...);
    
    
    
    
    
    
  • Параметр CreateWindow и CreateWindowEx функции hmenu иногда используются для передачи целочисленного управляющего идентификатора. В этом случае вы должны привести тип к HMENU:
    
                      HWND hwmd;
    
    
    
                      int id;
    
    
    
                      hwnd = CreateWindow("Button", "Ok", BS_PUSBUTTON,
    
    
    
                               x, y, cx, cy, hwndParent,
    
    
    
                               (HMENU)id, // здесь требуется приведение типа
    
    
    
                               hinst, NULL);
    
    
    
    
    
    
  • Полиморфические типы данных (WPARAM, LPARAM, LRESULT, void FAR*) следует возможно скорее присваивать переменным. Избегайте использовать их в своих программах, когда тип значения известен. Это минимизирует число потенциально небезопасных и непереносимых в 32-разрядный режим преобразований типов. Макрокоманды API и механизмы обработки сообщений, предусмотренные в windowsx.h, будут выполнять практически всю упаковку и распаковку этих типов данных способом, обеспечивающим переносимость в 32-разрядный режим.
  • Ознакомьтесь с наиболее общими предупреждениями и ошибками компилятора, которые вы можете обнаружить при переходе к STRICT.

Типы UINT и WORD

Тип UINT создан и широко используется в API для создания типа данных, переносимого с Windows 3.x. UINT определяется как


            typedef undigned int UINT;





UINT необходим из-за различий в размерах int между 16-разрядной Windows и Win32. Для 16-разрядной Windows int - это 16-битовое беззнаковое целое, а для Win32 - 32-битовое беззнаковое целое. Для описания целочисленных объектов, которые при компиляции 32-разрядных приложений предполагается расширить с 16 до 32 бит используйте UINT.

Тип WORD определяется следующим образом:


             typedef unsigned short WORD;

WORD описывает 16-битовое значение и в Windows, и в Word32. Используйте этот тип для создания объектов, которые остаются 16-битовыми на обеих платформах. Поскольку описатели Win32 расширены до 32 бит, они больше не могут иметь тип WORD.

WINAPI и соглашения по вызову CALLBACK

Макрокоманда WINAPI, которая находится в файле windows.h, определяет соглашения по вызову. WINAPI дает в результате соответствующее соглашение по вызову, применяемое на целевой платформе. WINAPI следует использовать вместо FAR PASCAL. Эта макрокоманда позволяет задать альтернативные соглашения по вызову. В настоящее время Win32 использует __stdcall. Фундаментальный тип unsigned изменен на более переносимый UINT.

В своей функции обратного вызова используйте соглашения по вызову CALLBACK, заменяющие FAR PASCAL.

Данные сообщения и обработка сообщений

В 32-битовом коде Windows вам требуется изменить способ распаковки данных сообщения из lParam и wParam. В Win32 wParam вырастает в размере с 16 до 32 бит, в то время как lParam сохраняет размер 32 бита. Так как lParam в 16-разрядной Windows часто содержит описатель и другое значение, а в Win32 описатель увеличивается до 32 бит, необходима новая схема упаковки wParam и lParam.

В качестве примера сообщения, на которое влияют изменения в размере параметра, можно привести WM_COMMAND. В Windows 3.x wParam содержит 16-битовый идентификатор, а lParam - 16-битовый описатель окна и 16-битовую команду.

В Win32 lParam содержит только 32-битовый описатель окна. 16-битовая команда перемещается из lParam в младшие 16 бит wParam, а старшие 16 бит wParam содержат идентификатор. Эта новая схема означает, что вам нужно изменить способ извлечения информации из этих параметров, для чего используются обработчики сообщений.

Обработчик сообщений предоставляет переносимый способ извлечения сообщений из wParam и lParam. В зависимости от вашей среды (16-битовой Windows или Win32) обработчики сообщений используют разные методы выделения из сообщений данных. Использование макрокоманд обработки сообщений обеспечит функционирования кода выделения данных из сообщения на любой из двух платформ.

Переносимость системных вызовов DOS

Для вызова функций файлового ввода-вывода DOS Windows 3.0 предусматривает функцию API DOS3Call. Эта и другие функции INT 21H заменены в Win32 соответствующими 32-разрядными вызовами.

Функция
21H
Операция
DOS
Эквивалент API
Win32
OEH Выбор диска. SetCurrentDirectory
19H Получение текущего диска. GetCurrentDirectory
2AH Получение даты. GetDateAndTime
2BH Установка даты. SetDateAndTime
2CH Получение времени. GetDateAndTime
3DH Установка времени. SetDateAndTime
36H Получение свободного GetDiskFreeSpace пространства на диске.
39H Создание каталога. CreateFile
3AH Удаление каталога. RemoveDirectory
3BH Установка текущего каталога. SetCurrentDirectory
3CH Создание описателя. CreateFile
3DH Открытие описателя. CreateFile
3EH Закрытие описателя. CloseHandle
3FH Чтение описателя. ReadFile
40H Запись описателя. WriteFile
41H Удаление файла. DeleteFile
42H Получение атрибутов файла. SetFilePointer
43H Получение атрибутов файла. GetAttributesFile
43H Установка атрибутов файла. SetAttributesFile
47H Получение текущего каталога. GetCurrentDirectory
4EH Поиск первого файла. FindFirstFile
4FH Поиск следующего файла. FindNextFile
56H Изменение записи каталога. MoveFile
57H Получение даты/времени файла. GetDateAndTimeFile
57H Установка даты/времени файла. SetDateAndTimeFile
59H Получение расширенной ошибки. GetLastError
5AH Создание уникального файла. GetTempFileName
5BH Создание нового файла. CreateFile
5CH Блокировка файла. LockFile
5CH Разблокировка файла. UnlockFile
67H Установка описателя счетчика. SetHandleCount

Общие сообщения об ошибках и предупреждения компилятора

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

Call to function имя_функции with no prototype
(Вызов функции без прототипа)

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

Conversion may lose signifigant digits
(Преобразование может привести к потере значимых цифр)

Это предупреждение является результатом преобразования компилятором значения, например, LONG в int. Оно сообщает, что вы можете потерять информацию. Если вы уверены, что таких проблем не возникает, то с помощью соответствующего явного приведения типа к меньшему размеру это предупреждение можно подавить.

Function should return a value
(Функция должна возвращать значение)

Функция, описанная как возвращающая значение, значения не возвращает. В старом коде Си, отличном от стандарта ANSI, такое предупреждение является общим для функций, не возвращающих значения и не имеющих типа возврата:


            foo(i)



            int i;



            {



            ...



            }





Описанные таким образом функции интерпретируются компилятором, как возвращающие int. Если функция ничего не возвращает, ее следует описать так:


            void foo(int i)



            {



            ...



            }



Lvalue required
(Требуется именующее значение)

Type mismatch in parameter
(Несовпадение типа в параметре)

Эти ошибки указывают, что вы пытаетесь передать тип, отличный от указателя, там, где требуется указатель. При определении STRICT описателей, а также LRESULT, WPARAM и LPARAM внутренним образом описываются как указатели, поэтому попытка передачи в качестве указателя int, WORD или LONG дает в результате подобные ошибки.

Эти ошибки следует исправлять путем описания отличных от указателя значения, используемых при передаче параметров или присваивании. В случае специальных констант, таких как (HWND)1, вам следует пользоваться новыми макрокомандами (такими как HWND_BOTTOM). Ошибки несоответствия типов следует подавлять в исключительных случаях (так как часто это может дать в результате некорректный код).

Non-portable conversion
(Не переносимое преобразование)

Вы приводите указатель или описатель ближнего типа к 32-битовому значению, такому как LRESULT, LPARAM, LONG или DWORD. Это предупреждение практически всегда указывает на ошибку, так как старшие 16 бит значения будут содержать ненулевое значение. Помещая в старшие 16 бит значение текущего сегмента данных, компилятор сначала конвертирует 16-битовый ближний указатель к 32-битовому дальнему указателю.

Чтобы избежать этого предупреждения и обеспечить размещение в старших 16 битах 0, нужно привести тип описателя к UINT:


            HWND hwnd;



            LRESULT result = (LRESULT)(UINT)hwnd;





В тех случаях, когда вы хотите, чтобы 32-битовое значение содержало указатель FAR, можно избежать предупреждения путем явного приведения типа к дальнему указателю:


            char near* pch;



            LPARAM lparam = (LPARAM)(LPSTR)pch;





Not an allowed type
(Не является допустимым типом)

Это сообщение об ошибке обычно выводится в результате попытки разыменования указателя void. Обычно это бывает при непосредственном использовании значения-указателя, возвращаемого GlobalLock или LocalLock. Чтобы решить данную проблему, перед использованием указателя присвойте возвращаемое значение переменной соответствующего типа (используя при необходимости приведение типа).

Size of the type is unknown or zero
(Размер типа неизвестен или равен нулю)

Вы пытаетесь с помощью + или += изменить значение пустого указателя. Это ошибка обычно появляется в результате того, что отдельные функции Windows (например, GlobalLock или LocalLock) возвращающие указатели произвольных типов, определены для возврата void FAR* вместо LPSTR. Чтобы решить эту проблему, присвойте значение void* описанной соответствующим образом переменной (приведя при необходимости тип):


            BYTE FAR* lpb = (BYTE FAR*)GlobalLock(h);



            lpb += sizeof(DWORD);





Type mismatch in redeclaration of имя_параметра
(Несовпадение типа при повторном описании параметра)

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

Построение выполняемых файлов Win32

Для построения приложения Win32 вы должны использовать соответствующие инструментальные средства, параметры, библиотеки и код запуска. В следующей таблице перечислены параметры компилятора и компоновщика, библиотеки и код запуска, необходимые для компоновки и получения файлов .DLL и .EXE.


                                                                        



         Параметры    Параметры   Библиотеки       Код     Создаваемый  



           BCC32        TLINK                     запуска     файл      



                                                                        



         -W, -WE        /Tpe      cw32.lib       c0w32.obj    GUI.EXE   



                                  import32.lib                          



                                                                        



         -WD, -WDE      /Tpd      cw32.lib       c0x32.obj    GUI.DLL   



                                  imprtw32.lib                          



                                                                        



         -WC            /Tpe/ap   cx32.lib       c0d32.obj  Console.EXE 



                                  import32.lib                          



                                                                        



         -WCD, -WCDE    /Tpd/ap   cx32.lib       c0d32.obj  Console.DLL 



                                  imprtw32.lib                          



Библиотеки DLL

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

Динамическая компоновка

Когда приложение использует функцию из статически компонуемой библиотеки (например, библиотеки исполняющей системы Си), копия этой функции связывается с вашим приложением на этапе компоновки (компоновщиком TLINK). Два одновременно работающих приложения, использующих одну и ту же функцию, будут иметь собственную копию данной функции. Эффективней было бы совместно использовать единственную ее копию. Такую возможность дают динамически компонуемые библиотеки. При динамической компоновке внешние ссылки разрешаются на этапе выполнения.

Когда программа использует функцию из DLL, эта функция не компонуется с файлом .EXE. При динамической компоновке используется другой метод. Во-первых, на этапе компоновке TLINK связывает с вашим файлом .EXE записи импорта (содержащие информацию о DLL и адресе процедуры). Это временно разрешает в вашей программе внешние ссылки на функции DLL. Данные записи импорта обеспечиваются файлами определения модуля или библиотеками импорта. На этапе выполнения информация записи импорта используется для поиска и привязки к вашей программе функции DLL.

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

Создание DLL

DLL создаются аналогично файлам .EXE: компилируются файлы исходного кода, затем выполняется компоновка объектных файлов. Однако, DLL не имеют функции main и компонуются по другому. Ниже рассказывается, как следует писать DLL.


                         LibMain, DllEntryPoint и WEP





В качестве основной точки входа для DLL должны предусматриваться функция LibMain (для 16-разрядных программ) или DllEntryPoint (для 32-разрядных). В случае 32-разрядных программ Windows вызывает DllEntryPoint при каждой загрузке или выгрузке DLL, при каждом присоединении или отсоединении от DLL дополнительных процессов или при создании/уничтожении внутри процесса нити.

Инициализация DLL практически целиком зависит от функции конкретной DLL и может включать в себя следующие основные задачи:

  • Разблокировку сегмента данных с помощью UnlockData, если она описывается как MOVEABLE (только для 16-битовых приложений).
  • Задание для DLL глобальных переменных (если она их использует).

Код инициализации выполняется только для первого приложения, использующего DLL. Код запуска DLL автоматически инициализирует локальную динамически распределяемую область памяти, поэтому в LibMain не нужно включать соответствующий код. Функции LibMain передаются следующие параметры:


            int FAR PASCAL LibMain (HINSTANCE hInstance, WORD wDataSeg;



                                    WORD cbHeapSize, LPSTR lpSmdLine)



hInstance - это описатель экземпляра DLL.

wDataSeg - это значение регистра сегмента данных (DS).

cbHeapSize - размер локальной динамически распределяемой области памяти, заданной для DLL в файле определения модуля.

lpCmdLine - это дальний указатель командной строки, задан ной при загрузке DLL. Он почти всегда нулевой, так как DLL обычно загружается автоматически и без параметров.

LibMain обычно возвращает значение 0 (успешная инициализация) или 1 (неуспешная инициализация). В последнем случае Windows выгружает DLL из памяти.

Точкой выхода для 16-битовой DLL является функция WEP (Windows Exit Procedure). Эта функция для DLL не обязательна, так как библиотеки исполняющей системы Borland C++ предусматривают ее по умолчанию, но для выполнения какой-либо очистки перед выгрузкой DLL из памяти вы можете указать свою собственную функцию.

В Borland С++ WEP экспортировать не требуется. Borland С++ определяет свою собственную WEP, которая вызывает вашу WEP (если она определена), а затем выполняет очистку системы. WEP имеет следующий прототип:


            int FAR PASCAL WEP (int nParameter)





где nParameter - это WEP_SYSTEMEXIT или WEP_FREE_DLL. WEP_SYSTEMEXIT указывает на завершение работы Windows, а WEP_FREE_DLL только на выгрузку DLL. В случае успешного выполнения WEP возвращает 1. По этому значению Windows в настоящее время не выполняет никаких действий.

Экспорт и импорт функций

Чтобы сделать функции DLL доступными для других приложений (.EXE или других DLL), имена функций должны экспортироваться. Чтобы использовать экспортированные функции, нужно импортировать их мена. Экспорт функций выполняется двумя способами:

  • Создается файл определения модуля с секцией EXPORTS, в которой перечисляются все функции, используемые в других приложениях. Это можно сделать с помощью инструментального средства IMPDEF.
  • Перед именем каждой функции DLL, которая должна экспортироваться, можно указать ключевое слово _export (в определении функции). Кроме того, при построении или компоновке DLL в IDE нужно выбрать корректный параметр генерации кода или задать корректную командную строку компилятора.

Чтобы функция могла импортироваться другим приложением или DLL, она должна описываться как экспортируемая из DLL. Вы должны также указать компоновщику, что хотите импортировать эти функции. Это можно сделать тремя способами:

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

Функции DLL не компонуются непосредственно с приложением Windows. Они вызываются на этапе выполнения. Это означает, что такие функции должны иметь дальний тип вызова (так как DLL будет иметь другой сегмент кода). Используемые функцией DLL данные также должны иметь дальний тип.

Чтобы функцию можно было использовать в приложении, она должна также компилироваться как доступная для экспорта и затем экспортироваться. Для этого вы можете скомпилировать DLL так, чтобы все функции в ней были экспортируемыми (параметр -WD), и указать перед ними ключевое слово _export.

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

Экспорт и импорт классов

Чтобы использовать в DLL классы, класс требуется экспортировать из DLL и импортировать в файл .EXE. Для этого можно использовать условное макроопределение. Например, в файл заголовка можно включить следующее:


            #if defined (BUILD_DLL)



                #define _MY_CLASS _export



            #elif defined(USE_DLL)



                #define _MY_CLASS _import



            #else



                #define _MY_CLASS



            #endif





В своих определения определите классы следующим образом:

            class _MY_CLASS class {



            ...



            };





При построении DLL определите BUILD_DLL (например, с помощью параметра -D). Макрокоманда _MY_CLASS будет расширяться в _import. Определите _MY_DLL при построении файла .EXE, который будет использовать DLL. Макрокоманда _MY_CLASS будет расширяться в _import.

Статические данные в 16-битовых DLL

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

Использование DLL

Ниже перечислены типичные формы командной строки, позволяющие использовать DLL-версии библиотек исполняющей системы Borland и описанные ниже библиотеки классов.

Для 16-битовой компиляции и компоновки с использованием DLL-версии библиотеки исполняющей системы дайте команду:


            bcc -c -D_RTLDLL -ml source.cpp



            tlink -C -Twe c0w1 source, source, , import crtldll





Обратите внимание на использование макрокоманды _RTLDLL и переключателя командной строки -ml. Для 32-битовой версии используются команды:


            bcc -c -D_RTLDLL source.cpp



            tlink32 -Tpe -ap c0x32 source, source, , import32 cw32i





Для 16-битовой компиляции и компоновки с использованием DLL-версии библиотеки класса дайте команды:


            bcc -c -D_BIDSDLL -ml source.cpp



            tlink -C -Twe c0w1 source, source, , import bidsi crtldll





Для 32-битовой компиляции и компоновки с использованием DLL-версии библиотеки класса дайте команды:


            bcc32 -c -D_BIDSDLL -ml source.cpp



            tlink32 -Tpe -ap c0x32 source, source,



                                      , import32 bidsfi cw32i



Встроенный ассемблер

Встроенный ассемблер позволяет включать в ваши программы на языке Си и С++ операторы ассемблера. Инструкции встроенного ассемблера компилируются и ассемблируются с вашей программой, и вам не потребуется писать отдельные модули.

Синтаксис встроенного ассемблера и его использование

Чтобы включить в код Си/С++ инструкции ассемблера, используйте ключевое слово asm и следующий формат:


            asm код_операции операнды;





где "код_операции" - допустимая инструкция процессора 80х86, "операнды" содержат операнды (операнд), допустимые для указанной операции (константы, переменные и метки). Концом оператора asm является символ ; или новая строка. После точки с запятой на той же строке может размещаться новый оператор asm, но на следующей строке оператор продолжаться не может. Для включения нескольких операторов asm их можно заключить в фигурные скобки (первая скобка должна быть на той же строке, что и asm):

            asm {



                pop ax; pop ds



                iret



                }





Точки с запятой для комментария здесь не используются (как в TASM). При комментировании таких операторов применяйте стандартные комментарии Си. Ассемблерная часть оператора копируется непосредственно в вывод и включаются в операторы языка ассемблера, которые Borland С++ генерирует для инструкций Си и С++. Все идентификаторы Си заменяются на соответствующие эквиваленты ассемблера. Каждый оператор asm рассматривается как оператор Си.

Оператор asm может использоваться в функции или как внешнее описание вне функции. Оператор asm, размещенные внутри функции, помещаются в сегмент кода, а операторы asm вне функции - в сегмент данных.

Ссылки на данные и функции

В операторах asm вы можете использовать любой идентификатор Си, включая динамические локальные переменные, регистровые переменные и параметры функции. Borland С++ автоматически преобразует эти символы в соответствующие операнды ассемблера и добавляет к именам идентификаторов символы подчеркивания.

В общем случае вы можете использовать идентификатор Си в любой позиции, где допускается операнд адреса. Там, где допустим регистровый операнд, вы можете использовать регистровую переменную. Если ассемблер обнаруживает идентификатор при анализе операторов инструкции встроенного ассемблера, то он ищет идентификатор в таблице идентификаторов Си. Имена регистров 80х86 из поиска исключаются. Имена регистров можно задавать символами в верхнем регистре.

При программировании вам не нужно заботиться о точных смещениях локальных переменных - использование имени переменной предусматривает корректные смещения. Однако, может оказаться необходимым включить в инструкции ассемблера WORD PTR, BYTE PTR или другие переопределения размера. Для косвенных инструкций вызова типа FAR или LES необходимо переопределение DWORD PTR.

Использование элементов структуры

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


            struct myStruct {



                int a_a;



                int a_b;



                int a_c;



            } myA;







            myfumc()



            {



            ...



            asm {mov ax, WORD PTR myA.a_b



                 mov bx, WORD PTR myA.a_c



                }



            ...



            }





Этот фрагмент описывает структурный тип с именем myStruct с тремя элементами a_a, a_b, a_c. Первый оператор встроенного ассемблера перемещает значение, содержащееся в myA.a_b, в регистр AX. Второй перемещает значение по адресу [di]+offset(a_c) в регистр BX (он берет адрес, записанный в DI, и добавляет его к смещению a_c от начала myStruct). Оператор ассемблера даст следующий код:


            mov ax, DGROUP : myA+2



            mov bx, [di+4]





Таким образом, если вы загружаете регистр (такой как DI) адресом структуры типа meStruct, то можете использовать имена элементов структуры для непосредственной ссылки на них. Имя элемента структуры можно использовать в любой позиции, где вы операторе языка ассемблера допускается числовая константа.

Элементу структуры должна предшествовать точка. Она указывает, что это имя элемента структуры, а не обычный идентификатор Си. В выводе ассебмлера имена элементов структуры заменяются соответствующими числовым смещением элемента, а информация о типе не сохраняется. Такие элементы структуры могут использоваться в операторах ассебмлера в качестве констант этапа компиляции.

Однако есть одно ограничение: если две используемые во встроенном ассебмлере структуры имеют элемент с одинаковыми именем, их нужно сделать различными. Укажите между точкой и именем элемента тип структуры в скобках (как в случае приведения типа), например:


            asm  mov  bx.[di].(struct tm)tm_hour





Метки и инструкции перехода

Во встроенном ассемблере вы можете использовать любые условные и безусловные инструкции перехода и циклов. Эти инструкции допускаются только внутри функции. Поскольку метки в операторах asm определить нельзя, инструкции перехода должны использовать метки goto языка Си. Если метка находится слишком далеко, переход не будет автоматически преобразовываться в дальний переход. Поэтому работать с условными переходами нужно аккуратно. Для проверки переходов можно использовать параметр -B. Прямые переходы дальнего типа генерировать нельзя. Допускаются косвенные переходы. Чтобы использовать косвенные переходы, применяйте в качестве операнда инструкции перехода имя регистра.

Компиляция со встроенным ассемблером

Существует два способа, с помощью которых Borland C++ может обрабатывать в вашем коде Си или С++ операторы встроенного ассемблера. Во-первых, Borland С++ может преобразовывать код Cи или C++ непосредственно в код ассебмлера, а затем передавать его TASM для генерации файла .OBJ. Во-вторых, Borland С++ для включения ваших операторов ассебмлера непосредственно в поток инструкций компилятора может использовать встроенный ассебмлер (BASM).

Для обработки операторов ассебмлера, встроенных в программы Си и С++ можно указать параметр компилятора -B. По умолчанию -B вызывает TASM или TASM32. Это можно переопределить с помощью параметр -Exxx, где xxx - другой ассебмлер. Если указывается параметр -B, компилятор сначала генерирует файл ассебмлера, а затем вызывает TASM. Если включить в исходные код программы оператор #pragma inline, то TASM будет вызываться и без параметра -B.

16-разрядный компилятор Borland С++ использует BASM, что позволяет ассемблировать встроенный код ассебмлера самому компилятору, а не TASM. Этот внутренний ассебмлер компилятора делает все то же, что и TASM, но имеет следующие ограничения:

  • не может использовать макрокоманды ассебмлера;
  • не обрабатывает инструкции 80386/80486;
  • не допускает синтаксис режима Ideal;
  • позволяет использовать только ограниченный набор директив ассебмлера.

Так как BASM не является полным ассебмлером, некоторые конструкции этого языка он не воспринимает. В этом случае Borland С++ будет выводить сообщение об ошибке. При этом у вас есть две возможности: упростить программный код встроенного ассебмлера или использовать параметр -B для вызова TASM. Однако TASM может не идентифицировать место ошибки, так как номер строки исходного кода теряется.

Коды операций

В качестве операторов встроенного ассемблирования допускается включать любые коды операций 80х86. Существует четыре класса команд, позволяемых компилятором Borland C++:

  • обычные команды - стандартный набор кодов операций процессора 8086;
  • строковые команды - специальные коды обработки строк;
  • команды перехода - различные коды операций перехода;
  • директивы ассемблирования - размещения и определения данных.

Отметим, что компилятор допускает задания любых операндов, даже если они ошибочны или не разрешены Ассемблером. Точный формат операндов не может быть принудительно установлен компилятором.

Ниже приводится полный перечень мнемонических имен кодов операций, которые могут быть использованы в операторах встроенного ассемблирования:

Мнемонические имена кодов операций

aaa fdvtr fpatan lsl aad feni fprem mov aam ffroe** fplan mul aas fiadd frndint neg adc ficom frstor nop add ficomp fsave not and fidiv fscale or bound fidifr fsqrt out call fild fst pop cbw fimul fstcw popa clc fincstp** fslenv popi cld finit fstp push cli fist fstsw pusha cmc fistp fsub pushf cmp fisub fsubp rcl cwd fisubr fsubr rcr daa fld fsubrp ret das fld1 ftst rol dec fldcw fweit ror div fldenv fxam sahf enter fldl2e fxch sal f2xm1 fldl2t fxtract sar fabs fldlg2 fyl2x sbb fadd fldln2 fyl2xp1 shl faddp fldpi hlt shr fold fldz idiv smsw fbstp fmul imul stc fchs fmulp in std fclex fnclex inc sti fcom fndisi int sub fcomp fneni into test fcompp fninit iret verr fdecstp** fnop lahf verw fdisi fnsave lds wait fdiv fnstcw lea xchg fdivp fnstenv leave xlat fdivr fnstsw les xor

При использовании средства встроенного ассемблирования в подпрограммах, эмулирующих операции с плавающей точкой (параметр BCC -f), коды операции, помеченные **, не поддерживаются.

При использовании в операторах встроенного ассемблирования мнемонических команд процессора 80186 необходимо включать параметр командной строки -1. Тогда компилятор включит в генерируемый им Ассемблерный код соответствующие операторы, в результате чего Турбо Ассемблер будет ожидать появление данных мнемонических имен. При использовании предыдущих версий ассемблера эти мнемонические имена могут не поддерживаться.

Строковые инструкции

Помимо кодов операций, приведенных выше, возможно использование следующих строковых команд, как в исходном виде, так и с префиксами циклического выполнения. Строковые инструкции BASM cmps insw movsb outsw stos smpsb lods movsw scas stosb smpsw lodsb scasb stosw lodsw outsb scasw insb movs

Префиксы

Допустимы следующие префиксы: lock rep repe repne repnz repz

Инструкции перехода

Инструкции перехода рассматриваются отдельно. Поскольку метка не может быть включена в саму команду, переходы выполняются к меткам Си (см. выше раздел "Использование команд перехода и меток"). В следующей таблице перечислены допустимые инструкции перехода: Инструкции перехода ja jge jnc jns loop jae jl jne jnz loope jb jle jng jo loopne jbe jmp jnge jp loopnz jc jna jnl jpe loopz jcxz jnae jnle jpo je jnb jno js jg jnbe jnp jz

Директивы ассемблирования

В операторах встроенного ассемблирования Borland C++ допустимы следующие директивы: db dd dw extrn

Назад | Содержание | Вперед

Скидка до 20% на услуги дата-центра. Аренда серверной стойки. Colocation от 1U!

Миграция в облако #SotelCloud. Виртуальный сервер в облаке. Выбрать конфигурацию на сайте!

Виртуальная АТС для вашего бизнеса. Приветственные бонусы для новых клиентов!

Виртуальные VPS серверы в РФ и ЕС

Dedicated серверы в РФ и ЕС

По промокоду CITFORUM скидка 30% на заказ VPS\VDS

VPS/VDS серверы. 30 локаций на выбор

Серверы VPS/VDS с большим диском

Хорошие условия для реселлеров

4VPS.SU - VPS в 17-ти странах

2Gbit/s безлимит

Современное железо!

Новости мира IT:

Архив новостей

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 495 7861149
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2019 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...