Xuất chữ ra vùng client của ứng dụng

Chia sẻ: Buihuu Tan | Ngày: | Loại File: PDF | Số trang:8

0
72
lượt xem
18
download

Xuất chữ ra vùng client của ứng dụng

Mô tả tài liệu
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

Một trong các đặc tính nổi bật nhất của Windows là giao diện giao tiếp với người dùng. Nhờ đặc tính này, nhiều dạng dữ liệu thông tin khác nhau được máy tính hỗ trợ khi xuất ra màn hình, máy in, … Trong đó xuất văn bản ra vùng làm việc của các ứng dụng Win32 là hình thức phổ biến nhất trong giao diện đồ họa. Ở các bài viết trước, các thao tác xuất thông tin chỉ được thực hiện thông qua các cửa sổ thông báo, hộp thoại và các phần tử điều khiển. Chương...

Chủ đề:
Lưu

Nội dung Text: Xuất chữ ra vùng client của ứng dụng

  1. Iczelion’s Tutorial Win32 ASM Tutorial 4 : Painting with Text Xuất chữ ra vùng client của ứng dụng Một trong các đặc tính nổi bật nhất của Windows là giao diện giao tiếp với người dùng. Nhờ đặc tính này, nhiều dạng dữ liệu thông tin khác nhau được máy tính hỗ trợ khi xuất ra màn hình, máy in, … Trong đó xuất văn bản ra vùng làm việc của các ứng dụng Win32 là hình thức phổ biến nhất trong giao diện đồ họa. Ở các bài viết trước, các thao tác xuất thông tin chỉ được thực hiện thông qua các cửa sổ thông báo, hộp thoại và các phần tử điều khiển. Chương này sẽ trình bày cách thể hiện nội dung văn bản trên vùng làm việc của cửa sổ thông qua các hàm Win32 API. Trong bài viết này, chúng ta học cách làm thế nào để xuất chữ ra vùng client của ứng dụng, tìm hiểu sâu hơn về việc xử lý thông điệp WM_PAINT, các khái niệm về thiết bị ngữ cảnh (device context) và các thông tin metrics. Tìm hiểu về Device Context Văn bản là một trong những đối tượng đồ họa của hệ thống. Mỗi ký tự là tập hợp của nhiều điểm ảnh (pixels) được xếp thành những mẫu riêng biệt. Đó là lý do tại sao người ta dùng thuật ngữ là vẽ painting chứ không phải là writing. Thông thường, bạn “vẽ” chữ trong vùng client của ứng dụng (thực tế, bạn cũng có thể vẽ bên ngoài vùng client, nhưng đó là vấn đề khác). Việc xuất chữ ra màn hình trong Windows hoàn toàn khác trong Dos. Trong Dos, ở chế độ văn bản màn hình có kích thước 80 x 25 (80 cột x 25 dòng) và chỉ sử dụng bởi một ứng dụng. Nhưng trong Windows thì khác, màn hình được chia sẽ cho những ứng khác nhau. Một vài bắt buộc phải tuân thủ để tránh tình trạng màn hình của ứng dụng này chồng lên màn hình của ứng dụng khác .Vì thế, HĐH sẽ giới hạn vùng painting của mỗi cửa sổ là vùng client của chính nó. Vùng client của cửa sổ là phần của cửa sổ ứng dụng trừ thanh tiêu đề (title), đường viền (border), thanh menu (menu bar), thanh công cụ (tool bar), thanh trạng thái (status bar) và thanh cuộn (scrollbar). Hình ảnh của vùng client được thể hiện như sau: Người dịch: Benina (REA TEAM) Trang 1 Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)
  2. Iczelion’s Tutorial Win32 ASM Tutorial 4 : Painting with Text Kích thước vùng client của cửa sổ window không là hằng số. Người dùng có thể thay đổi kích thước này bất kỳ khi nào họ muốn. Vì vậy, bạn cần phải xác định kích thước của vùng client một cách linh động. Khi một chương trình cần vẽ lên trên vùng client, bạn phải “xin phép”, xem HĐH có chấp thuận cho bạn có quyền lên vùng client của ứng dụng hay không. HĐH sẽ xác định kích thước của vùng client của ứng dụng, font chữ, màu chữ và các thuộc tính khác của giao diện thiết bị đồ họa (GDI – Graphics Device Interface), và gởi về handle của thiết bị ngữ cảnh (handle to device context) cho ứng dụng của bạn. Bạn có thể dùng device context như một giấy thông hành để vẽ trên vùng client. Vậy device context là gì?. Nó thực sự chỉ là một cấu trúc dữ liệu được duy trì bên trong Windows bởi GDI . Một DC được kết hợp với một thiết bị hiển thị đặc thù như là màn hình hay máy in. Đối với màn hình, một DC thường kết hợp với một cửa sổ riêng biệt trên màn hình. Một số giá trị trong DC là các thuộc tính đồ họa như colors, font… Bạn có thể thay đổi những giá trị mặc định này bằng cách gọi các hàm GDI khác nhau. Các hàm GDI cho phép bạn nhận các giá trị hiện hành của các thuộc tính này. Dĩ nhiên, còn có các hàm GDI khác cho phép bạn thực sự vẽ vùng client của cửa sổ. Có thể xem DC như là môi trường được thiết lập mặc định được HĐH chuẩn bị sẳn cho bạn. Bạn có thể thay đổi thiết lập mặc định nếu bạn muốn. Khi một chương trình cần vẽ, đầu tiên nó cần nhận một handle của một DC. Thông thường, có 3 cách để nhận handle của DC:  Sử dụng hàm BeginPaint và EndPaint để xử lý thông điệp WM_PAINT Bạn sử dụng phương pháp này khi xử lý các thông điệp WM_PAINT. Hai hàm được yêu cầu: BeginPaint và EndPaint. Hai hàm này yêu cầu handle cửa sổ, đưa vào thủ tục window như một đối số và địa chỉ của biến cấu trúc này là ps và định nghĩa nó trong thủ tục window như sau: PAINTSTRUCT ps; Cấu trúc PAINTSTRUCT được định nghĩa như sau: typedef struct tagPAINTSTRUCT { // ps HDC hdc; //handle c•a DC BOOL fErase; //Cho bi•t background có b• xóa hay không RECT rcPaint; //Cho bi•t t•a •• HCN tô v• l•i BOOL fRestore; //dành riêng cho Windows s• d•ng BOOL fIncUpdate; //dành riêng cho Windows s• d•ng BYTE rgbReserved[32]; //dành riêng cho Windows s• d•ng } PAINTSTRUCT; Trong khi xử lý một thông điệp WM_PAINT, đầu tiên thủ tục Window gọi BegianPaint. Hàm BeginPaint thường làm nền của vùng không hợp lệ bị xóa để chuẩn bị vẽ. Hàm cũng điền vào các trường trong cấu trúc ps. Giá trị trả về từ BeginPaint là handle của DC. Nó thường lưu trong một biến có tên là hdc. Bạn định nghĩa biến này trong thủ tục Window như sau: HDC hdc; Người dịch: Benina (REA TEAM) Trang 2 Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)
  3. Iczelion’s Tutorial Win32 ASM Tutorial 4 : Painting with Text Hiểu như thế nào về vùng không hợp lệ? Khi cửa sổ ứng dụng bị người dùng tương tác (thay đổi kích thước, phóng to, thu nhỏ, đóng mở các hộp thoại …), thì vùng client của cửa sổ sẽ bị thay đổi, nếu không vẽ lại vùng client kịp thời, thì rất có thể vùng client sẽ bị loang lỗ (được gọi là invalid region). Windows thường xuyên kêu chương trình của bạn “tô son điểm phấn” lại bằng cách phát thông điệp WM_PAINT. Để phản hồi lại thông điệp này, thủ tục Window sẽ cho tô điểm lại cửa sổ tại những chỗ bị loang lỗ. Việc này đòi hỏi thủ tục Window phải nhận được một DC, cung cấp những công cụ vẽ, quyền thâm nhập vào thiết bị cũng như giấy phép thông hành cho phép vẽ cửa sổ. Kiểu dữ liệu HDC được định nghĩa là một số nguyên 32-bit không dấu. Chương trình sau đó có thể sử dụng các hàm GDI như TextOut, mà handle của DC yêu cầu. Việc gọi EndPaint sẽ loại bỏ handle của DC. Điển hình, việc xử lý thông điệp WM_PAINT có dạng như sau: case WM_PAINT: hdc = BeginPaint(hwnd, &ps); //S• d•ng các hàm GDI EndPaint(hwnd, &ps); return 0; Thủ tục Window cần gọi BeginPaint và EndPaint như một cặp khi xử lý thông điệp WM_PAINT. Nếu thủ tục Window không xử lý các thông điệp WM_PAINT, nó cần đưa thông điệp WM_PAINT cho DefWindowProc, là thủ tục Window mặc định nằm trong HĐH. DefWindowProc xử lý thông điệp WM_PAINT như sau: case WM_PAINT: BeginPaint(hwnd, &ps); EndPaint(hwnd, &ps); return 0; Việc gọi liên tiếp BeginPaint và EndPaint sẽ làm hợp lệ vùng không hợp lệ trước đó. Windows đặ một thông điệp WM_PAINT trong hàng đợi thông điệp vì một phần của client không hợp lệ. Bạn không gọi BeginPaint và EndPaint (hay ValidateRect), HĐH sẽ không hợp lệ vùng đó và sẽ gửi tiếp cho bạn một thông điệp WM_PAINT khác. Sự việc sẽ làm treo chương trình vì không thể thoát.  Sử dụng hàm GetDC để nhận handle và ReleaseDC để giải phóng handle của DC khi xử lý các thông điệp khác WM_PAINT Mặc dù tốt nhất là vẽ lại toàn bộ vùng client trong khi xử lý thông điệp WM_PAINT, nhưng có thể bạn sẽ thấy hữu dụng hơn khi vẽ một phần vùng client trong quá trình xử lý các thông điệp khác. Hoặc bạn có thể cần một handle của DC cho mục đích khác như nhận thông tin về Device Context. Người dịch: Benina (REA TEAM) Trang 3 Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)
  4. Iczelion’s Tutorial Win32 ASM Tutorial 4 : Painting with Text Để nhận một handle của DC của vùng client của cửa sổ, bạn gọi GetDC để nhận handle và gọi ReleaseDC khi bạn muốn giải phóng DC hDC = GetDC(hWnd); //X• lý ………… ReleaseDC(hWnd, hDC); Cũng như BeginPaint và EndPaint, các hàm GetDC và ReleaseDC nên gọi theo cặp. Khi bạn gọi GetDC trong khi xử lý một thông điệp. Bạn nên gọi ReleaseDC trước khi bạn thoát thủ tục Window. Không nên gọi GetDC trong một thông điệp và ReleaseDC trong một thông điệp khác. Không như handle DC trả về BeginPaint, handle DC trả về từ GetDC có hình chữ nhật xén toàn bộ vùng client. Bạn có thể vẽ trên bất kỳ phần nào của vùng client, không chỉ đơn thuần là trên hình chữ nhật không hợp lệ (nếu thực sự có hình chữ nhật không hợp lệ). Không như BeginPaint, GetDC không hợp lệ hóa bất cứ vùng hợp lệ nào. Nếu bạn cần hợp lệ hóa toàn bộ vùng client, bạn có thể gọi: ValidateRect (hwnd, NULL);  Gọi hàm GetWindowDC (CreateDC) và ReleaseDC. hDC = GetWindowDC(hWnd); // X• lý ………… ReleaseDC(hWnd, hDC); Lưu ý: Các hàm GetDC và BeginPaint trả về device context cho vùng client của cửa sổ, riêng hàm GetWindowDC trả về device context của toàn bộ cửa sổ kể cả thanh tiêu đề, menu, thanh cuộn… và tất nhiên là cả vùng client. Để vẽ ra ngoài vùng làm việc (client area), phải chặn thông điệp WM_NCPAINT( NC: None Client). Một điều mà bạn phải nhớ là, sau khi bạn nhận được handle của DC, bạn chỉ sử dụng DC cho việc xử lý một thông điệp duy nhất, tức là không được nhận handle ở thông điệp này mà giải phóng nó ở một thông điệp khác. HĐH sẽ gởi thông điệp WM_PAINT đến cửa sổ để vẽ lại vùng client của nó. HĐH không lưu nội dung của vùng client của một cửa số. Thay vào đó, khi có một sự kiện xảy ra, thì nó cho phép vẽ lại vùng client (như trường hợp vùng client của cửa sổ này bị che bởi vùng client của một cửa sổ khác), HĐH sẽ đặt thông điệp WM_PAINT trong hàng đợi thông điệp. Nó chịu trách nhiệm vẽ lại vùng client của cửa sổ. Bạn phải cung cấp các thông tin để HĐH biết cửa sổ muốn vẽ lại vùng client như thế nào trong phần WM_PAINT của thủ tục window, vì vậy thủ tục window sẽ vẽ lại vùng client khi thông điệp WM_PAINT được tiếp nhận. Người dịch: Benina (REA TEAM) Trang 4 Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)
  5. Iczelion’s Tutorial Win32 ASM Tutorial 4 : Painting with Text Khái niệm khác mà bạn phải tìm hiểu đến là thuật ngữ hình chữ nhật không hợp lệ (invalid rectangle). HĐH định nghĩa một hình chữ nhật không hợp lệ là một vùng hình chữ nhật nhỏ nhất trong vùng client cần được vẽ lại. Khi HĐH phát hiện ra một hình chữ nhật không hợp lệ trong vùng client của một cửa sổ, nó sẽ gởi thông điệp WM_PAINT đến cửa sổ đó. Để phản hồi lại thông điệp WM_PAINT này, handle của được chứa trong một cấu trúc PAINTSTRUCT, trong số những trường của cấu trúc này, có tọa độ của hình chữ nhật không hợp lệ. Bạn gọi hàm BeginPaint trong việc phản hồi thông điệp WM_PAINT để hợp lệ hóa hình chữ nhật không hợp lệ. Nếu không thực hiện thông điệp WM_PAINT, thì ít nhất bạn cũng phải gọi hàm DefWindowProc hay ValidateRect hợp lệ lệ hóa hình chữ nhật ko hợp lệ, nếu không thì HĐH sẽ gởi cho bạn thông điệp WM_PAINT lặp đi lặp lại nhiều lần. Để phản hồi lại thông điệp WM_PAINT, bạn cần thực hiện các bước sau:  Lấy một handle của DC bằng hàm BeginPaint.  Vẽ vùng client.  Giải phóng handle của DC bằng hàm EndPaint Chú ý rằng, bạn không phải làm hợp lệ hóa hình chữ nhật ko hợp lệ. Nó được làm một cách tự động bởi hàm BeginPaint. Giữa cặp BeginPaint vàEndPaint, bạn có thể gọi bất kỳ hàm GDI để vẽ vùng client của bạn. Gần như tất cả chúng đều cần handle của device context như một tham số. Chúng ta sẽ viết một chương trình hiển thị dòng chữ “Win32 assembly is great and easy!” ở giữa vùng client. .386 .model flat,stdcall option casemap:none WinMain proto :DWORD,:DWORD,:DWORD,:DWORD include windows.inc include user32.inc include kernel32.inc includelib user32.lib includelib kernel32.lib .data ClassName db "SimpleWinClass",0 AppName db "Our First Window",0 OurText db "Win32 assembly is great and easy!",0 .data? hInstance HINSTANCE ? CommandLine LPSTR ? .code start: invoke GetModuleHandle, NULL mov hInstance,eax invoke GetCommandLine Người dịch: Benina (REA TEAM) Trang 5 Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)
  6. Iczelion’s Tutorial Win32 ASM Tutorial 4 : Painting with Text mov CommandLine,eax invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT invoke ExitProcess,eax WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE, CmdLine:LPSTR,CmdShow:DWORD LOCAL wc:WNDCLASSEX LOCAL msg:MSG LOCAL hwnd:HWND mov wc.cbSize,SIZEOF WNDCLASSEX mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInst pop wc.hInstance mov wc.hbrBackground,COLOR_WINDOW+1 mov wc.lpszMenuName,NULL mov wc.lpszClassName,OFFSET ClassName invoke LoadIcon,NULL,IDI_APPLICATION mov wc.hIcon,eax mov wc.hIconSm,0 invoke LoadCursor,NULL,IDC_ARROW mov wc.hCursor,eax invoke RegisterClassEx, addr wc INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\ WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\ CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\ hInst,NULL mov hwnd,eax INVOKE ShowWindow, hwnd,SW_SHOWNORMAL INVOKE UpdateWindow, hwnd .WHILE TRUE INVOKE GetMessage, ADDR msg,NULL,0,0 .BREAK .IF (!eax) INVOKE TranslateMessage, ADDR msg INVOKE DispatchMessage, ADDR msg .ENDW mov eax,msg.wParam ret WinMain endp WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM LOCAL hdc:HDC LOCAL ps:PAINTSTRUCT LOCAL rect:RECT .IF uMsg==WM_DESTROY invoke PostQuitMessage,NULL .ELSEIF uMsg==WM_PAINT invoke BeginPaint,hWnd, ADDR ps mov hdc,eax invoke GetClientRect,hWnd, ADDR rect invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, DT_SINGLELINE or DT_CENTER or DT_VCENTER invoke EndPaint,hWnd, ADDR ps .ELSE invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret .ENDIF xor eax,eax ret WndProc endp end start Người dịch: Benina (REA TEAM) Trang 6 Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)
  7. Iczelion’s Tutorial Win32 ASM Tutorial 4 : Painting with Text Phân tích mã lệnh Đa số các mã lệnh trong đoạn code trên giống với ví dụ trong bài viết thứ 3. Tôi chỉ giải thích những những đoạn mã mới và quan trọng: LOCAL hdc:HDC LOCAL ps:PAINTSTRUCT LOCAL rect:RECT Đây là những biến cục bộ, chúng được sử dụng bởi các hàm GDI trong phần xử lý thông điệp WM_PAINT.  hdc được dùng để lưu handle của device context được trả về từ hàm BeginPaint.  ps là cấu trúc PAINTSTRUCT. Thông thường bạn không sử dụng các giá trị trong ps. Nó được chuyển đến hàm BeginPaint và HĐH sẽ điền vào cấu trúc này các giá trị thích hợp. Sau đó, bạn chuyển ps đến hàm EndPaint khi đã hoàn thành việc vẽ trong vùng client.  rect là một cấu trúc RECT, được định nghĩa như sau: typedef struct _RECT { // rc LONG left; LONG top; LONG right; LONG bottom; } RECT; left và top là tọa độ của góc trên bên trái của hình chử nhật. right và bottom là tọa độ góc dưới bên phải. Một điều cần nhớ là: Trục tọa độ gốc x-y tại góc trên bên trái của vùng client. Vì vậy điểm y = 10 là nằm dưới điểm y = 0 invoke BeginPaint,hWnd, ADDR ps mov hdc,eax invoke GetClientRect,hWnd, ADDR rect invoke DrawText, hdc,ADDR OurText,-1, ADDR rect, \ DT_SINGLELINE or DT_CENTER or DT_VCENTER invoke EndPaint,hWnd, ADDR ps Để phản hồi thông điệp WM_PAINT, bạn gọi hàm BeginPaint với tham số là handle của cửa sổ mà bạn muốn vẽ và một cấu trúc PAINTSTRUCT không được khởi tạo giá trị ban đầu cho các thuộc tính. Sau khi hoàn thành lời gọi hàm, eax chứa handle của device context. Kế đến bạn gọi hàm GetClientRect để lấy kích thước của vùng client. Kích thước được trả về trong biến rect và bạn truyền nó đến hàm DrawText như một trong những tham số của hàm. Cú pháp của hàm DrawText như sau: DrawText proto hdc:HDC, lpString:DWORD, nCount:DWORD, lpRect:DWORD, uFormat:DWORD Người dịch: Benina (REA TEAM) Trang 7 Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)
  8. Iczelion’s Tutorial Win32 ASM Tutorial 4 : Painting with Text Tương tự như hàm TabbedTextOut(), hàm DrawText() cũng kết xuất chữ, cung cấp một vài khả năng định dạng. Theo tôi, hàm này chỉ hữu ích vì nó có khả năng wrapping khi xử lý văn bản gồm nhiều dòng (multiline).Vì vậy bạn có thể xác định vị trí của chuỗi mà bạn muốn vẽ. Một hàm khác cũng thực hiện chức năng kết xuất chữ đó là hàm TextOut(), nhưng chỉ xuất những văn bản đơn giản nhất vì nó “vẽ” một dòng văn bản đơn, sử dụng font chữ được chọn. DrawText sẽ định dạng chuỗi text một cách thích hợp sao cho nó không vượt quá đường biên giới hạn hình chữ nhật. Nó sử dụng font chữ, màu chữ và màu nền đã được lựa chọn (trong device context) để vẽ chữ. Những dòng chữ phải nằm trong đường biên của hình chữ nhật. Nó trả về chiều cao của chuỗi được kết xuất, có giá trị là đơn vị của thiết bị xuất (màn hình máy tính), trong trường hợp của chúng ta là pixels (điểm ảnh). Chúng ta hãy xem xét các tham số của chúng: hdc: handle của device context lpString: con trỏ trỏ tới chuỗi ký tự cần phải kết xuất (vẽ trong vùng client). Chuỗi này phải là chuỗi kết thúc bằng NULL, nếu khác bạn phải chỉ rõ chiều dài của nó trong tham số kế tiếp – nCount. nCount: số ký tự trong chuỗi, không kể ký tự NULL ở cuối chuỗi nếu có. Nếu chuỗi kết thúc là NULL thì nCount phải là -1 hay nói cách khác, nCount phải chứa số của các ký tự trong chuỗi mà bạn muốn vẽ. lpRect: con trỏ trỏ tới vùng hình chữ nhật (một cấu trúc lọai RECT) mà bạn muốn vẽ chuỗi trong đó. Chú ý rằng hình chữ nhật cũng là một mẫu hình chữ nhật, có nghĩa là bạn không thể vẽ chuỗi ra ngòai hình chữ nhật này. uFormat: Giá trị xác định chuỗi được hiển thị như thế nào trong hình chữ nhật. Chúng ta dùng 3 giá trị được kết hợp bởi toán OR như sau: DT_SINGLELINE : ch• ••nh m•t dòng text ••n DT_CENTER : trung tâm c•a text theo ph••ng ngang DT_VCENTER : trung tâm c•a text theo ph••ng ••ng Sau khi hòan thành việc vẽ chuỗi lên vùng client, bạn phải gọi hàm EndPaint để giải phóng handle của device context. Bạn có thể tóm tắt lại những điểm quan trọng trong bài viết này:  Bạn gọi cặp hàm BeginPaint() và EndPaint() để xử lý thông điệp WM_PAINT.  Làm bất cứ điều gì bạn thích với vùng client giữa các hàm BeginPaint và EndPaint. Nếu bạn muốn vẽ lại vùng client để xử lý các các thông điệp khác, bạn có 2 lựa chọn:  Sử dụng cặp hàm GetDC() và ReleaseDC() và thực hiện việc vẽ giữa 2 lời gọi hàm  Gọi hàm InvalidRect() hay UpdateWindow() để làm mất hiệu lực toàn bộ vùng client, ép Windows gởi thông điệp WM_PAINT vào trong hàng đợi thông điệp của cửa sổ và thực hiện việc vẽ trong section WM_PAINT. Người dịch: Benina (REA TEAM) Trang 8 Tổng hợp và hiệu chỉnh: NhatPhuongLe (VNCERT TEAM)
Đồng bộ tài khoản