Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Программирование для Windows NT

© Александр Фролов, Григорий Фролов
Том 27, часть 2, М.: Диалог-МИФИ, 1996, 272 стр.

[Назад] [Содеожание] [Дальше]

Приложение DLLCALL

Приложение DLLCALL работает совместно с DLL-библиотекой DLLDemo.DLL, описанной в предыдущем разделе.

Главное окно приложения DLLCALL и меню File показано на рис. 3.7.

Рис. 3.7. Главное окно приложения DLLCALL

Если из меню File выбрать строку Find App Window, на экране появится диалоговая панель Find Application Window, показанная на рис. 3.8.

Рис. 3.8. Диалоговая панель Find Application Window

Здесь в поле Enter application window title to find вы должны ввести заголовок окна приложения. Если приложение с таким заголовком запущено, оно будет найдено, после чего на экране появится соответствующее сообщение (рис. 3.9).

Рис. 3.9. Сообщение о том, что заданное окно найдено

При инициализации DLL-библиотеки, вызванной подключением процесса DLLCALL, на экране возникает сообщение, показанное на рис. 3.10.

Рис. 3.10. Собщение о подключении процесса к DLL-библиотеке

Когда приложение DLLCALL отключается от DLL-библиотеки, на экран выводится сообщение, показанное на рис. 3.11.

Рис. 3.11. Сообщение об отключении процесса от DLL-библиотеки

Главный файл исходных текстов приложения DLLCALL представлен в листинге 3.4.

Листинг 3.4. Файл dlldemo\dllcall\dllcall.c


// ==================================================
// Приложение DLLCall
// Вызов функции из DLL-библиотеки
//
// (С) Фролов А.В., 1996
// Email: frolov@glas.apc.org
// ==================================================

#define STRICT
#include <windows.h>
#include <windowsx.h>
#include "resource.h"
#include "afxres.h"

#include "dllcall.h"

// Определяем тип: указатель на функцию
typedef HWND (WINAPI *MYDLLPROC)(LPSTR);

// Указатель на функцию, расположенную в 
// DLL-библиотеке
MYDLLPROC GetAppWindow;

// Буфер для заголовка окна, поиск которого
// будет выполняться
char szWindowTitle[512];

// Идентификатор DLL-библиотеки
HANDLE hDLL;

HINSTANCE hInst;
char szAppName[]  = "DLLCallApp";
char szAppTitle[] = "DLL Call Demo";

// -----------------------------------------------------
// Функция WinMain
// -----------------------------------------------------
int APIENTRY 
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASSEX wc;
  HWND hWnd;
  MSG msg;
  
  // Сохраняем идентификатор приложения
  hInst = hInstance;

  // Преверяем, не было ли это приложение запущено ранее
  hWnd = FindWindow(szAppName, NULL);
  if(hWnd)
  {
    // Если было, выдвигаем окно приложения на
    // передний план
    if(IsIconic(hWnd))
	    ShowWindow(hWnd, SW_RESTORE);
	  SetForegroundWindow(hWnd);
    return FALSE;
  }

  // Регистрируем класс окна
  memset(&wc, 0, sizeof(wc));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.hIconSm = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICONSM), 
    IMAGE_ICON, 16, 16, 0);
  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC)WndProc;
  wc.cbClsExtra  = 0;
  wc.cbWndExtra  = 0;
  wc.hInstance = hInst;
  wc.hIcon = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICON), 
    IMAGE_ICON, 32, 32, 0);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU);
  wc.lpszClassName = szAppName;
  if(!RegisterClassEx(&wc))
    if(!RegisterClass((LPWNDCLASS)&wc.style))
	  return FALSE;
    
  // Создаем главное окно приложения
  hWnd = CreateWindow(szAppName, szAppTitle, 
     WS_OVERLAPPEDWINDOW, 
     CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 
     NULL, NULL, hInst, NULL);
  if(!hWnd) return(FALSE);

  // Отображаем окно и запускаем цикл 
  // обработки сообщений
  ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);
  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// -----------------------------------------------------
// Функция WndProc
// -----------------------------------------------------
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, 
        LPARAM lParam)
{
  switch(msg)
  {
    HANDLE_MSG(hWnd, WM_COMMAND, WndProc_OnCommand);
    HANDLE_MSG(hWnd, WM_DESTROY, WndProc_OnDestroy);

    default:
      return(DefWindowProc(hWnd, msg, wParam, lParam));
  }
}

// -----------------------------------------------------
// Функция WndProc_OnDestroy
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnDestroy(HWND hWnd)
{
  PostQuitMessage(0);
  return 0L;
}

// -----------------------------------------------------
// Функция WndProc_OnCommand
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnCommand(HWND hWnd, int id, 
  HWND hwndCtl, UINT codeNotify)
{
  switch (id)
  {
    case ID_FILE_EXIT:  
    {
      // Завершаем работу приложения
      PostQuitMessage(0);
      return 0L;
      break;
    }
	  
    case ID_FILE_FINDWINDOW:
    {
      // Отображаем диалоговую панель для ввода
      // заголовка главного окна приложения,
      // поиск которого будет выполняться
      if(DialogBox(hInst, MAKEINTRESOURCE(IDD_DLGFIND), 
        hWnd, DlgProc))
      {
        // Первый способ вызова функции из DLL-библиотеки:
        // прямой вызов с использованием библиотеки экспорта

/*
        // Выполняем поиск окна с заголовком, заданным
        // при помощи диалоговой панели
        if(FindApplicationWindow(szWindowTitle) != NULL)
          MessageBox(NULL, "Application window was found",
            szAppTitle, MB_OK | MB_ICONINFORMATION);
        
          else
          MessageBox(NULL, "Application window was not found",
            szAppTitle, MB_OK | MB_ICONINFORMATION);
*/

        // Второй способ вызова функции из DLL-библиотеки:
        // загрузка DLL-библиотеки функцией LoadLibrary

        // Загружаем DLL-библиотеку
        hDLL = LoadLibrary("DLLDEMO.DLL");

        // Если библиотека загружена успешно, выполняем
        // вызов функции
        if(hDLL != NULL)
        {
          // Получаем адрес нужной нам функции
          GetAppWindow = 
            (MYDLLPROC)GetProcAddress(hDLL, 
              "FindApplicationWindow");

          // Если адрес получен, вызываем функцию
          if(GetAppWindow != NULL)
          {
            // Выполняем поиск окна с заголовком, заданным
            // при помощи диалоговой панели
            if(GetAppWindow(szWindowTitle) != NULL)
              MessageBox(NULL, "Application window was found",
                szAppTitle, MB_OK | MB_ICONINFORMATION);
            else
              MessageBox(NULL, 
                "Application window was not found",
                szAppTitle, MB_OK | MB_ICONINFORMATION);
          }
          
          // Освобождаем DLL-библиотеку
          FreeLibrary(hDLL);
        }
      }
 
      break;      
    }
    
    case ID_HELP_ABOUT:
    {
      MessageBox(hWnd, 
        "DLL Call Demo\n"
        "(C) Alexandr Frolov, 1996\n"
        "Email: frolov@glas.apc.org",
        szAppTitle, MB_OK | MB_ICONINFORMATION);
	     return 0L;
	     break;
    }

  	default:
	    break;
  }
  return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, codeNotify,
    DefWindowProc);
}

// -----------------------------------------------------
// Функция DlgProc
// -----------------------------------------------------
LRESULT WINAPI
DlgProc(HWND hdlg, UINT msg, WPARAM wParam, 
        LPARAM lParam)
{
  switch(msg)
  {
    HANDLE_MSG(hdlg, WM_INITDIALOG, DlgProc_OnInitDialog);
    HANDLE_MSG(hdlg, WM_COMMAND,    DlgProc_OnCommand);

	default:
    return FALSE;
  }
}

// -----------------------------------------------------
// Функция DlgProc_OnInitDialog
// -----------------------------------------------------

BOOL DlgProc_OnInitDialog(HWND hdlg, HWND hwndFocus, 
                          LPARAM lParam)
{
  return TRUE;
}

// -----------------------------------------------------
// Функция DlgProc_OnCommand
// -----------------------------------------------------
#pragma warning(disable: 4098)
void DlgProc_OnCommand(HWND hdlg, int id, 
  HWND hwndCtl, UINT codeNotify)
{
  switch (id)
  {
    case IDOK:
    {
      // Если пользователь нажал кнопку OK,
      // получаем текст из поля редактирования и
      // сохраняем его в глобальном буфере szWindowTitle
      GetDlgItemText(hdlg, IDC_EDIT1, szWindowTitle, 512);  
      
      // Завершаем работу диалоговой панели
      EndDialog(hdlg, 1);
      return TRUE;
    }

    // Если пользователь нажимает кнопку Cancel,
    // завершаем работу диалоговой панели
    case IDCANCEL:
    {
      // Завершаем работу диалоговой панели
      EndDialog(hdlg, 0);
      return TRUE;
    }
    default:
      break;
  }
  return FALSE;
}

Файл dllcall.h (листинг 3.5) содержит прототипы функций, определенных в приложении DLLCALL.

Листинг 3.5. Файл dlldemo\dllcall\dllcall.h


// -----------------------------------------------------
// Описание функций
// -----------------------------------------------------
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

void WndProc_OnCommand(HWND hWnd, int id, 
  HWND hwndCtl, UINT codeNotify);

void WndProc_OnDestroy(HWND hWnd);

HWND FindApplicationWindow(LPSTR lpszWindowTitle);

LRESULT WINAPI
DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);

BOOL DlgProc_OnInitDialog(HWND hdlg, HWND hwndFocus, 
                          LPARAM lParam);

void DlgProc_OnCommand(HWND hdlg, int id, 
  HWND hwndCtl, UINT codeNotify);

В файле dllcall.rc (листинг 3.6) определены ресурсы приложения DLLCALL. Это меню, две пиктограммы и диалоговая панель, а также текстовые строки, которые не используются.

Листинг 3.6. Файл dlldemo\dllcall\dllcall.rc


//Microsoft Developer Studio generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

//////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

//////////////////////////////////////////////////////////////
// Russian resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS)
#ifdef _WIN32
LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT
#pragma code_page(1251)
#endif //_WIN32

//////////////////////////////////////////////////////////////
//
// Menu
//

IDR_APPMENU MENU DISCARDABLE 
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "F&ind App Window",   ID_FILE_FINDWINDOW
        MENUITEM SEPARATOR
        MENUITEM "E&xit",              ID_FILE_EXIT
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&About...",          ID_HELP_ABOUT
    END
END

#ifdef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE DISCARDABLE 
BEGIN
    "\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED

//////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure 
// application icon
// remains consistent on all systems.
IDI_APPICON             ICON    DISCARDABLE     "Dllcall.ico"
IDI_APPICONSM           ICON    DISCARDABLE     "Dllcalsm.ico"

//////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_DLGFIND DIALOG DISCARDABLE  0, 0, 247, 74
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Find Application Window"
FONT 8, "MS Sans Serif"
BEGIN
    EDITTEXT        IDC_EDIT1,21,31,152,16,ES_AUTOHSCROLL
    DEFPUSHBUTTON   "OK",IDOK,190,7,50,14
    PUSHBUTTON      "Cancel",IDCANCEL,190,24,50,14
    GROUPBOX        "Enter application window title to find", 
                    IDC_STATIC,13,13,167,49
END

//////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE 
BEGIN
    IDD_DLGFIND, DIALOG
    BEGIN
        LEFTMARGIN, 7
        RIGHTMARGIN, 240
        TOPMARGIN, 7
        BOTTOMMARGIN, 67
    END
END
#endif    // APSTUDIO_INVOKED

#endif    // Russian resources
//////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//

//////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

Файл resource.h (листинг 3.7) содержит определения констант для файла описания ресурсов приложения.

Листинг 3.7. Файл dlldemo\dllcall\resource.h


//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by DLLCall.rc
//
#define IDR_MENU1                       101
#define IDR_APPMENU                     101
#define IDI_APPICON                     102
#define IDI_APPICONSM                   103
#define IDD_DLGFIND                     104
#define IDC_EDIT1                       1000
#define ID_FILE_EXIT                    40001
#define ID_HELP_ABOUT                   40002
#define ID_FILE_FINDWINDOW              40003

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        105
#define _APS_NEXT_COMMAND_VALUE         40004
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

Рассмотрим исходные тексты приложения DLLCALL.

Глобальные переменные и определения

В начале файла dllcall.c (листинг 3.4) мы определили тип MYDLLPROC как указатель на функцию, возвращающую знначение HWND и принимающую один параметр типа LPSTR.

Далее в области глобальных переменных мы определили переменную GetAppWindow с типом MYDLLPROC:


MYDLLPROC GetAppWindow;

Эта переменная будет использована для хранения указателя на функцию из динамически загружаемой DLL-библиотеки.

Глобальный буфер szWindowTitle предназначен для хранения заголовка окна, поиск которого будет выполнять наше приложение.

В глобальной переменной hDLL хранится идентификатор динамически загруженной DLL-библиотеки.

Функция WinMain

Функция WinMain сохраняет идентификатор прилождения в глобальной переменной hInst а затем проверяет, не было ли это приложение уже запущено. Если было, главное окно приложения выдвигается на передний план.

Далее функция WinMain регистрирует класс главного окна приложения, создает и отображает это окно. Затем запускается обычный цикл обработки сообщений.

Функция WndProc

Функция WndProc обрабатывает сообщения WM_COMMAND и WM_DESTROY, для чего вызываются, соответственно, функции WndProc_OnCommand и WndProc_OnDestroy.

Функция WndProc_OnDestroy

Эта функция вызывается при уничтожении главного окна приложения. Ее задачей является завершение цикла обработки сообщений, для чего она вызывает функцию PostQuitMessage.

Функция WndProc_OnCommand

Эта функция обрабатывает сообщение WM_COMMAND, которое приходит от главного меню приложения.

Когда пользователь выбирает из меню File строку Find App Window, с помощью функции DialogBox на экран выводится диалоговая панель, предназначенная для ввода заголовка окна, которое нужно найти.

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

В листинге мы подготовили два способа подключения DLL-библиотеки - прямой с использованием библиотеки экспорта и динамический.

Первый способ достаточно прост, однако предполагает, что в проект приложения DLLCALL будет включен файл библиотеки экспорта DLLDemo.LIB. Этот файл создается автоматически системой Microsoft Visual C++ при сборке проекта DLL-библиотеки.

Фрагмент кода, использующий прямое подключение, закрыт в листинге 3.4 символами комментария:


if(FindApplicationWindow(szWindowTitle) != NULL)
  MessageBox(NULL, "Application window was found",
    szAppTitle, MB_OK | MB_ICONINFORMATION);
else
  MessageBox(NULL, "Application window was not found",
    szAppTitle, MB_OK | MB_ICONINFORMATION);

В этом фрагменте мы выполняем простой вызов функции FindApplicationWindow, определенной в DLL-библиотеке DLLDemo.DLL. Прототип функции FindApplicationWindow мы поместили в файл dllcall.h.

Второй фрагмент загружает DLL-библиотеку при помощи функции LoadLibrary, а в случае успеха затем получает указатель на функцию FindApplicationWindow. Для получения указателя здесь применяется функция GetProcAddress:


hDLL = LoadLibrary("DLLDEMO.DLL");
if(hDLL != NULL)
{
  GetAppWindow = 
   (MYDLLPROC)GetProcAddress(hDLL, "FindApplicationWindow");
  if(GetAppWindow != NULL)
  {
    if(GetAppWindow(szWindowTitle) != NULL)
      MessageBox(NULL, "Application window was found",
        szAppTitle, MB_OK | MB_ICONINFORMATION);
    else
      MessageBox(NULL, "Application window was not found",
        szAppTitle, MB_OK | MB_ICONINFORMATION);
  }
  FreeLibrary(hDLL);
}

Проверяя работу этих двух фрагментов кода, обратите внимение на последовательность появления сообщений о подключении процесса к DLL-библиотеке и отключении от нее.

При использовании прямого подключения DLL-библиотеки сообщение о подключении процесса появляется сразу после запуска приложения DLLCALL, даже еще до появления на экране главного окна этого приложения. Это и понятно - DLL-библиотека отображается в адресное пространство процесса при его запуске. Если нужная DLL-библиотека не будет найдена, процесс так и не сможет запуститься. При этом на экране появится соответствующее системное сообщение.

Когда DLL-библиотека загружается динамически, сообщение о подключении процесса появляется только после того, как пользователь выберет строку Find App Window из меню File, так как только после этого произойдет подключение. Сообщение об отключении процесса появится после отображения результатов поиска окна, так как в этот момент будет вызвана функция FreeLibrary.

Функция DlgProc

Функция DlgProc обрабатывает сообщения WM_INITDIALOG и WM_COMMAND, поступающие в функцию диалога диалоговой панели, предназначенной для ввода заголовка окна. Эти сообщения обрабатываются, соответственно, функциями DlgProc_OnInitDialog и DlgProc_OnCommand.

Функция DlgProc_OnInitDialog

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

Функция DlgProc_OnCommand

Функция DlgProc_OnCommand обрабатывает сообщение WM_COMMAND, поступающее в функцию диалога от органов управления, расположенных в диалоговой панели.

Если пользователь нажимает кнопку OK, функция DlgProc_OnCommand извлекает содержимое однострочного текстового редактора (введенное имя заголовока окна), вызывая для этого макрокоманду GetDlgItemText, и сохраняет это содержимое в глобальном буфере szWindowTitle. Затем функция завершает работу диалоговой панели с кодом 1, в результате чего приложение приступит к поиску окна с заданным заголовком.

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

[Назад] [Содеожание] [Дальше]