Программирование для 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, работа диалоговой панели завершается с нулевым кодом. Поиск окна в этом случае не выполняется. |