Bài giảng lập trình windows

Măc̣ dù kỹ thuâṭ sƣ̉ duṇ g thƣ viêṇ liên kết đôṇ g theo kiểu này kh ông làm tăng hiêụ quả sƣ̉ duṇ g của hàm Rectangle nhƣng nó laị là môṭ cách hiêụ quả trong trƣờ ng hơp̣ mà chúng ta không biết tên của thƣ viêṇ cho tớ i khi chƣơng trình chaỵ (chẳng haṇ đối vớ i hàm AlphaBlend chẳng haṇ ). Đoaṇ mã trên sƣ̉ duṇ g hai hàm LoadLibrary và FreeLibrary . Windows kiểm soát các biến đếm tham chiếu tớ i tất cả các module thƣ viêṇ . Hàm LoadLibrary sẽ làm cho biến đếm tham chiếu tớ i các thƣ viêṇ đƣơc̣ nap̣ tăng lên 1. Biến đếm tham chiếu cũng đƣợc tăng lên khi Windows nap̣ môṭ chƣơng trình có sƣ̉ duṇ g thƣ viêṇ . FreeLibarary sẽ làm cho biến đếm này giảm đi 1, trƣờ ng hơp̣ môṭ instance của môṭ chƣơng trình sƣ̉ duṇ g thƣ viêṇ bi ̣loaị khỏi bô ̣nhớ biến đếm tham chiếu cũng giảm đi 1 đơn vi.̣ Khi biến đếm tham chiếu này bằng 0 Windows sẽ loại bỏ thƣ viện khỏi bộ nhớ vì lúc đó thƣ viện không còn cần thiết nữa.

pdf96 trang | Chia sẻ: huyhoang44 | Lượt xem: 804 | Lượt tải: 0download
Bạn đang xem trước 20 trang tài liệu Bài giảng lập trình windows, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
atus (SERVICE_STOP_PENDING, -1); break; case SERVICE_CONTROL_PAUSE: PauseFlag = TRUE; /* Interrogated periodically. */ break; case SERVICE_CONTROL_CONTINUE: PauseFlag = FALSE; break; case SERVICE_CONTROL_INTERROGATE: break; default: if (Control > 127 && Control < 256) /* User defined. */ Bài giảng môn học: Lâp̣ triǹh Windows 60 break; } UpdateStatus (-1, -1); /* Increment checkpoint. */ return; } /* This is the service-specific function, or "main," and is called from the more generic ServiceMain. In general, you can take any server, such as ServerNP.c, and rename "main" as "ServiceSpecific"; putting code right here. But some changes are required to update status. */ int ServiceSpecific (int argc, LPTSTR argv []) { UpdateStatus (-1, -1); /* Increment the checkpoint. */ /* ... Initialize system ... */ /* Be sure to update the checkpoint periodically. */ return 0; } 4. Quản lý các dịch vụ của Windows 4.1 Các phƣơng pháp kiểm soát các dịch vụ của Windows Để quản lý các dịch vụ của Windows ta có hai cách: một là dùng công cụ SCM, hai là gõ trực tiếp các lệnh trên dòng lệnh. 4.2 Ví dụ : Điều khiển các dic̣h vu ̣của Windows #include "EvryThng.h" static SC_HANDLE hScm; static BOOL Debug; int _tmain (int argc, LPTSTR argv []) { BOOL Exit = FALSE; TCHAR Command [MAX_COMMAND_LINE + 10], *pc; Bài giảng môn học: Lâp̣ triǹh Windows 61 DWORD i, LocArgc; /* Local argc. */ TCHAR argstr [MAX_ARG] [MAX_COMMAND_LINE]; LPTSTR pArgs [MAX_ARG]; /* Prepare the local "argv" array as pointers to strings. */ for (i = 0; i < MAX_ARG; i++) pArgs [i] = argstr [i]; /* Open the SC Control Manager on the local machine. */ hScm = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS); /* Main command processing loop. */ _tprintf (_T ("\nWindows Service Management")); while (!Exit) { _tprintf (_T ("\nSM$")); _fgetts (Command, MAX_COMMAND_LINE, stdin); ... Similar to JobShell ... if (_tcscmp (argstr [0], _T ("create")) == 0) { Create (LocArgc, pArgs, Command); } ... Similarly for all commands ... } CloseServiceHandle (hScm); return 0; } int Create (int argc, LPTSTR argv [], LPTSTR Command) { /* Create a new service as a "demand start" service: argv [1]: service Name argv [2]: display Name argv [3]: binary executable */ SC_HANDLE hSc; TCHAR CurrentDir [MAX_PATH + 1], Executable [MAX_PATH + 1]; Bài giảng môn học: Lâp̣ triǹh Windows 62 hSc = CreateService (hScm, argv [1], argv [2], SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, Executable, NULL, NULL, NULL, NULL, NULL); return 0; } /* Delete a service -- argv [1]: ServiceName to delete. */ int Delete (int argc, LPTSTR argv [], LPTSTR Command) { SC_HANDLE hSc; hSc = OpenService (hScm, argv [1], DELETE); DeleteService (hSc); CloseServiceHandle (hSc); return 0; } /* Start a named service -- argv [1]: service name to start. */ int Start (int argc, LPTSTR argv [], LPTSTR Command) { SC_HANDLE hSc; TCHAR WorkingDir [MAX_PATH + 1]; LPTSTR pWorkingDir = WorkingDir; LPTSTR argvStart [] = {argv [1], WorkingDir}; GetCurrentDirectory (MAX_PATH + 1, WorkingDir); hSc = OpenService(hScm, argv [1], SERVICE_ALL_ACCESS); /* Start the service with one arg, the working directory. */ /* Note: The service name agrees, by default, with the name */ /* associated with the handle, hSc, by OpenService. */ /* But, the ServiceMain function does not verify this. */ StartService (hSc, 2, argvStart); CloseServiceHandle (hSc); Bài giảng môn học: Lâp̣ triǹh Windows 63 return 0; } /* Control a named service. argv [1]: service name to control. argv [2]: Control command: stop, pause, resume, interrogate. */ static LPCTSTR Commands [] = {"stop," "pause," "resume," "interrogate," "user"}; static DWORD Controls [] = { SERVICE_CONTROL_STOP, SERVICE_CONTROL_PAUSE, SERVICE_CONTROL_CONTINUE, SERVICE_CONTROL_INTERROGATE, 128}; int Control (int argc, LPTSTR argv [], LPTSTR Command) { SC_HANDLE hSc; SERVICE_STATUS ServiceStatus; DWORD dwControl, i; BOOL Found = FALSE; for (i= 0; i < sizeof (Controls)/sizeof (DWORD) && !Found; i++) Found = (_tcscmp (Commands [i], argv [2]) == 0); if (!Found) { _tprintf (_T ("\nIllegal Control Command %s"), argv [1]); return 1; } dwControl = Controls [i - 1]; hSc = OpenService(hScm, argv [1], SERVICE_INTERROGATE | SERVICE_PAUSE_CONTINUE | SERVICE_STOP | SERVICE_USER_DEFINED_CONTROL | SERVICE_QUERY_STATUS); ControlService (hSc, dwControl, &ServiceStatus); if (dwControl == SERVICE_CONTROL_INTERROGATE) { QueryServiceStatus (hSc, &ServiceStatus); Bài giảng môn học: Lâp̣ triǹh Windows 64 printf (_T ("Status from QueryServiceStatus\n")); printf (_T ("Service Status\n")); ... Print all other status information ... } if (hSc != NULL) CloseServiceHandle (hSc); return 0; } Bài tập: Bài tập 1: Viết dịch vụ ghi lại nhật ký các lần login và sử dụng hệ thống trên Windows. Bài tập 2: Viết chƣơng trình quản lý các dịch vụ của Windows. Bài giảng môn học: Lâp̣ triǹh Windows 65 Chƣơng 6: Lâp̣ trình Socket 1. Khái niệm sockets trên Windows Socket: là một thực thể logic đại diện cho kết nối giữa các máy tính trên một hệ thống mạng. Tất cả các truyền thông giƣ̃a các máy tính trên môṭ hê ̣thống maṇg đƣơc̣ thƣc̣ hiêṇ thông qua socket . Có hai mô hình lập trình mạng : mô hình client /server và mô hình peer 2 peer (mạng ngang hàng). Theo mô hình client/server: Server: mở môṭ dic̣h vu ̣bằng cách taọ ra môṭ đối tƣơṇg ServerSocket với tham số là điạ chỉ cổng để kết nối , sau đó nhâṇ kết nối tƣ̀ phía client bằng cách sƣ̉ duṇg hàm accept (), lấy các luồng dữ liệu vào /ra của client để thƣc̣ hiêṇ truyền dƣ̃ liêụ , thƣc̣ hiêṇ các xƣ̉ lý cần thiết , trả kết quả cho client và đóng kết nối tới client, dịch vụ kết thúc. 2. Các hàm sockets phía server Các hàm socket phía server mà chúng ta cần quan tâm gồm có: int bind ( SOCKET s, const struct sockaddr *saddr, int namelen); int listen (SOCKET s, int nQueueSize); SOCKET accept ( SOCKET s, LPSOCKADDR lpAddr, LPINT lpAddrLen); Ví dụ: struct sockaddr_in SrvSAddr; /* Server address struct. */ struct sockaddr_in ConnectAddr; SOCKET SrvSock, sockio; ... SrvSock = socket (AF_INET, SOCK_STREAM, 0); SrvSAddr.sin_family = AF_INET; SrvSAddr.sin_addr.s_addr = htonl (INADDR_ANY); SrvSAddr.sin_port = htons (SERVER_PORT); bind (SrvSock, (struct sockaddr *) &SrvSAddr, sizeof SrvSAddr); listen (SrvSock, 5); AddrLen = sizeof (ConnectAddr); Bài giảng môn học: Lâp̣ triǹh Windows 66 sockio = accept (SrvSock, (struct sockaddr *) &ConnectAddr, &AddrLen); ... Receive requests and send responses ... shutdown (sockio); closesocket (sockio); 3. Các hàm sockets phía client Các hàm socket phía client cần phải quan tâm là: int connect ( SOCKET s, LPSOCKADDR lpName, int nNameLen); int send ( SOCKET s, const char * lpBuffer, int nBufferLen, int nFlags); Ví dụ: SOCKET ClientSock; ... ClientSock = socket (AF_INET, SOCK_STREAM, 0); memset (&ClientSAddr, 0, sizeof (ClientSAddr)); ClientSAddr.sin_family = AF_INET; ClientSAddr.sin_addr.s_addr = inet_addr (argv [1]); ClientSAddr.sin_port = htons (SERVER_PORT); ConVal = connect (ClientSock, (struct sockaddr *) &ClientSAddr, sizeof (ClientSAddr)); 4. Ứng dụng mang đơn giản 4.1 Phía server #define _NOEXCLUSIONS #include "EvryThng.h" #include "ClntSrvr.h" /* Defines request and response records. */ struct sockaddr_in SrvSAddr; Bài giảng môn học: Lâp̣ triǹh Windows 67 /* Server's socket address structure. */ struct sockaddr_in ConnectSAddr; /* Connected socket. */ WSADATA WSStartData; /* Socket library data structure. */ typedef struct SERVER_ARG_TAG { /* Server thread arguments. */ volatile DWORD number; volatile SOCKET sock; volatile DWORD status; /* Explained in main thread comments. */ volatile HANDLE srv_thd; HINSTANCE dlhandle; /* Shared library handle. */ } SERVER_ARG; volatile static ShutFlag = FALSE; static SOCKET SrvSock, ConnectSock; int _tmain (DWORD argc, LPCTSTR argv []) { /* Server listening and connected sockets. */ BOOL Done = FALSE; DWORD ith, tstatus, ThId; SERVER_ARG srv_arg [MAX_CLIENTS]; HANDLE hAcceptTh = NULL; HINSTANCE hDll = NULL; /* Initialize the WSA library, Ver 2.0, although 1.1 will work. */ WSAStartup (MAKEWORD (2, 0), &WSStartData); /* Open command library DLL if specified on command line. */ if (argc > 1) hDll = LoadLibrary (argv [1]); /* Initialize thread arg array. */ for (ith = 0; ith < MAX_CLIENTS; ith++) { srv_arg [ith].number = ith; srv_arg [ith].status = 0; srv_arg [ith].sock = 0; srv_arg [ith].dlhandle = hDll; srv_arg [ith].srv_thd = NULL; Bài giảng môn học: Lâp̣ triǹh Windows 68 } /* Follow standard server socket/bind/listen/accept sequence. */ SrvSock = socket (AF_INET, SOCK_STREAM, 0); SrvSAddr.sin_family = AF_INET; SrvSAddr.sin_addr.s_addr = htonl ( INADDR_ANY ); SrvSAddr.sin_port = htons ( SERVER_PORT ); bind (SrvSock, (struct sockaddr *) &SrvSAddr, sizeof SrvSAddr); listen (SrvSock, MAX_CLIENTS); /* Main thread becomes listening/connecting/monitoring thread. */ /* Find an empty slot in the server thread arg array. */ /* status values: 0 -- slot is free; 1 -- thread stopped; 2 -- thread running; 3 -- stop entire system. */ while (!ShutFlag) { for (ith = 0; ith < MAX_CLIENTS && !ShutFlag; ) { if (srv_arg [ith].status==1 || srv_arg [ith].status==3) { /* Thread stopped, normally or by shutdown request. */ WaitForSingleObject (srv_arg[ith].srv_thd INFINITE); CloseHandle (srv_arg[ith].srv_thd); if (srv_arg [ith].status == 3) ShutFlag = TRUE; else srv_arg [ith].status = 0; /* Free thread slot. */ } if (srv_arg [ith].status == 0 || ShutFlag) break; ith = (ith + 1) % MAX_CLIENTS; if (ith == 0) Sleep (1000); /* Break the polling loop. */ /* Alternative: use an event to signal a free slot. */ } /* Wait for a connection on this socket. */ /* Separate thread so we can poll the ShutFlag flag. */ hAcceptTh = (HANDLE)_beginthreadex (NULL, 0, AcceptTh, &srv_arg [ith], 0, &ThId); Bài giảng môn học: Lâp̣ triǹh Windows 69 while (!ShutFlag) { tstatus = WaitForSingleObject (hAcceptTh, CS_TIMEOUT); if (tstatus == WAIT_OBJECT_0) break; /* Connection made. */ } CloseHandle (hAcceptTh); hAcceptTh = NULL; /* Prepare for next connection. */ } _tprintf (_T ("Server shutdown. Wait for all srvr threads\n")); /* Terminate the accept thread if it is still running. * See the Web site for more detail on this shutdown logic. */ if (hDll != NULL) FreeLibrary (hDll); if (hAcceptTh != NULL) TerminateThread (hAcceptTh, 0); /* Wait for any active server threads to terminate. */ for (ith = 0; ith < MAX_CLIENTS; ith++) if (srv_arg [ith].status != 0) { WaitForSingleObject (srv_arg[ith].srv_thd, INFINITE); CloseHandle (srv_arg[ith].srv_thd); } shutdown (SrvSock, 2); closesocket (SrvSock); WSACleanup (); return 0; } static DWORD WINAPI AcceptTh (SERVER_ARG * pThArg) { /* Accepting thread that allows the main thread to poll the */ /* shutdown flag. This thread also creates the server thread. */ LONG AddrLen, ThId; AddrLen = sizeof (ConnectSAddr); pThArg->sock = accept (SrvSock, /* This is a blocking call. */ Bài giảng môn học: Lâp̣ triǹh Windows 70 (struct sockaddr *) &ConnectSAddr, &AddrLen); /* A new connection. Create a server thread. */ pThArg->status = 2; pThArg->srv_thd = (HANDLE) _beginthreadex (NULL, 0, Server, pThArg, 0, &ThId); return 0; /* Server thread remains running. */ } static DWORD WINAPI Server (SERVER_ARG * pThArg) /* Server thread function. Thread created on demand. */ { /* Each thread keeps its own request, response, and bookkeeping data structures on the stack. */ /* ... Standard declarations from serverNP omitted ... */ SOCKET ConnectSock; int Disconnect = 0, i; int (*dl_addr)(char *, char *); char *ws = " \0\t\n"; /* White space. */ GetStartupInfo (&StartInfoCh); ConnectSock = pThArg->sock; /* Create a temp file name. */ sprintf (TempFile, "%s%d%s", "ServerTemp", pThArg->number, ".tmp"); while (!Done && !ShutFlag) { /* Main command loop. */ Disconnect = ReceiveRequestMessage (&Request, ConnectSock); Done = Disconnect || (strcmp (Request.Record, "$Quit") == 0) || (strcmp (Request.Record, "$ShutDownServer") == 0); if (Done) continue; /* Stop this thread on "$Quit" or "$ShutDownServer". */ hTmpFile = CreateFile (TempFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, &TempSA, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); Bài giảng môn học: Lâp̣ triǹh Windows 71 /* Check for a DLL command. For simplicity, shared */ /* library commands take precedence over process commands. First, extract the command name. */ i = strcspn (Request.Record, ws); /* Length of token. */ memcpy (sys_command, Request.Record, i); sys_command [i] = '\0'; dl_addr = NULL; /* Will be set if GetProcAddress succeeds. */ if (pThArg->dlhandle != NULL) { /* Try server "in process." */ dl_addr = (int (*)(char *, char *)) GetProcAddress (pThArg->dlhandle, sys_command); if (dl_addr != NULL) __try { /* Protect server process from exceptions in DLL. */ (*dl_addr) (Request.Record, TempFile); } __except (EXCEPTION_EXECUTE_HANDLER { ReportError (_T ("Exception in DLL"), 0, FALSE); } } } if (dl_addr == NULL) { /* No in-process support. */ /* Create a process to carry out the command. */ /* ... Same as in serverNP ... */ } /* ... Same as in serverNP ... */ } /* End of main command loop. Get next command. */ /* End of command loop. Free resources; exit from the thread. */ _tprintf (_T ("Shutting down server# %d\n"), pThArg->number); shutdown (ConnectSock, 2); closesocket (ConnectSock); Bài giảng môn học: Lâp̣ triǹh Windows 72 pThArg->status = 1; if (strcmp (Request.Record, "$ShutDownServer") == 0) { pThArg->status = 3; ShutFlag = TRUE; } return pThArg->status; } 4.2 Phía client #define _NOEXCLUSIONS /* Required to include socket definitions. */ #include "EvryThng.h" #include "ClntSrvr.h" /* Defines request and response records. */ /* Message functions for request and response. */ /* ReceiveResponseMessage also prints the received messages. */ static DWORD SendRequestMessage (REQUEST *, SOCKET); static DWORD ReceiveResponseMessage (RESPONSE *, SOCKET); struct sockaddr_in ClientSAddr; /* Clients's socket address. */ int _tmain (DWORD argc, LPTSTR argv []) { SOCKET ClientSock = INVALID_SOCKET; REQUEST Request; /* See ClntSrvr.h. */ RESPONSE Response; /* See ClntSrvr.h. */ WSADATA WSStartData; /* Socket library data structure. */ BOOL Quit = FALSE; DWORD ConVal, j; TCHAR PromptMsg [] = _T ("\nEnter Command> "); TCHAR Req [MAX_RQRS_LEN]; TCHAR QuitMsg [] = _T ("$Quit"); /* Request: shut down client. */ TCHAR ShutMsg [] = _T ("$ShutDownServer"); /* Stop all threads. */ CHAR DefaultIPAddr [] = "127.0.0.1"; /* Local system. */ Bài giảng môn học: Lâp̣ triǹh Windows 73 /* Initialize the WSA library, Ver 2.0, although 1.1 will work. */ WSAStartup (MAKEWORD (2, 0), &WSStartData); /* Connect to the server. */ /* Follow the standard client socket/connect sequence. */ ClientSock = socket (AF_INET, SOCK_STREAM, 0); memset (&ClientSAddr, 0, sizeof (ClientSAddr)); ClientSAddr.sin_family = AF_INET; if (argc >= 2) ClientSAddr.sin_addr.s_addr = inet_addr (argv [1]); else ClientSAddr.sin_addr.s_addr = inet_addr (DefaultIPAddr); ClientSAddr.sin_port = htons (SERVER_PORT); /* Defined as 1070. */ connect (ClientSock, (struct sockaddr *) &ClientSAddr, sizeof (ClientSAddr)); /* Main loop to prompt user, send request, receive response. */ while (!Quit) { _tprintf (_T ("%s"), PromptMsg); /* Generic input, but command to server must be ASCII. */ _fgetts (Req, MAX_RQRS_LEN-1, stdin); for (j = 0; j <= _tcslen (Req); j++) Request.Record [j] = Req [j]; /* Get rid of the new line at the end. */ Request.Record [strlen (Request.Record) - 1] = '\0'; if (strcmp (Request.Record, QuitMsg) == 0 || strcmp (Request.Record, ShutMsg) == 0) Quit = TRUE; SendRequestMessage (&Request, ClientSock); ReceiveResponseMessage (&Response, ClientSock); } shutdown (ClientSock, 2); /* Disallow sends and receives. */ closesocket (ClientSock); WSACleanup (); Bài giảng môn học: Lâp̣ triǹh Windows 74 _tprintf (_T ("\n****Leaving client\n")); return 0; } 5. Windows Sockets 2.0 Là phiên bản mới nhất của Windows cho lập trình mạng theo mô hình socket. Bài tập: Bài tập 1: Viết chƣơng trình client/server cho phép client nhập vào một xâu, gửi sang server, server sẽ trả về số từ có trong xâu. Bài giảng môn học: Lâp̣ triǹh Windows 75 Chƣơng 7: Thƣ viêṇ liên kết đôṇg Các thƣ viện liên kết động Dynamic -link Libararies là môṭ trong các phần tƣ̉ quan troṇg nhất của hê ̣điều hành Windows . Hầu hết các thao tác truy câp̣ điã cƣ́ng trên Windows đều đƣơc̣ thƣc̣ hiêṇ bởi các chƣơng trình hoăc̣ các file liên kết đôṇg . Cho đến thời điểm này chúng ta đa ̃viết rất nhiều c ác chƣơng trình và bây giờ là lúc chúng ta xem xét việc viết các thƣ viện liên kết đôṇg. Rất nhiều nguyên tắc trong viêc̣ viết các chƣơng trình cũng đƣơc̣ áp duṇg trong viêc̣ viết các thƣ viêṇ song có môṭ số thay đổi quan troṇg. 7.1. Khái niệm và ứng dụng của thƣ viện liên kết động Nhƣ các baṇ thấy môṭ chƣơng trình trên Windows là môṭ file chaỵ thƣờng taọ ra môṭ hoăc̣ môṭ số cƣ̉a sổ chƣơng trình và sƣ̉ duṇg môṭ vòng lăp̣ thông điêp̣ để nhâṇ các thông tin input tƣ̀ ngƣời dùng . Các thƣ viện liên kết động thƣờng không phải là các file chạy trực tiếp và chúng thƣờng không nhận các thông điệp . Chúng thƣờng là các file riêng biệt chứa các hàm có thể đƣợc gọi bởi các chƣơng trình và các thƣ viện khác để thực hiện một công việc cụ thể nào đó . Môṭ thƣ viêṇ liên kết đôṇg thƣờng đƣơc̣ nap̣ vào môṭ nhớ để thƣc̣ hiêṇ khi môṭ module chƣơng trình khác gọi tới một hàm trong thƣ viện. Thuâṭ ngƣ̃ liên kết đô ̣ ng “dynamic link” đề câp̣ tới quá trình mà Windows sƣ̉ duṇg để liên kết môṭ lời goị hàm trong môṭ module chƣơng trình với các hàm thƣc̣ sƣ ̣nằm trong môṭ thƣ viêṇ liên kết đôṇg. Các liên kết tĩnh đƣợc thực hiện khi trình biên dịch tiến hành bƣớc liên kết (link) các file object (.obj), các thƣ viện run-time (*.lib) và sử dụng một chƣơng trình biên dịch tài nguyên để tạo thành một file chạy .exe. Quá trình liên kết động không diễn ra vào lúc biên dic̣h và liên kết chƣơng trình mà diễn ra vào lúc chƣơng trình chạy. 7.2. Hệ thống thƣ viện liên kết động của Windows Các thƣ viện Kernel 32.dll, user32.dll và gdi 32.dll, rất nhiều các file điều khiển khác chẳng haṇ nhƣ keyboard .drv, system.drv và mouse .drv cũng nhƣ các trình điều khiển card màn hình và máy in đều là các thƣ viện liên kết động . Các thƣ viện này đều alf các thƣ viện mà hầu hết các chƣơng trình trên Windows đều sử dụng. Môṭ vài thƣ viêṇ liên kết động (chẳng haṇ nhƣ các font chƣ̃) đƣơc̣ goị là các thƣ viêṇ tài nguyên (resource only ). Các thƣ viện này chỉ chứa dữ liệu (thƣờng là dƣới daṇg các tài nguyên) và không chứa mã chƣơng trình . Do đó môṭ trong các muc̣ đích của c ác thƣ viện liên kết đôṇg là cung cấp các hàm và các tài nguyên có thể đƣơc̣ sƣ̉ duṇg bởi các chƣơng trình khác. Trong các hê ̣điều hành ngày xƣa (kiểu nhƣ DOS) chỉ có hệ điều hành mới chứa các thủ tục mà các chƣơng trìn h khác có thể goị đến để hoàn thành môṭ công viêc̣ gì đó . Trên Windows quá trình môṭ module chƣơng trình goị tới môṭ hàm trong môṭ module chƣơng trình khác là rất thƣờng xuyên . Bằng cách viết các Dll chúng ta có thêm các mở rôṇg cho hê ̣điều hành. Măc̣ dù môṭ module thƣ viêṇ liên kết đôṇg có thể có bất cƣ́ phần tên mở rôṇg nào (chẳng haṇ nhƣ .exe hay .com) nhƣng phần tên mở rôṇg chuẩn của các thƣ viêṇ liên kết đôṇg trên Windows là .dll. Chỉ có các thƣ viện liên kết động có phần tên mở rộng là dll mới đƣợc tự đôṇg nap̣ vào bô ̣nhớ bởi Windows các thƣ viêṇ có phần tên mở rôṇg khác se ̃đƣơc̣ nap̣ thông qua viêc̣ goị tới các hàm LoadLibrary hoăc̣ LoadLibraryEx . Chúng ta th ƣờng thấy các thƣ viện liên kết động đƣợc sử dụng trong các chƣơng trình lớn. Chẳng haṇ nhƣ chúng ta viết môṭ gói phần mềm kế toán lớn chƣ́a môṭ vài chƣơng trình khác nhau. Các chƣơng trình này sử dụng nhiều thủ tục chung do đó chúng ta có thể kết hơp̣ Bài giảng môn học: Lâp̣ triǹh Windows 76 các hàm này vào một thƣ viện liên kết tĩnh (*.lib) sau đó thêm vào mỗi module chƣơng trình trong quá trình biên dic̣h các chƣơng trình này . Tuy nhiên cách tiếp câṇ đó se ̃là lañg phí vì mỗi chƣơng trình se ̃chƣ́a các đoaṇ ma ̃giống nhau và hơn nƣ̃a nếu chúng ta thay đổi môṭ hàm nào đó trong thƣ viện chúng ta sẽ phải liên kết lại tất cả các chƣơng trình có sử dụng thƣ viện đó. Nhƣng nếu chúng ta cho tất cả các hàm đó vào môṭ thƣ viêṇ liên kết đôṇg chẳng haṇ nhƣ account.dll chẳng haṇ thì se ̃giải quyết đƣơc̣ cả hai vấn đề trên . Chỉ có module cần thiết chứa tất cả các ma ̃của các hàm đƣơc̣ sƣ̉ duṇg bởi tất cả các chƣơng trình điều nà y se ̃đòi hỏi ít không gian điã cƣ́ng hơn và đồng thời đòi hỏi ít bô ̣nhớ hơn khi nhiều chƣơng trình cùng chaỵ đồng thời, hơn nƣ̃a chúng ta có thể thay đổi các cài đăṭ của các hàm trong thƣ viêṇ mà không cần thiết phải liên kết laị thƣ viêṇ với các chƣơng trình. Các thƣ viện liên kết động cũng có thể là cá sản phẩm thƣơng mại , hiêṇ nay có rất nhiều hãng chuyên cung cấp các hàm thƣ viện dành cho phát triển một loại sản phẩm phần mềm nào đó chẳng haṇ nhƣ DirectX là môṭ tâp̣ các thƣ viêṇ liên kết đôṇg dành cho viêc̣ viết các chƣơng trình đồ hoạ cao cấp. Thƣ viêṇ: môṭ lời ngàn ý. (One word, many meanings) Môṭ phần các nhầm lâñ đối với các thƣ viêṇ liên kết đôṇg là việc sử dụng của thuật ngữ thƣ viêṇ (library) trong môṭ số các khung cảnh khác nhau . Bên caṇh các thƣ viêṇ liên kết đôṇg chúng ta còn nói đến các thƣ viêṇ đối tƣơṇg và các thƣ viêṇ import. Môṭ thƣ viêṇ đối tƣơṇg là môṭ file có phần mở rôṇg là .lib chƣ́a các đoaṇ ma ̃chƣơng trình sẽ đƣợc thêm vào file .exe trong quá trình liên kết tiñh . Chẳng haṇ trong VC++ thƣ viêṇ đối tƣơṇg run-time thƣờng liên kết với các chƣơng trình là libc.lib. Môṭ thƣ viện import là một dạng đặc biệt của các file thƣ viện đối tƣợng . Giống nhƣ các thƣ viêṇ đối tƣơṇg các thƣ viêṇ import có phần mở rôṇg là .lib và đƣơc̣ sƣ̉ duṇg bởi trình biên dịch để giải quyết các lời gọi hàm trong các c hƣơng trình của chúng ta viết ra . Tuy nhiên các thƣ viêṇ import này không chƣ́a các ma ̃chƣơng trình . Thay vào đó chúng cung cấp cho trình liên kết các thông tin cần thiết đẻ thiết lâp̣ các bảng chuyển (relocation table) trong file .exe để thƣc̣ hiêṇ các liên kết đôṇg . Các file kernel 32.lib, user32.lib, gdi32.lib đi kèm với các trình biên dic̣h của Microsoft đều là các thƣ viêṇ import . Nếu chúng ta goị tới môṭ hàm chẳng haṇ nhƣu Rectangle thì file gdi 32.lib sẽ báo cho trình liên kết biết rằng đó là một hàm trong thƣ viêṇ liên kết đôṇg gdi 32.dll. Thông tin này se ̃đƣơc̣ chƣ́a trong file chƣơng trình (*.exe) để Windows có thể thƣc̣ hiêṇ các liên kết đôṇg khi chƣơng trình của chúng ta đƣơc̣ thƣc̣ hiêṇ. Các thƣ viện đối tƣợng và các thƣ viện import đều đƣợc sử dụng trong quá trình phát triển các chƣơng trình . Các thƣ viện liên kết động đƣợc sử dụng trong quá trình chƣơng trình chạy. Môṭ thƣ viêṇ liên kết đôṇg phải sẵn có trên đĩa để chƣơng trình có thể sử dụng nó khi chạy. Khi Windows cần nap̣ môṭ module DLL trƣớc khi chaỵ môṭ chƣơng trình đòi hỏi sƣ̉ dụng hàm trong nó file thƣ viện phải nằm cùng thƣ mục với file .exe, trong thƣ muc̣ hiêṇ thời, trong thƣ muc̣ system của hê ̣điều hành hoăc̣ nằm trong thƣ muc̣ có trong biến môi trƣờng PATH của hê ̣điều hành (hê ̣điều hành se ̃tiến hành tìm kiếm thƣ viêṇ theo thƣ́ tƣ ̣đó). 7.3. Các bƣớc tạo một thƣ viện DLL Măc̣ dù ý tƣởng của các thƣ viện liên kết động là có thể sử dụng với nhiều ứng dụng nhƣng chúng ta se ̃viết môṭ ƣ́ng duṇg demo sƣ̉ duṇg môṭ thƣ viêṇ liên kết đôṇg đơn giản . Bài giảng môn học: Lâp̣ triǹh Windows 77 Chúng ta sẽ tạo một một thƣ viện liên kết động là edrlib .dll (Easy drawing routine ). Hàm thƣ viện này sẽ chỉ chứa một hàm đơn giản để thực hiện công việc vẽ ra một xâu trong ứng dụng demo của chúng ta. Để taọ ra môṭ thƣ viêṇ liên kết đôṇg chúng ta cần có môṭ cách tiếp câṇ khác s o với cách mà chúng ta vẫn dùng để viết các ứng dụng . VC++ phân biêṭ giƣ̃a các khái niêṃ “workspace” và “project” . Môṭ project thƣờng là môṭ ƣ́ng duṇg hoăc̣ môṭ thƣ viêṇ liên kết đôṇg . Môṭ workspace có thể gồm nhiều project . Cho đến thời điểm này chúng ta mới chỉ viết các workspace chỉ có 1 project. Trong phần này chúng ta se ̃taọ môṭ workspace có hai project , môṭ cho viêc̣ taọ dll và môṭ cho viêc̣ goị tới file dll đó. Các bƣớc tạo ứng dụng với Visual Studio .NET 2003 nhƣ sau: 1. Tạo 1 Solution (Dll1 chẳng haṇ) rỗng (Blank Solution) 2. Thêm môṭ project chƣ́a file dll cho ƣ́ng duṇg (chọn New  Project  Win32 Project) và gõ tên của Project là SimpleDll. 3. Trong hôp̣ thoaị Application Setting chọn DLL (Application Type) và chọn mục Empty. 4. Thêm file ma ̃nguồn cho Project và gõ nôị dung của file dll vào: /*---------------------- simpledll.h header file ----------------------*/ #ifdef __cplusplus #define EXPORT extern "C" __declspec (dllexport) #else #define EXPORT __declspec (dllexport) #endif EXPORT BOOL CALLBACK EdrCenterTextA (HDC, PRECT, PCSTR) ; EXPORT BOOL CALLBACK EdrCenterTextW (HDC, PRECT, PCWSTR) ; #ifdef UNICODE #define EdrCenterText EdrCenterTextW #else #define EdrCenterText EdrCenterTextA #endif Bài giảng môn học: Lâp̣ triǹh Windows 78 /*------------------------------------------------- simpledll.c -- Easy Drawing Routine Library module (c) Charles Petzold, 1998 -------------------------------------------------*/ #include #include "simpledll.h" int WINAPI DllMain (HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved) { return TRUE ; } EXPORT BOOL CALLBACK EdrCenterTextA (HDC hdc, PRECT prc, PCSTR pString) { int iLength ; SIZE size ; iLength = lstrlenA (pString) ; GetTextExtentPoint32A (hdc, pString, iLength, &size) ; return TextOutA (hdc, (prc->right - prc->left - size.cx) / 2, (prc->bottom - prc->top - size.cy) / 2, pString, iLength) ; } EXPORT BOOL CALLBACK EdrCenterTextW (HDC hdc, PRECT prc, PCWSTR pString) { int iLength ; SIZE size ; Bài giảng môn học: Lâp̣ triǹh Windows 79 iLength = lstrlenW (pString) ; GetTextExtentPoint32W (hdc, pString, iLength, &size) ; return TextOutW (hdc, (prc->right - prc->left - size.cx) / 2, (prc->bottom - prc->top - size.cy) / 2, pString, iLength) ; } 5. Tạo một Project Win 32 Project, sau đó choṇ kiểu Project là Application, tạo file .c cho ƣ́ng duṇg và gõ nôị dung ƣ́ng duṇg vào nhƣ sau: #include #include "..\\SimpleDll\\simpledll.h" LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] = TEXT ("StrProg") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; Bài giảng môn học: Lâp̣ triǹh Windows 80 return 0 ; } hwnd = CreateWindow (szAppName, TEXT ("DLL Demonstration Program"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc ; PAINTSTRUCT ps ; RECT rect ; switch (message) { case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; GetClientRect (hwnd, &rect) ; EdrCenterText (hdc, &rect, TEXT ("This string was displayed by a DLL")) ; Bài giảng môn học: Lâp̣ triǹh Windows 81 EndPaint (hwnd, &ps) ; return 0 ; case WM_DESTROY: PostQuitMessage (0) ; return 0 ; } return DefWindowProc (hwnd, message, wParam, lParam) ; } 6. Đặt chế độ dịch là Project thứ hai phụ thuôc̣ (dependencies) vào Project thƣ́ nhất và thƣ muc̣ Output cùng taṃ của hai Project là ../Release. 7. Dịch và chạy chƣơng trình. Ở đây chúng ta có hai phiên bản của một hàm trong thƣ viện liên kết động đƣợc sử dụng, điều này cho phép dùng các hàm có hỗ trợ Unicode trong trƣờng hợp hệ thống có hỗ trợ và ngƣợc lại sử dụng một hàm không có Unicode , thƣờng tên của hàm se ̃có thêm chƣ̃ W nếu có hỗ trợ Unicode và A nếu không. Đồng thời chúng ta cũng t hấy trong ma ̃của thƣ viêṇ có môṭ hàm DllMain , hàm này có vai trò tƣơng tƣ ̣nhƣ hàm WinMain trong môṭ chƣơng trình . Tác dụng của hàm DllMain là khởi taọ và thu hồi bô ̣nhớ và nhƣ̃ng thƣ́ liên quan khác , chúng ta sẽ bàn tới vấn đề này ở cuối chƣơng, hiêṇ taị chỉ cần return TRUE là ổn. Điều bí ẩn còn laị có le ̃là ở điṇh danh EXPORT . Các hàm trong một thƣ viện liên kết đôṇg đƣơc̣ sƣ̉ duṇg bởi các ƣ́ng duṇg khác phải đƣơc̣ xuất khẩu . Điều này không liên quan tới các vấn đề thƣơng mại thông thƣờng mà chỉ là một chỉ thị đảm bảo trình biên dịch sẽ thêm tên hàm vào thƣ viện import simpledll .lib để trình liên kết có thể đƣa các thông tin phù hơp̣ vào chƣơng trình (file *.exe) để có thể nạp các thƣ viện dll khi chƣơng trình chạy . Điṇh danh EXPORT còn bao gồm chỉ điṇh lớp chƣ́a __declspec(dllexport) và một chỉ thị tiền xử lý if extern “C” để đề phòng trƣờng hơp̣ file header đƣơc̣ biên dic̣h theo kiểu C++. Điều này ngăn chăṇ trình biên dic̣h khỏi các lỗi trùng tên của các hàm C ++ và cho phép các thƣ viện liên kết đôṇg có thể đƣơc̣ sƣ̉ duṇg bởi cả các chƣơng trình C và C++. Điểm vào và điểm thoát của thƣ viêṇ (Entry and Exit Point) Hàm DllMain đƣợc gọi đến khi thƣ viện liên kết động lần đầu tiên đƣợc nạp vào bộ nhớ để thực hiện và khi nó kết thúc nhiệm vụ (bị loại khỏi bộ nhớ ). Tham số đầu tiên của hàm DllMain là handle tới instance của th ƣ viêṇ. Nếu nhƣ thƣ viêṇ có sƣ̉ duṇg các tài nguyên đòi hỏi một handle instance (chẳng haṇ nhƣ các hôp̣ thoaị ), chúng ta nên lƣu lại hInstance vào môṭ biến toàn cuc̣ . Tham số cuối cùng của hàm DllMain đƣơc̣ dƣ̃ trƣ ̣dành cho hê ̣t hống sƣ̉ dụng. Tham số fdwReason có thể là môṭ trong 4 giá trị chỉ ra tại sao Windows lại gọi tới hàm DllMain. Trong các muc̣ tiếp theo chúng ta nên nhớ rằng môṭ chƣơng trình đơn có thể đƣơc̣ Bài giảng môn học: Lâp̣ triǹh Windows 82 nạp nhiều lần và chạy đồng thời t rên Windows. Mỗi lần chƣơng trình đƣơc̣ nap̣ nó đƣơc̣ xem nhƣ là môṭ tiến trình (process) riêng rẽ. Giá trị của tham số fdwReason bằng DLL _PROCESS_ATTACH chỉ ra rằng thƣ viêṇ liên kết đôṇg đa ̃đƣơc̣ ánh xa ̣vào vùng điạ chỉ của mô ̣ t tiến trình . Đây là môṭ đầu mối cho phép thƣ viện thực hiện bất cứ khởi tạo nào đòi hỏi đƣợc phục vụ cho các yêu cầu tiếp theo của tiến trình. Các khởi tạo kiểu này có thể là cấp phát bộ nhớ chẳng hạn . Trong thời gian tiến trình đang chạy , DllMain đƣơc̣ goị với môṭ tham số DLL _PROCESS_ATTACH chỉ môṭ lần trong cả thời gian tồn taị của process đó . Bất cƣ́ môṭ tiến trình nào khác sƣ̉ duṇg cùng file DLL se ̃goị đến hàm DllMain với môṭ giá trị tham số DLL_PROCESS_ATTACH. Nếu nhƣ viêc̣ khởi taọ là thành công DllMain se ̃trả về môṭ giá tri ̣ khác 0, giá trị trả về là 0 sẽ làm cho Windows không chạy chƣơng trình. Nếu giá tri ̣ của fdwReason bằng DLL _PROCESS_DETACH thì có nghĩa là chƣơng trình không cần file DLL nữa , và đây là một cơ hội để thƣ viện thực hiện các công việc dọn dẹp của nó . Trên các hê ̣điều hành 32 bit của Windows điều này không thƣc̣ sƣ ̣cần thiết nhƣng là môṭ thói quen lâp̣ trình tốt. Tƣơng tƣ ̣khi hàm DllMain đƣơc̣ goị với môṭ giá tri ̣ tham số là DLL_THREAD_ATTACH thì có nghiã là môṭ tiến trình sƣ̉ duṇg thƣ viêṇ đa ̃taọ ra môṭ luồng (thread) mới. Khi luồng kết thúc Windows laị goị tới hàm DllMain với tham số là DLL_THREAD_DETACH. Cũng có thể xảy ra trƣờng hợp Windows thực hiện lời gọi tới hàm DllMain với giá trị của tham số fdwReason bằng DLL _THREAD_DETACH mà không thƣc̣ hiêṇ lời goị với giá tri ̣ DLL _THREAD_ATTACH trƣớc đó nếu nhƣ thƣ viêṇ liên kết đôṇg đƣơc̣ gắn với môṭ tiến trình sau khi luồng đa ̃đƣơc̣ taọ ra. Luồng vâñ tồn taị khi hàm DllMain đƣơc̣ goị đến với tham số DLL_THREAD_DETACH. Nó thậm chí có thể gửi các thông điệp trong tiến trì nh. Nhƣng các luồng không nên sử dụng hàm PostMessage () vì luồng có thể kết thúc trƣớc khi thông điêp̣ đến đích. Chƣơng trình để test thƣ viêṇ liên kết đôṇg là môṭ chƣơng trình đơn giản và là môṭ Project khác (thuôc̣ loaị Win 32 Application). Chúng ta có thể để các file của 2 Project vào cùng một thƣ mục hoặc riêng rẽ trong 2 thƣ muc̣. Trong quá trình dic̣h chƣơng trình file simpledll .dll và simpledll .lib se ̃đƣơc̣ sinh ra trƣớc, file simpledll .lib se ̃đƣơc̣ tƣ ̣ đôṇg liên kết với chƣơng trình thƣ̉ nghiêṃ và file simpledll.dll se ̃đƣơc̣ nap̣ vào bô ̣nhớ khi chƣơng trình chaỵ . Cần chú ý là chƣơng trình usedll.exe không chƣ́a ma ̃của hàm sƣ̉ duṇg trong thƣ viêṇ , mã của hàm chỉ đƣợc nạp vào b ộ nhớ khi chƣơng trình chaỵ. Viêc̣ include file simpledll .h cũng giống nhƣ chúng ta include file windows .h, liên kết với file simpledll .lib cũng tƣơng tƣ ̣nhƣ liên kết với file user 32.lib và liên kết với file simpledll.dll cũng giống nhƣ chƣơng trình liên kết với file user32.dll. Măc̣ dù chúng ta xếp môṭ file DLL là môṭ mở rôṇg của Windows nhƣng nó cũng là môṭ mở rôṇg của chƣơng trình ƣ́ng duṇg của chúng ta . Tất cả nhƣ̃ng gì file DLL thƣc̣ hiêṇ đều là thay măṭ cho ƣ́ng duṇg sƣ̉ duṇg nó . Chẳng haṇ tất cả các thao tác cấp phát bô ̣nhớ đƣơc̣ kiểm soát bởi chƣơng trình . Bất cƣ́ cƣ̉a sổ nào nó taọ ra đều sở hƣ̃u bởi chƣơng trình và bất cƣ́ file nào đƣợc mở cũng đƣợc kiểm soát bởi ch ƣơng trình. Nhiều chƣơng trình có thể sƣ̉ duṇg cùng Bài giảng môn học: Lâp̣ triǹh Windows 83 môṭ file dll đồng thời , nhƣng Windows se ̃đóng vai trò lá chắn để ngăn chăṇ các can thiêp̣ lâñ nhau giƣ̃a các ƣ́ng duṇg này. Nhiều tiến trình có thể chia sẻ cùng môṭ môṭ đoaṇ ma ̃trong môṭ thƣ viêṇ liên kết đôṇg . Tuy nhiên dƣ́ liêụ đƣơc̣ sƣ̉ đuṇg bởi môṭ thƣ viêṇ DLL se ̃là khác nhau với các tiến trình khác nhau. Mỗi tiến trình có môṭ không gian điạ chỉ dƣ̃ liêụ của riêng nó để chƣ́a các dƣ̃ li ệu có thể sƣ̉ duṇg đến bởi file DLL . Chia sẻ bô ̣nhớ giƣ̃a các tiến trình đòi hỏi môṭ số kỹ thuâṭ khác mà chúng ta sẽ bàn tới trong phần tiếp theo. 7.4. Chia sẻ bô ̣nhớ giƣ̃a các thƣ viêṇ liên kết đôṇg Windows cô lâp̣ các ƣ́ ng duṇg sƣ̉ duṇg cùng môṭ thƣ viêṇ liên kết đôṇg đồng thời . Tuy nhiên đôi khi đây không phải là môṭ lƣạ choṇ thích hơp̣ . Chúng ta có thể muốn viết một thƣ viêṇ DLL chƣ́a môṭ vùng nhớ nào đó có thể đƣơc̣ chia sẻ bởi nhiều ƣ́n g duṇg, hoăc̣ giƣ̃a các instance của cùng môṭ ƣ́ng duṇg. Điều này liên quan tới viê ̣sƣ̉ duṇg bô ̣nhớ chia sẻ , hay chính xác là một file ánh xạ bộ nhớ (memory-mapped file). Chúng ta sẽ khảo sát một chƣơng trình có tên là strprog (String Program ) và thƣ viện đƣơc̣ sƣ̉ duṇg là strlib (string library). Strlib chƣ́a 3 hàm mà strprog có thể gọi tới . Và một trong số các hàm của strlib se ̃sƣ̉ duṇg môṭ hàm call-back đƣơc̣ điṇh nghiã trong strprog. Strlib là môṭ thƣ viêṇ liên kết đôṇg chƣ́a và làm viêc̣ với môṭ xâu tối đa 256 ký tự. Xâu đƣơc̣ chuyển thành daṇg ký tƣ ̣hoa và kiểm soát trong vùng bô ̣nhớ chia sẻ của strlib . Strprog có thể sử dụng 3 hàm của thƣ viện Strlib để cộng , xóa và nhâṇ đƣơc̣ tất cả các xâu hiêṇ taị tƣ̀ strlib. Chƣơng trình strprog có hai muc̣ menu để lƣạ choṇ là Enter và Delete cho phép ngƣời dùng nhập các xâu để thực hiện việc cộng và xóa các xâu . Strprog se ̃in ra giá tri ̣ tất cả cá c xâu hiêṇ đang nằm chƣ́a trong thƣ viêṇ. Các hàm đƣợc định nghĩa trong thƣ viện Strlib gồm có: EXPORT BOOL CALLBACK AddString (pStringIn) EXPORT BOOL CALLBACK DeleteString (pStringIn) EXPORT int CALLBACK GetStrings (pfnGetStrCallBack, pParam) Hàm thứ nhất sẽ chuyển các xâu thành dạng ký tự hoa và thêm vào danh sách các xâu của thƣ viện. Giá trị trả về của hàm là TRUE (khác 0) nếu nhƣ thành công và FALSE (0) nếu nhƣ xảy ra lỗi : hoăc̣ xâu có đô ̣dài bằng 0 hoăc̣ không cấp phát đƣơc̣ bô ̣nhớ hoăc̣ đa ̃sƣ̉ duṇg hết 256 xâu của bô ̣nhớ. Hàm thứ hai sẽ thực hiện xóa bỏ một xâu khỏi danh sách các xâu của thƣ viện nếu khớp và nếu có nhiều xâu khớp thì chỉ xâu đầu tiên bị xóa . Kết quả trả về của hàm là TRUE nếu xóa bỏ thành công và FALSE nếu xâu cần xóa có độ dài bằng 0 hoăc̣ không tìm thấy. Hàm thứ ba là hàm sử dụng để liệt kê các xâu đang có trong thƣ viện , hàm này sử dụng môṭ tham số là môṭ hàm cal l-back chƣ́a trong chƣơng trình sƣ̉ duṇg hàm . Hàm này phải đƣợc điṇh nghiã trong chƣơng trình sƣ̉ duṇg thƣ viêṇ nhƣ sau: EXPORT BOOL CALLBACK GetStrCallBack (PSTR pString, PVOID pParam) Tham số pfnGetStrCallBack của hàm GetString chỉ tới m ột hàm call-back. GetString có thể goị tới hàm GetStrCallBack mỗi lần cho mỗi giá tri ̣ của môṭ xâu cho tới khi hàm này trả về FALSE. GetString se ̃trả về số lƣơṇg các xâu đƣơc̣ truyền cho hàm call -back. Biến pParam sẽ là một con trỏ far tới dƣ̃ liêụ đƣơc̣ ngƣời dùng điṇh nghiã. Bài giảng môn học: Lâp̣ triǹh Windows 84 Tất nhiên ở đây chúng ta cũng sƣ̉ duṇg hai phiên bản cho mỗi hàm , ANSI và Unicode version. 7.5. Các vấn đề khác về thƣ viện liên kết động Tôi đa ̃tƣ̀ng đề câp̣ trong phần đầu củ a chƣơng này là các thƣ viêṇ liên kết đôṇg không nhâṇ các thông điêp̣ . Tuy nhiên môṭ thƣ viêṇ liên kết đôṇg có thể goị tới hàm GetMessage và PeekMessage. Các thông điệp mà thƣ viện lấy về từ hàng đợi thông điệp qua các hàm nà y thƣc̣ sƣ ̣là các thông điêp̣ của các chƣơng trình goị tới các hàm của thƣ viêṇ . Nói chung thƣ viêṇ làm viêc̣ thay măc̣ cho chƣơng trình goị nó – môṭ qui luâṭ chi phối hầu hết các hàm của Windows mà môṭ thƣ viêṇ có thể goị tới. Môṭ thƣ viêṇ liên kết đôṇg có thể nap̣ các tài nguyên (chẳng haṇ nhƣ các biểu tƣơṇg chƣơng trình, các xâu và các ảnh bitmap) tƣ̀ file thƣ viêṇ hoăc̣ tƣ̀ các file của chƣơng trình goị tới thƣ viêṇ đó. Các hàm nạp tài nguyên đòi hỏi môṭ handle tới instance . Nếu nhƣ thƣ viêṇ sƣ̉ dụng handle tới instance của riêng nó (đƣơc̣ truyền cho thƣ viêṇ qua viêc̣ goị tới hàm DllMain để khởi tạo nó ) thì thƣ viện có thể nhận đƣợc các tài nguyên từ file riêng c ủa nó. Để nap̣ các tài nguyên từ chƣơng trình gọi tới các hàm của thƣ viện , đòi hỏi handle tới instance của chƣơng trình goị hàm. Viêc̣ khai báo các lớp cƣ̉a sổ chƣơng trình và taọ ra cƣ̉a sổ chƣơng trình trong môṭ thƣ viêṇ đ òi hỏi một số thủ thuật . Cả cấu trúc lớp cửa sổ và hàm CreateWindow hoặc CreateWindowEx đều đòi hỏi môṭ handle tới môṭ instance của chƣơng trình . Măc̣ dù chúng ta có thể sử dụng hande của thƣ viện trong việc tạo ra các lớ p cƣ̉a sổ và cƣ̉a sổ chƣơng trình , các thông điêp̣ cƣ̉a sổ vâñ đi qua hàng đơị thông điêp̣ của chƣơng trình goị tới thƣ viêṇ khi thƣ viêṇ taọ ra cƣ̉a sổ chƣơng trình . Nếu nhƣ baṇ cần phải taọ ra các lớp cƣ̉a sổ và các cƣ̉a sổ chƣơng trình trong môṭ thƣ viêṇ thì tốt nhất là nên sƣ̉ duṇg handle tới instance của chƣơng trình gọi tới hàm thƣ viện. Vì các thông điệp cho các hộp thoại modal đƣợc nhận bên ngoài vòng lặp thông điệp của chƣơng trình nên chún g ta có thể taọ ra môṭ hôp̣ thoaị modal trong môṭ thƣ viêṇ bằng cách gọi tới hàm DialogBox . Handle tới instance có thể là của thƣ viêṇ hoăc̣ tham số hwndParent của hàm DialogBox có thể đăṭ bằng NULL . Các thƣ viện không có import Thay vì để Windows thƣc̣ hiêṇ viêc̣ liên kết đôṇg khi chƣơng trình lần đầu tiên đƣơc̣ nạp vào bộ nhớ chúng ta có thể liên kết một chƣơng trình với một thƣ viện khi chƣơng trình đang chaỵ. Chẳng haṇ chúng ta muốn goị tới hàm Rectangle nhƣ sau: Rectangle (hdc, xLeft, yTop, xRight, yBottom) ; Điều này se ̃làm cho chƣơng trình đƣơc̣ liên kết tới thƣ viêṇ gdi 32.lib khi biên dic̣h để lấy điạ chỉ của hàm Rectangle. Chúng ta có thể gọi tới hàm Rectangle theo một cách khác: typedef BOOL (WINAPI * PFNRECT) (HDC, int, int, int, int) ; khai báo hai biến: HANDLE hLibrary ; PFNRECT pfnRectangle ; Tiếp đến goị tới các hàm LoadLibrary và GetProcAddress : Bài giảng môn học: Lâp̣ triǹh Windows 85 hLibrary = LoadLibrary (TEXT ("GDI32.DLL")) pfnRectangle = (PFNPRECT) GetProcAddress (hLibrary, TEXT ("Rectangle")) Và bây giờ có thể gọi tới hàm Rectangle: pfnRectangle (hdc, xLeft, yTop, xRight, yBottom) ; FreeLibrary (hLibrary) ; Măc̣ dù kỹ thuâṭ sƣ̉ duṇg thƣ viêṇ liên kết đôṇg theo kiểu này kh ông làm tăng hiêụ quả sƣ̉ duṇg của hàm Rectangle nhƣng nó laị là môṭ cách hiêụ quả trong trƣờng hơp̣ mà chúng ta không biết tên của thƣ viêṇ cho tới khi chƣơng trình chaỵ (chẳng haṇ đối với hàm AlphaBlend chẳng haṇ). Đoaṇ ma ̃ trên sƣ̉ duṇg hai hàm LoadLibrary và FreeLibrary . Windows kiểm soát các biến đếm tham chiếu tới tất cả các module thƣ viêṇ . Hàm LoadLibrary sẽ làm cho biến đếm tham chiếu tới các thƣ viêṇ đƣơc̣ nap̣ tăng lên 1. Biến đếm tham chiếu cũng đƣợc tăng lên khi Windows nap̣ môṭ chƣơng trình có sƣ̉ duṇg thƣ viêṇ . FreeLibarary se ̃làm cho biến đếm này giảm đi 1, trƣờng hơp̣ môṭ instance của môṭ chƣơng trình sƣ̉ duṇg thƣ viêṇ bi ̣ loaị khỏi bô ̣nhớ biến đếm tham chiếu cũng giảm đi 1 đơn vi.̣ Khi biến đếm tham chiếu này bằng 0 Windows se ̃ loại bỏ thƣ viện khỏi bộ nhớ vì lúc đó thƣ viện không còn cần thiết nữa. Các thƣ viện chỉ chứa tài nguyên Bất cƣ́ hàm này trong môṭ thƣ viêṇ liên kết đô ̣ng mà môṭ chƣơng trình trên Windows và các thƣ viện khác có thể gọi tới đều phải đƣợc export . Tuy nhiên môṭ thƣ viêṇ liên kết đôṇg có thể không nhất thiết phải chứa bất cứ một hàm export nào . Vâỵ các thƣ viêṇ đó chƣ́a gì ? Câu trả lời là các tài nguyên. Chẳng haṇ chúng ta làm viêc̣ trên môṭ ƣ́ng duṇg Windows cần môṭ số các ảnh bitmap . Thông thƣờng chúng ta se ̃liêṭ kê các ảnh này trong file kic̣h bản tài nguyên và nap̣ chúng vào bô ̣nhớ với hàm LoadBitmap. Nhƣng có le ̃chúng ta muốn có môṭ số tâp̣ ảnh , mỗi tâp̣ cho môṭ đô ̣phân giải của màn hình hình thƣờng đƣơc̣ sƣ̉ duṇg với Windows . Giải pháp khả dĩ nhất là chƣ́a các tâp̣ ảnh khác nhau này vào các file khác nhau vì môṭ ngƣời dùng chỉ cần tới môṭ tâp̣ các ảnh này trên đĩa cứng. Và các file này đƣợc gọi là các file thƣ viện chỉ chứa tài nguyên . Hình 21-5 cho chúng ta thấy cách thƣ́c taọ ra môṭ thƣ viêṇ chỉ chƣ́a tài nguyên đƣơc̣ goị là bitlib.dll chƣ́a 9 ảnh bitmap. File bitlib.rc chƣ́a tất cả các ảnh này và gán cho mỗi ảnh môṭ số. Để taọ ra file bitlib .dll chúng ta cần có 9 ảnh có tên lần lƣợt là bitmap 1.bmp, bitmap2.bmp, , bitmap9.bmp. Chúng ta có thể sử d ụng các ảnh đi kèm với đĩa CD của quyển sách để dùng. Bài tập: Bài tập 1: Viết thƣ viện DLL chứa các hàm xử lý xâu. Bài giảng môn học: Lâp̣ triǹh Windows 86 Tài liệu tham khảo [1] Lê Hƣ̃u Đaṭ. Lâp̣ trình Windows. NXB Giáo duc̣. [2] Charles Petzold. Programming Windows, fifth edition. Microsoft Press. 1998. [3] Johnson M. Hart. Windows System Programming Third Edition. Addison Wesley Professional. 2004. Bài giảng môn học: Lâp̣ triǹh Windows 87 Đề thi tham khảo Đề số 1: Bài số 1 Cho Dialog sau: Biết rằng các ID của các control của Dialog trên nhƣ sau : các ID của các Edit Text tƣơng ứng với các Static Text Canh a , Canh b , Canh c lần lƣơṭ là : ID_CANHA, ID_CANHB, ID_CANHC, ID của Static 1 là ID_KQKT, ID của Static 2 là ID_DTCV, ID của các Button Kiem tra và Thoat là ID _KIEMTRA, ID_THOAT. Hãy viết hàm xử lý cho Dialog trên sau cho khi nhấn vào nút Kiem tra thì chƣơng trình se ̃kiểm tra xem ba số nguyên đƣơc̣ nhâp̣ và 3 Edit text tƣơng ƣ́ng có là 3 cạnh của 1 tam giác hay không , kết quả kiểm tra đƣơc̣ thông báo qua Static 1. Trong trƣờng hơp̣ là 3 cạnh của 1 tam giác haỹ tính và hiển thi ̣ chu vi , diêṇ tích của tam giác qua Static 2 và khi ngƣời dùng nhấn vào nút Thoat sẽ kết thúc Dialog. Bài số 2 a) Hãy trình bày (đƣa ra ) dạng đơn giản nhất của một hàm xử lý thông điệp cửa sổ chƣơnh trình (hàm Window Proc). b) Giả sử chƣơng trình chỉ có một mục menu Help , trong đó có 2 mục menu con là Contents và About , hãy viết hàm xử lý thôn g điêp̣ cƣ̉a sổ chƣơng trình sao cho khi ngƣời dùng choṇ các muc̣ trong menu Help chƣơng trình se ̃hiển thi ̣ các thông báo (hàm Message Box) tƣơng ƣ́ng và khi ngƣời dùng nhấn chuôṭ trái vào 1 vị trí trên màn hình, hãy in ra một thông báo về toạ đô ̣chuôṭ taị vi ̣ trí đó (hàm TextOut). Đề số 2: Bài số 1 Viết hàm xƣ̉ lý hôp̣ thoaị sau: Bài giảng môn học: Lâp̣ triǹh Windows 88 Các điều khiển XA , YA, XB, YB, XC, YC là toạ đô ̣ 3 đỉnh trên măṭ phẳng toạ đô ̣ (nguyên). Khi nhấn nút “Tinh chu vi” haỹ tính chu vi của tam giác tạo thành bởi 3 đỉnh A, B, C và hiển thi ̣ lên IDC _KQ. Nhấn “Thoat” để thóat khỏi hôp̣ thoaị và nhấn “Tinh dien tich” se ̃ tính diện tích và hiển thị nhƣ trong phần tính chu vi Bài số 2 a) Hãy trình bày (đƣa ra) dạng đơn giản nhất của một hàm xử lý thông điệp cửa sổ ch o môṭ hôp̣ thoaị. b) Hãy viết hàm WndProc cho một chƣơng trình có hệ thống menu gồm 1 mục File , trong đó có các muc̣ con với các chƣ́c năng sau : Menu1, Menu2, khi ngƣời dùng nhấn vào các mục này chỉ cần đƣa ra thông báo đơn giản , mục Exit để thoát khỏi chƣơng trình. Đề số 3: Bài số 1 Viết hàm xƣ̉ lý hôp̣ thoaị sau: Các điều khiển XA , YA, XB, YB, XC, YC là toạ đô ̣ 3 đỉnh trên măṭ phẳng tọa độ (nguyên). Khi nhấn nút “Tinh chu vi” haỹ tính chu vi của tam giác taọ thành bởi 3 đỉnh A, B, C và hiển thi ̣ lên IDC _KQ. Nhấn “Thoat” để thóat khỏi hôp̣ thoaị và nhấn “Tinh dien tich” se ̃ tính diện tích và hiển thị nhƣ trong phần tính chu vi Bài giảng môn học: Lâp̣ triǹh Windows 89 Bài số 2 a) Hãy trình bày (đƣa ra) dạng đơn giản nhất của một hàm xử lý thông điệp cửa sổ ch o môṭ hôp̣ thoaị. b) Hãy viết hàm WndProc cho một chƣơng trình có hệ thống menu gồm 1 mục File , trong đó có các mu c̣ con với các chƣ́c năng sau : Menu1, Menu2, khi ngƣời dùng nhấn vào các mục này chỉ cần đƣa ra thông báo đơn giản , mục Exit để thoát khỏi chƣơng trình.

Các file đính kèm theo tài liệu này:

  • pdfwin32api_1254.pdf