Go Back   Cựu Học Sinh Lê Quý Đôn - Long An > :: Góc Học Tập :: > Tin học > Học lập trình

Học lập trình Pascal , C+ , C++ , VB

Bộ nhớ động trong C++

Bộ nhớ động trong C++

this thread has 6 replies and has been viewed 4554 times

Gởi Ðề Tài Mới Trả lời
 
Ðiều Chỉnh Xếp Bài
Old 05-06-2006, 05:48 PM   #1
Hồ sơ
LeGiang
Banned
 
Tham gia ngày: Jan 2005
Số bài viết: 473
Tiền: 25
Thanks: 41
Thanked 593 Times in 241 Posts
LeGiang is an unknown quantity at this point
Default

BỘ NHỚ ĐỘNG TRONG C++ </span>
(được viết bởi batanlp)
<a href=\'http://www.diendantinhoc.net/tute/laptrinh/c/dynmem-batanlp/\' target=\'_blank\'><span style=\'color:red\'>nguồn</a>
Trong bài viết này, tôi sẽ giới thiệu với các bạn về cách lập trình với con trỏ (pointer) trong việc cấp phát bộ nhớ động (dynamic memory alocation) bằng ngôn ngữ C++. Đối với các bạn đã có kinh nghiệm lập trình với C++ thì bài này đối với các bạn chỉ là "a-bờ-cờ" mà thôi. Nhưng với các bạn mới học C++ thì có lẽ là bổ ích.

Nếu bạn mới học lập trình và làm quen với cấu trúc dữ liệu thì một trong số những cấu trúc dữ liệu đầu tiên mà bạn "rớ" tới là Stack (ngăn xếp) và Queue (hàng đợi). Bạn có thể dùng 1 mảng (array) để thiết kế Stack và Queue. Dùng mảng thì đơn giản, nhưng bạn sẽ gặp một số bất lợi như sau:

Lãng phí bộ nhớ: giả sử bạn khai báo int array_entry[100] thì bạn sẽ có 1 vùng nhớ cho 100 phần tử, nhưng nếu chương trình của bạn chỉ thường xuyên dùng có 10 phần tử, và 1 vài lần là dùng đến 100 phần tử thì tức là bạn đã phí phạm 90 phần tử.

Thiếu bộ nhớ: vì tiết kiệm, bạn chỉ khai báo int array_entry[10], nhưng nếu bạn cần dùng đến 15 phần tử thì...overflow ngay. Chưa hết, array cần một vùng nhớ liên tục, giả sử máy bạn vẫn còn nhiều bộ nhớ trống, nhưng không có vùng nhớ trống liên tục nào đủ lớn cho mảng của bạn. Thế là vẫn...thiếu bộ nhớ.

Và cuối cùng, người ta khi thấy bạn viết như vậy thì coi bạn là dân amateur, buồn nhỉ? L

Nhưng không sao, bài viết này sẽ giúp bạn vượt qua các trở ngại đó. Bạn sẽ biết cách ứng dụng con trỏ để tạo thành các cấu trúc dữ liệu kiên kết (linked structure) để tạo thành các linked stack hoặc linked queue. Bạn sẽ biết được cách toàn quyền cấp phát bộ nhớ khi có nhu cầu sử dụng; và khi không dùng nữa thì ta xoá nó đi, nhường memory cho các chương trình khác.
Ghi chú: xem như là bạn đã có học và biết qua sơ sơ về C++ rồi, nhứng đoạn code trong bài viết này là C++ chứ không phải là C.

Ứng dụng con trỏ, ta có thể tạo ra một danh sách liên kết (mảng liên kết - linked list) như sau:



Phần đánh dấu màu hồng (và nút tròn đầu tiên) là các con trỏ, có nhiệm vụ link đến phần tử tiếp theo trong danh sách. Nhờ có các link này mà khi đang ở phần tử thứ nhất, bạn sẽ có manh mối để lần đến phần tử thứ hai, rồi phần tử thứ ba...

Một phần tử của danh sách có thể được biểu diễn qua cấu trúc như sau (1 phần tử ta gọi là 1 Node):

Code:
struct Node {
    // data members
    Node_entry entry;
    Node *next;

    //constructors
    Node();
    Node(Node_entry item, Node *add_on = NULL);
};
Ghi chú: Node_entry là kiểu dữ liệu để chứa data của phần tử, entry chính là nơi data của phần tử được lưu. Ở ví dụ trong hình trên, với node đầu tiên thì entry sẽ chứa Fred, 367-2205 và Jan. 28.

Và ta có các hàm khởi tạo cho nó như sau:

Code:
Node::Node()
{
    next = NULL;
}

Node::Node(Node_entry item, Node *add_on)
{
    entry = item;
    next = add_on;
}
Như vậy, khi ta khai báo Node* data và khởi tạo xong cho data thì data->entry sẽ là phần dữ liệu của node đó, và data->next sẽ là đường dẫn tới node tiếp theo. Như thế thì ta có thể duyệt hết toàn bộ data mà ta đã lưu trong danh sách rồi. Để rõ hơn, ta hãy xem một ví dụ qua đoạn code nhỏ sau. Chú ý: khi data->next == NULL thì tức là node này là phần tử cuối cùng.



Đoạn code trong ví dụ sẽ tạo ra một danh sách liên kết như trong hình trên (không tin thì bạn...thử xem là biết liền J). Địa chỉ bộ nhớ của các node thì hoàn toàn ngẫu nhiên và không liên tục vì nó là dynamic memory allocation mà.

Thế là đã Ok cho việc tạo một cấu trúc liên kết. Giờ ta bắt tay vào xây dựng Linked Stack và Linked Queue. Tôi sẽ trình bày phần Linked Stack thôi, phần Linked Queue thì các bạn hãy phát triển thêm nhé. Cũng hoàn toàn tương tự thôi.

Ta khai báo Stack như sau:

Code:
enum Error_code {underflow, success, overflow};
typedef int Stack_entry;
class Stack {
public:
    Stack();
    Bool empty() const;
    Stack_entry push(const Stack_entry &item);
    Stack_entry top(Stack_entry &item) const;
    Error_code pop(); 
protected:
    Node *top_node;
}
Sau đó là các đoạn code cho các hàm trong Stack như sau:

Code:
Stack_entry Stack::push(const Stack_entry &item)
 {
    Node *new_top = new Node(item, top_node);
    if (new_top == NULL) return 0; 
    top_node = new_top;
    return item;
} 
/********************************************************************/

Error_code Stack::pop()
{
    Node *old_top = top_node;
    if (top_node == NULL) return underflow; 
    top_node = old_top->next;
    delete old_top;
    count--;
    return success;
}

/*******************************************************************/

Stack_entry Stack::top(Stack_entry &item) const
{ 
    if (top_node == NULL) return 0;
    item = top_node->entry;
    return item;
}

/*******************************************************************/

bool Stack::empty() const
{
    if (top_node == NULL) return true;
    return false;
}

/*******************************************************************/

Stack::Stack()
{
    top_node = NULL;
    count = 0;
}
Như thế đó, ta đã có 1 Linked Stack mà không dùng array. Dữ liệu được nhập vào tới đâu thì bộ nhớ được cấp phát tới đó. Và khi dữ liệu được lấy ra khỏi stack thì phần bộ nhớ của nó được giải phóng ngay lập tức, để dành cho việc khác.
LeGiang is offline   Trả Lời Với Trích Dẫn
Old 05-06-2006, 05:58 PM   #2
Hồ sơ
LeGiang
Banned
 
Tham gia ngày: Jan 2005
Số bài viết: 473
Tiền: 25
Thanks: 41
Thanked 593 Times in 241 Posts
LeGiang is an unknown quantity at this point
Default

Các bài viết sẽ tập trung giới thiệu lập trình C/C++ trên nền DOS, do đó các bạn cần trang bị 1 bộ editor và biên dịch vd như Turbor C++ hoặc Borland C++ 7.0.
[Đăng nhập để xem liên kết. ]
Bài 1: CẤU TRÚC MỘT CHƯƠNG TRÌNH C/C++</span>
<span style=\'color:purple\'>1. Hàm main:
Có lẽ một trong những cách tốt nhất để bắt đầu học một ngôn ngữ lập trình là bằng một chương trình. Vậy đây là chương trình đầu tiên của chúng ta :
Code:
// my first program in C++
#include <stdio.h>
#include <conio.h>
int main ()
{
 printf("Hello World!");
 return 0;
}
hoặc là:
Code:
// my first program in C++
#include <iostream.h>
int main ()
{
 cout << "Hello World!";
return 0;
}
Kết quả khi thực hiện ctrình như sau:
Code:
Hello World!
Chương trình trên đây là chương trình đầu tiên mà hầu hết những người học nghề lập trình viết đầu tiên và kết quả của nó là viết câu "Hello, World" lên màn hình. Đây là một trong những chương trình đơn giản nhất có thể viết bằng C/C++ nhưng nó đã bao gồm những phần cơ bản mà mọi chương trình C có. Hãy cùng xem xét từng dòng một :
Code:
// my first program in C++
Đây là dòng chú thích. Tất cả các dòng bắt đầu bằng hai dấu sổ (//) hoặc đóng/mở bằng dấu /* và */ được coi là chút thích mà chúng không có bất kì một ảnh hưởng nào đến hoạt động của chương trình. Chúng có thể được các lập trình viên dùng để giải thích hay bình phẩm bên trong mã nguồn của chương trình. Trong trường hợp này, dòng chú thích là một giải thích ngắn gọn những gì mà chương trình chúng ta làm.
Code:
#include <iostream.h>
Các câu bắt đầu bằng dấu (#) được dùng cho pre-processor (kí hiệu tiền xử lý). Chúng không phải là những dòng mã thực hiện nhưng được dùng để báo hiệu cho trình dịch. Ở đây câu lệnh #include <iostream.h> báo cho trình dịch biết cần phải kèm thêm (include) thư viện iostream. Đây là một thư viện vào ra (input-output) cơ bản trong C++ và nó phải được "include" vì nó sẽ được dùng trong chương trình. Đây là cách cổ điển để sử dụng thư viện iostream
Code:
int main ()
Dòng này tương ứng với phần bắt đầu khai báo hàm main. Hàm main là điểm (còn gọi là program entry-point) mà tất cả các chương trình C++ bắt đầu thực hiện. Nó không phụ thuộc vào vị trí của hàm này (ở đầu, cuối hay ở giữa của mã nguồn) mà nội dung của nó luôn được thực hiện đầu tiên khi chương trình bắt đầu. Thêm vào đó, do nguyên nhân nói trên, mọi chương trình C++ đều phải tồn tại một hàm main.
Theo sau main là một cặp ngoặc đơn bởi vì nó là một hàm. Trong C++, tất cả các hàm mà sau đó là một cặp ngoặc đơn () thì có nghĩa là nó có thể có hoặc không có tham số (không bắt buộc). Nội dung của hàm main tiếp ngay sau phần khai báo chính thức được bao trong các ngoặc nhọn ( { } ) như trong ví dụ của chúng ta
Code:
cout << "Hello World";
Dòng lệnh này làm việc quan trọng nhất của chương trình. cout là một dòng (stream) output chuẩn trong C++ được định nghĩa trong thư viện iostream và những gì mà dòng lệnh này làm là gửi chuỗi kí tự "Hello World" ra standard output hay màn hình.
Lưu ý: lệnh cout được khai báo trong iostream.h dùng cho C++, bạn có thể dùng 1 hàm tương tự với C chuẩn là hàm printf được khai báo trong stdio.h
Chú ý rằng mỗi dòng kết thúc bằng dấu chấm phẩy ( ; ). Kí tự này được dùng để kết thúc một lệnh và bắt buộc phải có sau mỗi lệnh trong chương trình C++ của bạn (một trong những lỗi phổ biến nhất của những lập trình viên C++ là quên mất dấu chấm phẩy).
Code:
return 0;
Lệnh return kết thúc hàm main và trả về mã đi sau nó, trong trường hợp này là 0. Đây là một kết thúc bình thường của một chương trình không có một lỗi nào trong quá trình thực hiện. Như bạn sẽ thấy trong các ví dụ tiếp theo, đây là một cách phổ biến nhất để kết thúc một chương trình C++.
Chương trình được cấu trúc thành những dòng khác nhau để nó trở nên dễ đọc hơn nhưng hoàn toàn không phải bắt buộc phải làm vậy. Ví dụ, thay vì viết
Code:
int main ()
{
 cout << " Hello World ";
 return 0;
}
ta có thể viết
Code:
int main () { cout << " Hello World "; return 0; }
cũng cho một kết quả chính xác như nhau. Tuy nhiên các bạn chú ý đến cách thức bố trí mã nguồn sao cho dễ đọc và thuận tiện khi phân tích, chỉnh sửa. Trong C++, các dòng lệnh được phân cách bằng dấu chấm phẩy ( . Việc chia chương trình thành các dòng chỉ nhằm để cho nó dễ đọc hơn mà thôi.
2. Các chú thích:
Các chú thích được các lập trình viên sử dụng để ghi chú hay mô tả trong các phần của chương trình. Trong C++ có hai cách để chú thích:
Code:
// Chú thích theo dòng
/* Chú thích theo khối */
Chú thích theo dòng bắt đầu từ cặp dấu xổ (//) cho đến cuối dòng. Chú thích theo khối bắt đầu bằng /* và kết thúc bằng */ và có thể bao gồm nhiều dòng. Chúng ta sẽ thêm các chú thích cho chương trình :
Code:
/* my second program in C++
with more comments */
#include <iostream.h>
int main ()
{
  cout << "Hello World! ";       // says Hello World!
  cout << "I'm a C++ programmer";  // says I'm a C++ program
  return 0;
}
Kết quả khi chạy chương trình sẽ cho ra:
Code:
Hello World! I'm a C++ program
Nếu bạn viết các chú thích trong chương trình mà không sử dụng các dấu //, /* hay */, trình dịch sẽ coi chúng như là các lệnh C++ và sẽ hiển thị các lỗi.
LeGiang is offline   Trả Lời Với Trích Dẫn
Old 05-06-2006, 06:11 PM   #3
Hồ sơ
LeGiang
Banned
 
Tham gia ngày: Jan 2005
Số bài viết: 473
Tiền: 25
Thanks: 41
Thanked 593 Times in 241 Posts
LeGiang is an unknown quantity at this point
Default

Bài 2: KIỂU DỮ LIỆU, BIẾN VÀ HẰNG SỐ</span>
<a href=\'http://www.thuvientinhoc.com/forums/lofiversion/index.php/t1153.html\' target=\'_blank\'>nguồn</a>
<span style=\'color:purple\'>1. Một số kiểu dữ liệu thông dụng
Khi lập trình, chúng ta lưu trữ các biến trong bộ nhớ của máy tính nhưng máy tính phải biết chúng ta muốn lưu trữ gì trong chúng vì các kiểu dữ liệu khác nhau sẽ cần lượng bộ nhớ khác nhau.
Đơn vị dữ liệu nhỏ nhất lưu trữ trong máy tính là bit. Bộ nhớ của máy tính chúng ta được tổ chức thành các byte có kích thước bằng 8 bit. Một byte có thể dùng để lưu trữ một loại dữ liệu nhỏ như là kiểu số nguyên từ 0 đến 255 hay một kí tự. Nhưng máy tính có thể xử lý các kiểu dữ liệu phức tạp hơn bằng cách gộp nhiều byte lại với nhau, như số nguyên dài hay số thập phân. Tiếp theo bạn sẽ có một danh sách các kiểu dữ liệu cơ bản trong C/C++ cũng như miền giá trị mà chúng có thể biểu diễn. Các kiểu dữ liệu sẽ được bố trí trên từng dòng lần lượt là tên kiểu, chiều dài (tính theo byte), mô tả và miền giá trị:
Tên
Số byte
Mô tả
Miền giá trị

Như sau:
- Kiểu char
Chiều dài: 1 byte
Mô tả: Kí tự hay kiểu số nguyên 8-bit
Miền giá trị:
+ có dấu (char): -128 - 127
+ không dấu (unsigned char): 0 - 255

- Kiểu short
Chiều dài: 2 bytes
Mô tả: kiểu số nguyên 16-bit
Miền giá trị:
+ có dấu (short): -32763 - 32762
+ không dấu (unsigned short): 0 - 65535

- Kiểu long
Chiều dài: 4 bytes
Mô tả: kiểu số nguyên 32-bit
Miền giá trị:
+ có dấu (long):-2147483648 - 2147483647
+ không dấu (unsigned long): 0 - 4294967295

- Kiểu int
Chiều dài: Độ dài của nó phụ thuộc vào hệ thống, như trong MS-DOS nó là 16-bit (2 bytes), trên Windows 9x/2000/NT là 32 bit (4 bytes)...
Mô tả: Xem short, long

- Kiểu float
Chiều dài: 4 bytes
Mô tả: Dạng dấu phẩy động
Miền giá trị: 3.4e + / - 38 (7 digits)

- Kiểu double
Chiều dài: 8 bytes
Mô tả: Dạng dấu phẩy động với độ chính xác gấp đôi
Miền giá trị: 1.7e + / - 308 (15 digits)

- Kiểu long double
Chiều dài: 10 bytes
Mô tả: Dạng dấu phẩy động với độ chính xác hơn nữa
Miền giá trị: 1.2e + / - 4932 (19 digits)

- Kiểu bool
Chiều dài: 1 bytes
Mô tả: Giá trị logic. Nó mới được thêm vào chuẩn ANSI-C++. Bởi vậy không phải tất cả các trình dịch đều hỗ trợ nó.
Miền giá trị: true hoặc false

2. Biến (Variable)
Biến là một đơn vị dữ liệu của chương trình, biến được xác định bởi định danh biến, hay đơn giản gọi là tên biến. Một tên biến hợp lệ là một chuỗi gồm các chữ cái, chữ số hoặc kí tự gạch dưới. Chiều dài của một tên là không giới hạn.
Kí tự trống, các kí tự đánh dấu đều không thể có mặt trong một tên. Chỉ có chữ cái, chữ số và kí tự gạch dưới là được cho phép. Thêm vào đó, một tên biến luôn phải bắt đầu bằng một chữ cái. Chúng cũng có thể bắt đầu bằng kí tự gạch dưới ( _ ) nhưng kí tự này thường được dành cho các liên kết bên ngoài (external link) hoặc giữa các tên hàm. Không bao giờ chúng bắt đầu bằng một chữ số.
Một luật nữa mà bạn phải quan tâm đến khi tạo ra các tên của riêng mình là chúng không được trùng với bất kì từ khoá nào của ngôn ngữ hay của trình biên dịch, ví dụ các tên sau đây luôn luôn được coi là từ khoá theo chuẩn ANSI-C++ và do vậy chúng không thể được dùng để đặt tên
Code:
asm, car, bool, break, marry, catch, to char, class, const, const_cast, continue, default, delete, do, double, dynamic_cast, else, enum, explicit, extern, false, float, for, friend, goto, if, inline, int, long, mutable, namespace, new, operator, private, protected, public, to register, reinterpret_cast, return, short, signed, sizeof, static, static_cast, struct, switch, template, this, throw, true, try, typedef, typeid, typename, union, unsigned, using, virtual, void, volatile, wchar_t
Thêm vào đó, một số biểu diễn khác của các toán tử (operator) cũng không được dùng làm tên vì chúng là những từ được dành riêng trong một số trường hợp.
Code:
and, and_eq, bitand, bitor, compl, not, not_eq, or, or_eq, xor, xor_eq
Trình dịch của bạn có thể thêm một từ dành riêng đặc trưng khác. Ví dụ, rất nhiều trình dịch 16 bit (như các trình dịch cho DOS) còn có thể các từ khoá far, huge và near.
Chú ý: Ngôn ngữ C/C++ là "case sensitive" có nghĩa là phân biệt chữ hoa chữ thường. Ví dụ một biến có tên RESULT khác với result cũng như Result.

3. Sử dụng biến trong chương trình

3.1. Khai báo biến (Declare)
Để có thể sử dụng một biến trong C/C++, đầu tiên chúng ta phải khai báo nó, ghi rõ nó là kiểu dữ liệu nào. Chúng ta chỉ cần viết tên kiểu (như int, short, float...) tiếp theo sau đó là một tên biến hợp lệ. Ví dụ:
Code:
   int a;           //khai báo biến kiểu số nguyên integer
   float mynumber;  //khai báo biến kiểu số thực dấu phẩy động
Dòng đầu tiên khai báo một biến kiểu int với tên là a. Dòng thứ hai khai báo một biến kiểu float với tên mynumber. Sau khi được khai báo, các biến trên có thể được dùng trong phạm vi (scope) của chúng trong chương trình.
Nếu bạn muốn khai báo một vài biến có cùng một kiểu và bạn muốn tiết kiệm công sức viết bạn có thể khai báo chúng trên một dòng, ngăn cách các tên bằng dấu phẩy. Ví dụ:
Code:
int a, b, c;
khai báo ba biến kiểu int (a, b và c) và hoàn toàn tương đương với:
Code:
int a;
int b;
int c;
Các kiểu số nguyên (char, short, long và int) có thể là số có dấu hay không dấu tuỳ theo miền giá trị mà chúng ta cần biểu diễn. Vì vậy khi xác định một kiểu số nguyên chúng ta đặt từ khoá signed (hoặc không cần) hoặc unsigned trước tên kiểu dữ liệu. Ví dụ:
Code:
 unsigned short nNumberOfSons;
signed int nMyAccountBalance;

Nếu ta không chỉ rõ signed hoặc unsigned nó sẽ được coi là có dấu, vì vậy trong khai báo thứ hai chúng ta có thể viết :
Code:
int nMyAccountBalance;
cũng hoàn toàn tương đương với dòng khai báo ở trên. Trong thực tế, rất ít khi người ta dùng đến từ khoá signed. Ngoại lệ duy nhất của luật này kiểu char. Trong chuẩn ANSI-C++ nó là kiểu dữ liệu khác với signed char và unsigned char.
Để có thể thấy rõ hơn việc khai báo trong chương trình, chúng ta sẽ xem xét một đoạn mã C++ ví dụ như sau:
Code:
// thao tác tính toán với các biến
#include <iostream.h>

int main ()
{
//khai báo biến kiểu số nguyên integer
  int a, b;
  int result;

//khởi gán và tính toán
  a = 5;
  b = 2;
  a = a + 1;
  result = a - b;

//in kết quả
  cout << result;

//thoát chương trình
  return 0;
}
Kết quả sau khi thực hiện chương trình là:
Code:
  4
Đừng lo lắng nếu như việc khai báo có vẻ hơi lạ lùng với bạn. Bạn sẽ thấy phần chi tiết còn lại trong phần tiếp theo

3.2. Khởi tạo các biến (Initial)
Khi khai báo một biến, giá trị của nó mặc nhiên là không xác định. Nhưng có thể bạn sẽ muốn nó mang một giá trị xác định khi được khai báo. Để làm điều đó, bạn chỉ cần viết dấu bằng và giá trị bạn muốn biến đó sẽ mang:
kiểu tên_biến = giá_trị_khởi_tạo_ban_đầu;
Ví dụ, nếu chúng ta muốn khai báo một biến int là a chứa giá trị 0 ngay từ khi khởi tạo, chúng ta sẽ viết :
Code:
int a = 0;
Bổ xung vào cách khởi tạo kiểu C này, C++ còn có thêm một cách mới để khởi tạo biến bằng cách bọc một cặp ngoặc đơn sau giá trị khởi tạo. Ví dụ:
Code:
int a (0);
Cả hai cách đều hợp lệ trong C++.
Lưu ý: một biến khi được khởi tạo ta vẫn có thể gán cho nó 1 giá trị khác trong quá trình chương trình thực thi và giá trị đó sẽ hợp lệ trong phạm vi hoạt động của biến đó (xem phần sau)
3.3. Phạm vi hoạt động của biến (Scope)
Tất cả các biến mà chúng ta sẽ sử dụng đều phải được khai báo trước. Một điểm khác biết giữa C và C++ là trong C++ chúng ta có thể khai báo biến ở bất kì nơi nào trong chương trình, thậm chí là ngay ở giữa các lệnh thực hiện chứ không chỉ là ở đầu khối lệnh như ở trong C.
Mặc dù vậy chúng ta vẫn nên theo cách của ngôn ngữ C khi khai báo các biến bởi vì nó sẽ rất hữu dụng khi cần sửa chữa một chương trình có tất cả các phần khai báo được gộp lại với nhau. Bởi vậy, cách thông dụng nhất để khai báo biến là đặt nó trong phần bắt đầu của mỗi hàm (biến cục bộ - local) hay trực tiếp trong thân chương trình, ngoài tất cả các hàm (biến toàn cục - global).
Global variables (biến toàn cục) có thể được sử dụng ở bất kì đâu trong chương trình, ngay sau khi nó được khai báo và bắt buộc phải đặt ở trên cùng ngoài tất cả các hàm hoặc đặt ở file header .h
Local variables (biến cục bộ) có tầm hoạt động bị giới hạn trong phần mã mà nó được khai báo - thường là trong dấu { } gần nhất. Nếu chúng được khai báo ở đầu một hàm (như hàm main), tầm hoạt động sẽ là toàn bộ hàm main. Điều đó có nghĩa là trong ví dụ trên, các biến được khai báo trong hàm main() chỉ có thể được dùng trong hàm đó, không được dùng ở bất kì đâu khác, tức là 1 biến được khai báo trong hàm A thì ko thể sử dụng nó trong hàm B nằm ngoài hàm A.
Bên cạnh các biến toàn cục và cục bộ, còn có các biến ngoài (external). Các biến này không những được dùng trong một file mã nguồn mà còn trong tất cả các file được liên kết trong chương trình.

Trong C++ tầm hoạt động của một biến chính là khối lệnh mà nó được khai báo (một khối lệnh là một tập hợp các lệnh được gộp lại trong một bằng các ngoặc nhọn { } ). Nếu nó được khai báo trong một hàm tầm hoạt động sẽ là hàm đó, còn nếu được khai báo trong vòng lặp thì tầm hoạt động sẽ chỉ là vòng lặp đó....
Hãy xem ví dụ sau và tìm hiểu cách hoạt động của các biến:
Code:
int nGlobal; //biến toàn cục có thể đựơc sử dụng trong cả hàm A và B 
void A()
{
  int a; //biến cục bộ của hàm A
  
  nGlobal = 5; //câu lệnh gán cho biến toàn cục hợp lệ
  b = 1; //trình biên dịch sẽ báo lỗi vì biến này chưa được khai báo trong hàm A
}
void B()
{
  int b;  //biến cục bộ của hàm B
    for(int i=0; i<10; i++)
  {
      int c = 5; //khai báo và khởi tạo biến c local trong vòng for
  }
 c = 1; //không hợp lệ vì biến c chỉ có phạm vi hoạt động trong vòng lặp for
}
3. Các hằng số (Const)
3.1. Giới thiệu các hằng số
Một hằng số là bất kì một biểu thức nào mang một giá trị cố định, ví dụ như các số nguyên sau:
Code:
1776
707
-273
chúng là các hằng mang giá trị số. Chú ý rằng khi biểu diễn một hằng kiểu số chúng ta không cần viết dấu ngoặc kép hay bất kì dấu hiệu nào khác.
Thêm vào những số ở hệ cơ số 10 (thập phân) ( cái mà tất cả chúng ta đều đã biết) C++ còn cho phép sử dụng các hằng số cơ số 8 (bát phân) và 16 (thập lục phân). Để biểu diễn một số hệ cơ số 8 chúng ta đặt trước nó kí tự 0, để biễu diễn số ở hệ cơ số 16 chúng ta đặt trước nó hai kí tự 0x. Ví dụ:
Code:
75 // Cơ số 10
0113 // cơ số 8
0x4b // cơ số 16
Các số thập phân (dạng dấu phẩy động):
Chúng biểu diễn các số với phần thập phân và/hoặc số mũ. Chúng có thể bao gồm phần thập phân, kí tự e (biểu diễn 10 mũ...).
Code:
3.14159 // 3.14159
6.02e23 // 6.02 x 1023
1.6e-19 // 1.6 x 10-19
3.0 // 3.0
Kí tự và xâu kí tự
Trong C++ còn tồn tại các hằng không phải kiểu số như kiểu kí tự và chuỗi. Kiểu kí tự được bao quanh bởi 2 dấu móc đơn, kiểu chuổi bao quanh bởi cặp móc kép:
Code:
'z'
'p'
"Hello world"
"How do you do?"
Hai biểu thức đầu tiên biểu diễn các kí tự đơn, các kí tự được đặt trong dấu nháy đơn ('), hai biểu thức tiếp theo biểu thức các xâu kí tự được đặt trong dấu nháy kép (").
Khi viết các kí tự đơn hay các xâu kí tự cần phải đặt chúng trong các dấu nháy để phân biệt với các tên biến hay các từ khoá. Chú ý:
Code:
x
'x'
x trỏ đến biến x trong khi 'x' là kí tự hằng 'x'.

Các kí tự đơn và các xâu kí tự có một tính chất riêng biệt là các mã điều khiển. Chúng là những kí tự đặc biệt mà không thể được viết ở bất kì đâu khác trong chương trình như là mã xuống dòng (\n) hay tab (\t). Tất cả đều bắt đầu bằng dấu xổ ngược (\). Sau đây là danh sách các mã điều khiển và ý nghĩa của nó:
Code:
\n
xuống dòng 

\r
lùi về đầu dòng 

\t
kí tự tab

\v
căn thẳng theo chiều dọc 

\b
backspace 

\f
sang trang

\a
Kêu bíp 

\'
dấu nháy đơn 

\"
dấu nháy kép 

\
dấu hỏi 

\\
kí tự xổ ngược
Ví dụ:
Code:
'\n' //sẽ chèn thêm kí hiệu ngắt dòng
'\t' //sẽ chèn thêm 1 dấu tab
"Left \t Right"
"one\ntwo\nthree"
Thêm vào đó, để biểu diễn một mã ASCII bạn cần sử dụng kí tự xổ ngược (\) tiếp theo đó là mã ASCII viết trong hệ cơ số 8 hay cơ số 16. Trong trường hợp đầu mã ASCII được viết ngay sau dấu sổ ngược, trong trường hợp thứ hai, để sử dụng số trong hệ cơ số 16 bạn cần viết kí tự x trước số đó (ví dụ \x20 hay \x4A).
Các hằng chuỗi kí tự có thể được viết trên nhiều dòng nếu mỗi dòng được kết thúc bằng một dấu sổ ngược (\), cách này có ích khi ta có 1 chuỗi quá dài khó quan sát khi viết chương trình:
Code:
"string expressed in \
two lines"
Bạn có thể nối một vài hằng xâu kí tự ngăn cách bằng một hay vài dấu trống, kí tự tab, xuống dòng hay bất kì kí tự trống nào khác.
Code:
"we form" "a unique" "string" "of characters"
3.2. Định nghĩa các hằng (#define)
Bạn có thể định nghĩa các hằng với tên mà bạn muốn để có thể sử dụng thường xuyên mà không mất tài nguyên cho các biến bằng cách sử dụng chỉ thị #define. Đây là dạng của nó:
#define tên_hằng giá_trịVí dụ:
Code:
#define PI 3.14159265
#define NEWLINE '\n'
#define WIDTH 100
chúng định nghĩa 3 hằng số mới. Sau khi khai báo bạn có thể sử dụng chúng như bất kì các hằng số nào khác, ví dụ:
Code:
 circle = 2 * PI * r;
 cout << NEWLINE;
Trong thực tế việc duy nhất mà trình dịch làm khi nó tìm thấy một chỉ thị #define là thay thế các tên hằng tại bất kì chỗ nào chúng xuất hiện (như trong ví dụ trước, PI, NEWLINE hay WIDTH) bằng giá trị mà chúng được định nghĩa. Vì vậy các hằng số #define được coi là các hằng số macro.
Chỉ thị #define không phải là một lệnh thực thi, nó là chỉ thị tiền xử lý (pre-processor), đó là lý do trình dịch coi cả dòng là một chỉ thị và dòng đó không cần kết thúc bằng dấu chấm phẩy. Nếu bạn thêm dấu chấm phẩy vào cuối dòng, nó sẽ được coi là một phần của giá trị định nghĩa hằng.
3.3. Khai báo các hằng (const)
Với tiền tố const bạn có thể khai báo các hằng với một kiểu xác định như là bạn làm với một biến
Code:
const int width = 100;
const to char tab = '\t';
const zip = 12440;
Trong trường hợp kiểu không được chỉ rõ (như trong ví dụ cuối) trình dịch sẽ coi nó là kiểu int.
LeGiang is offline   Trả Lời Với Trích Dẫn
Old 05-06-2006, 06:24 PM   #4
Hồ sơ
LeGiang
Banned
 
Tham gia ngày: Jan 2005
Số bài viết: 473
Tiền: 25
Thanks: 41
Thanked 593 Times in 241 Posts
LeGiang is an unknown quantity at this point
Default

Trước tiên bạn cần cài bộ Visual Studio 6 (hoặc bộ .NET)- đây sẽ là nơi bạn trực tiếp soạn thảo mã nguồn, tạo ứng dụng và thư viện MSDN - là bộ thư viện hỗ trợ lập trình không thể thiếu của các lập trình viên. Bộ VS6 bạn chỉ cần cài đặt đĩa 1, còn bộ MSDN bạn nên cài đầy đủ 3 CD.
Mục tiêu của toàn bộ bài học là để bao quát một số kĩ thuật đi từ cơ bản đến nâng cao, từ đó tạo ra kết quả cuối cùng là một chương trình nhỏ hoạt động ngầm như 1 spy bên dưới hệ thống Windows và theo dõi hoạt động của các chương trình khác, bao gồm bắt phím đánh vào, bắt thao tác chuột và chụp hình.


BÀI 1: TẠO ỨNG DỤNG DẠNG DIALOG ĐƠN GIẢN</span>
<a href=\'http://www.thuvientinhoc.com/forums/lofiversion/index.php/t115.html\' target=\'_blank\'>nguồn</a>
<span style=\'color:purple\'>
1. Tạo Ứng Dụng Nhanh với MFC AppWizard

Sau khi cài đặt xong VS6, bạn vào menu Start/Programs để chạy ứng dụng Microsoft Visual C++ 6.0, kết quả tương tự như hình sau:
<img src=\'http://www.whatvn.com/images/vc/sample1/1.gif\' border=\'0\' alt=\'user posted image\' />
(Lưu ý: để tiết kiệm hình ảnh tôi đã resize kích thước hình nhỏ lại)

Ta sẽ bắt đầu tạo 1 ứng dụng dialog đơn giản với Application Wizard qua 4 bước. Bạn chọn menu File/New (hoặc nhấn Ctrl-N) để mở cửa sổ New Project. VC++ 6 cho phép bạn tạo rất nhiều kiểu ứng dụng, từ database, ATL COM, web, dll, đến các ứng dụng Win 32 Console (chạy dạng cửa sổ console), ở đây ta sẽ chọn MFC AppWizard (exe). MFC là một bộ thư viện lập trình hướng đối tượng mà VC++ hổ trợ nhằm giúp tạo cho lập trình viên khả năng lập trình thân thiện, dễ sử dụng. Loại bỏ bớt những dài dòng phức tạp khi phải viết toàn bộ mã nguồn của một chương trình Windows, MFC cung cấp cung cấp khả năng tự động sinh ra bộ khung (framework) cho ứng dụng của bạn thông qua một số lớp (class) có tính kế thừa. Dần dần các bạn sẽ hiểu và thấy MFC dùng AppWizard lợi ích như thế nào.
Trở lại với ví dụ của chúng ta. Trong cửa sổ New Project, bạn tiếp tục nhập vào Tên Đồ án (Project name), ở đây ta nhập vào "sample1", ta để ý thấy trong hộp Platforms, VC++ chỉ hỗ trợ ứng dụng chạy trên nền Win 32, ta tiếp tục nhấn OK. Xem hình sau:
<img src=\'http://www.whatvn.com/images/vc/sample1/2.gif\' border=\'0\' alt=\'user posted image\' />
Ở bước 1 của MFC AppWizard, có 3 kiểu ứng dụng trình Wizard hỗ trợ đó là: Single document (SDI-Single Document Interface, một ứng dụng chỉ mở một cửa sổ con cho 1 tài liệu. Notepad, Wordpad là một kiểu SDI - trong một cửa sổ Notepad bạn ko thể có 2 cửa sổ con), Multiple documents (MDI - Multiple Documents Interface - MS Word là MDI, cùng một thời điểm bạn có thể mở cùng lúc nhiều tài liệu), Dialog based (Ứng dụng dạng cửa sổ đơn dialog). Ta tiếp chọn Dialog based và nhấn Next.
<img src=\'http://www.whatvn.com/images/vc/sample1/3.gif\' border=\'0\' alt=\'user posted image\' />
Tiếp tục bước 2, MFC AppWizard cung cấp mộ số thuộc tính cho bạn chọn cho ứng dụng dạng dialog. Thuộc tính About box nếu chọn sẽ cung cấp cho bạn 1 cửa sổ con About dạng dialog dành cho bạn khi muốn cung cấp một số thông tin về tác giả, ngày tháng tạo ứng dụng, sau này khi ứng dụng hoàn thành bạn có thể truy xuất đến nó thông qua System Menu (icon góc trên bên trái ứng dụng); Context-sensitive help sẽ tạo kèm theo ứng dụng một bộ help đơn giản; 3D controls hỗ trợ cách nhìn 3D có bóng cho các control, bạn check và uncheck vào nó và xem hình ví dụ bên cạnh; ActiveX controls cung cấp khả năng sử dụng các ActiveX control trong ứng dụng của bạn. Xem hình bên dưới, bạn tiếp tục nhấn Next.
<img src=\'http://www.whatvn.com/images/vc/sample1/4.gif\' border=\'0\' alt=\'user posted image\' />
Ở bước 3, có một số thông tin khác như generate source comments, tức là tự động sinh chú thích cho các đoạn mã, giúp bạn dễ theo dõi hơn; use MFC library cho bạn chọn cách thức sử dụng các thư viện của MFC, tôi sẽ trở lại với vấn đề này sau. Xem hình bên dưới, bạn tiếp tục nhấn Next.
<img src=\'http://www.whatvn.com/images/vc/sample1/5.gif\' border=\'0\' alt=\'user posted image\' />
Bước cuối cùng của MFC AppWizard, trình Wizard cho phép bạn chọn tên cho các lớp quan trọng đã được sinh tự động (class) cho ứng dụng của bạn. Ở đây ta giữ nguyên và chọn Finish.
<img src=\'http://www.whatvn.com/images/vc/sample1/6.gif\' border=\'0\' alt=\'user posted image\' />
Nếu thực hiện nhanh, quá trình này chỉ mất khoảng vài giây để tạo ngay 1 ứng dụng đơn giản nhất dạng dialog. Bạn xem hình bên dưới:
<img src=\'http://www.whatvn.com/images/vc/sample1/7.gif\' border=\'0\' alt=\'user posted image\' />
Cửa sổ VC++ 6 bao gồm hệ thống thực đơn, các thanh công cụ, cửa sổ Workspace bên trái, cửa sổ soạn thảo mã và thiết kế resource bên phải, cửa sổ trạng thái (status) dưới cùng.

Trở lại với các loại ứng dụng MFC. Trình AppWizard sẽ tự động phát sinh một số lớp chính tạo nên bộ khung cho ứng dụng loại đó. Đối với SDI, sẽ bao gồm lớp Application (cấp cao nhất), kế đến là lớp MainFrame, lớp View và Document. Tên các lớp là kết hợp của project name và kiểu. Vd nếu ta đặt tên project là "sample1", ta sẽ thấy có các lớp CSample1App, CMainFrame, CSample1View, CSample1Doc. Đối với ứng dụng MDI sẽ gồm các lớp giống như của SDI và có thể lớp ChildFrame tên CChildFrm đại diện cho 1 cửa sổ con trong ứng dụng. Đối với dạng ứng dụng dialog, bao gồm CSample1App, CSample1Dlg (đại diện cho dialog chính), CAboutDlg (đại diện cho dialog About).
Lớp Application sẽ là nơi nhận và phân phối các message từ hệ thống đến cho ứng dụng, kế đến là lớp MainFrame, ChildFrame, View và Document. Ứng dụng SDI chỉ cung cấp 1 document (vd: bạn chỉ mở được 1 file văn bản) và muốn thể hiện thông tin của document đó lên màn hình ta phải thông qua 1 view. Lớp View sẽ là nơi ta thực hiện các thao tác vẽ, xuất thông tin hay thể hiện văn bản lên màn hình. Mở rộng hơn, kiểu MDI cho phép mở cùng lúc nhiều document, mỗi document đại diện bởi 1 view. Ở kiểu dialog, các thao tác giao diện tập trung ở lớp dialog, không có lớp view.
LeGiang is offline   Trả Lời Với Trích Dẫn
Old 10-06-2006, 04:02 PM   #5
Hồ sơ
HoaCucVang
Super Moderator
 
HoaCucVang's Avatar
 
Tham gia ngày: Dec 2004
Tuổi: 39
Số bài viết: 902
Tiền: 25
Thanks: 312
Thanked 430 Times in 203 Posts
HoaCucVang is an unknown quantity at this point
Default

cam on legiang nha, nhung ma sao hom nay minh khong go dau duoc vay troi, po tay luọn MA legiang ne, chi hoc lap trinh da 2 nam roi nhung chang ranh duoc thu gi ca vi hoc tran lan het Pascal, DepC roi BC, gio lai phai hoc ca VC,hic hic met muon chet may ong thay nay bat tu hoc, moi mon viet 1 ngon ngu khac nhau riet roi lon tum lum, co cach nao do bi lon khong troi!!!!!!!!!!!
__________________

HoaCucVang is offline   Trả Lời Với Trích Dẫn
Old 10-06-2006, 04:04 PM   #6
Hồ sơ
HoaCucVang
Super Moderator
 
HoaCucVang's Avatar
 
Tham gia ngày: Dec 2004
Tuổi: 39
Số bài viết: 902
Tiền: 25
Thanks: 312
Thanked 430 Times in 203 Posts
HoaCucVang is an unknown quantity at this point
Default

Vao dien dan da duoc 1 nam ruoi roi ma con "tap tanh viet lach" xau ho qua di vi khong chiu post bai, hinh nhu minh lam bieng qua thi phai!...............
__________________

HoaCucVang is offline   Trả Lời Với Trích Dẫn
Old 21-06-2006, 03:37 PM   #7
Hồ sơ
LGi
Banned
 
LGi's Avatar
 
Tham gia ngày: May 2006
Số bài viết: 34
Tiền: 25
Thanks: 0
Thanked 12 Times in 10 Posts
LGi is an unknown quantity at this point
Default

BÀI 2: THU NHỎ ỨNG DỤNG XUỐNG KHAY HỆ THỐNG SYSTEM TRAY</span>
<a href=\'http://www.thuvientinhoc.com/forums/lofiversion/index.php/t1091.html\' target=\'_blank\'><span style=\'color:red\'>My Webpage</a>
Chúng ta trở lại với ứng dụng dạng hộp thoại dialog đơn giản ở bài 1. Bài 2 này chúng ta sẽ bắt đầu đề cập 1 số kĩ thuật phức tạp hơn của việc lập trình trên môi trường Windows, đó là tạo 1 ứng dụng có thể thu nhỏ dạng icon ở system tray và tạo menu popup đáp ứng khi người dùng nhấn phải chuột lên icon ở system tray; đồng thời cũng đề cập đến kĩ năng viết code kết hợp với MFC của VC++ 6.0.
1. Thu nhỏ ứng dụng xuống system tray
Ở ví dụ bài 1 chúng ta đã có 1 nút bấm "Hiện Messagebox", ta sẽ thêm 1 nút bấm khác để phục vụ khi người dùng nhấn vào nó để thu nhỏ ứng dụng xuống system tray. Bạn hãy làm tương tự như ở bài 1, ở khung WorkSpace bên trái, chọn tab Resource, sau đó chọn phần Dialog và nhấp đúp vào ID có tên IDD_SAMPLE1_DIALOG để vào chế độ soạn resource cho dialog ứng dụng của chúng ta. Sau đó bạn chọn hình nút bấm trên thanh công cụ bên phải và nhấp chuột vào giữa hình dialog, bạn nhấp chuột phải lên button vừa tạo và chọn Properties, kế tiếp chọn tab General, gõ vào ô Caption nội dung sau: "Minimize on tray". Lưu ý trước khi tạo button này, bạn dùng chuột kéo button trước đó (Hien Messagebox) sang trái để chúng ko đè lên nhau.
Kết quả bạn có thể thấy như sau:
<img src=\'http://www.whatvn.com/images/vc/sample1/31.gif\' border=\'0\' alt=\'user posted image\' />
Tương tự bài 1, ta tạo event handler cho sự kiện nhấp chuột lên button, bạn nhấp đúp chuột lên button vừa tạo để mở hộp thoại tạo mới hàm xử lý, gõ vào tên hàm xử lý sự kiện là OnMinimize và nhấn OK. Như vậy một hàm mới tên là OnMinimize() sẽ được tạo trong code của bạn nhưng chưa có lệnh xử lý, chúng ta sẽ thêm mã lệnh sau.
Code:
Void CSample1Dlg::OnMinimize() 
{
 //TODO:... 
}
Chúng ta sẽ bắt đầu viết mã lệnh phục vụ khi nhấn vào button thì thực hiện thu nhỏ ứng dụng xuống system tray.

Để phục vụ cho việc thu nhỏ, ta dùng hàm Shell API có tên Shell_NotifyIcon. API viết tắt của Application Programming Interface, hay gọi là Giao diện lập trình ứng dụng trên Windows. Nói một cách đơn giản API là tập hợp các hàm, các cấu trúc dữ liệu, thư viện mà hệ thống hỗ trợ cho các lập trình viên tạo nên các ứng dụng. Các API thường được đặt trong các tập tin thư viện như .dll nằm trong thư mục cài đặt Windows.
Hàm Shell_NotifyIcon làm nhiệm vụ gửi thông điệp yêu cầu (message) có chức năng cụ thể nào đó tới system tray. Ví dụ yêu cầu tạo tray icon, xóa tray icon... Khai báo hàm như sau:
Code:
BOOL Shell_NotifyIcon(
  DWORD dwMessage, 
  PNOTIFYICONDATA lpdata
);
Lưu ý:[/b] Bạn cần 1 tài liệu tham khảo lập trình VC++ hoặc thư viện MSDN để có thể tham khảo các API, cấu trúc,cách sử dụng của chúng...khi cần vì ko thể nào nhớ hết được.

Trong đó, dwMessage chỉ định kiểu yêu cầu. Ở đây ta quan tâm đến 2 loại message là:
- NIM_ADD : Tạo 1 icon ở system tray hay còn gọi là khu vực trạng thái taskbar
- NIM_DELETE : Xóa 1 icon khỏi system tray

lpdata là 1 con trỏ đến 1 cấu trúc dữ liệu chứa các thông tin cần thiết cho icon hoạt động ở system tray, như hình icon, mã điều khiển, thông điệp hệ thống sẽ nhận được khi user kích hoạt vào icon... Ta xem cấu trúc NOTIFYICONDATA và chú thích sau dấu //
Code:
typedef struct _NOTIFYICONDATA { 
  DWORD cbSize; // kích thước của cấu trúc
  HWND hWnd; // window handle của cửa sổ ứng dụng
  UINT uID; // mã điều khiển của trayicon 
  UINT uFlags; // mặt nạ cờ chứa các option đặc biệt
  UINT uCallbackMessage; // thông điệp ta sẽ tự định nghĩa cho trayicon
  HICON hIcon; // hình biểu tượng cho trayicon
  #if (_WIN32_IE < 0x0500)
    TCHAR szTip[64]; // tooltip trợ giúp nhanh khi di chuyển chuột lên trayicon
  #else
    TCHAR szTip[128];
  #endif
  #if (_WIN32_IE >= 0x0500)
    DWORD dwState; 
    DWORD dwStateMask; 
    TCHAR szInfo[256]; 
    union {
      UINT uTimeout; 
      UINT uVersion; 
    } DUMMYUNIONNAME;
    TCHAR szInfoTitle[64]; 
    DWORD dwInfoFlags; 
  #endif
  #if (_WIN32_IE >= 0x600)
    GUID guidItem;
  #endif
} NOTIFYICONDATA, *PNOTIFYICONDATA;
Các bạn chú ý 1 số chú thích trong cấu trúc NOTIFYICONDATA để hiểu sơ công dụng vì ta sẽ dùng đến chúng.

Như vậy yêu cầu để thực hiện hàm Shell_NotifyIcon là ta điền đầy đủ các tham số và gọi hàm, ta sẽ đi từng tham số một. Với tham số đầu tiên, vì ta muốn tạo ứng dụng dạng trayicon, ta sẽ dùng NIM_ADD. Cấu trúc ở tham số thứ hai phức tạp hơn, chúng ta cần chuẩn bị 1 số thông tin cho cấu trúc này.
Trước tiên ta cần vẽ 1 icon dùng để hiện ở system tray. Đây là dấu hiệu nhận biết ứng dụng đang ở system tray. Bạn trở lại Resource tab, chọn vào phần Icon, nhấn chuột phải và chọn Insert Icon, Resource Editor sẽ tự tạo cho bạn 1 icon có ID là IDI_ICON1, bạn nhấn chuột phải lên ID chọn Properties để thay đổi ID thành IDI_TRAY1. Sau đó ở phần vẽ icon bên phải, bạn dùng thanh công cụ vẽ 3 hình tròn như hình sau:
<img src=\'http://www.whatvn.com/images/vc/sample1/32.gif\' border=\'0\' alt=\'user posted image\' />
Sau đó bạn nhấn nút Save để lưu lại icon mình vừa tạo.
Đến đây ta tiếp tục với 1 vấn đề khác, đó là khi đã thu nhỏ dạng trayicon, icon này cần đáp ứng khi có sự kiện nhấp chuột lên nó. Tham số thứ 2 lpdata của hàm Shell_NotifyIcon yêu cầu truyền vào 1 cấu trúc NOTIFYICONDATA. Cấu trúc này cho phép ta định nghĩa thêm 1 windows message kiểu người dùng đặt vào 1 thuộc tính tên là uCallbackMessage. Như vậy ta sẽ định nghĩa 1 message như sau:
Code:
#define WM_NOTIFYOFINCONONTRAY (WM_USER+1) //WM_USER
là 1 hằng số được định nghĩa sẵn

và đặt dòng #define này ở đầu file .cpp
Ta cũng cần chỉ định window handle của cửa sổ cần thu nhỏ, ta sẽ dùng biến m_hWnd là thuộc tính có sẵn của tất cả các ứng dụng dạng window dùng MFC.
Kết quả ta sẽ khai báo cấu trúc như sau:
Code:
NOTIFYICONDATA tnid; 
  HICON hicon; //handle đến icon của ứng dụng trayicon
  hicon = AfxGetApp()->LoadIcon(IDI_TRAY1); // ta dùng hàm LoadIcon để load icon có sẵn trong resource mà ta tạo lúc trước

  tnid.cbSize = sizeof(NOTIFYICONDATA); //khai báo trứơc kích thước buffer
  tnid.hWnd = m_hWnd; //window handle
  tnid.uID = IDI_TRAY1; //ta tận dụng luôn id của trùng với icon đã tạo ở trên
  tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; 
  tnid.uCallbackMessage = WM_NOTIFYOFINCONONTRAY; // chỉ định window message người dùng
  tnid.hIcon = hicon; //handle tray icon
  _tcscpy(tnid.szTip, _T("Right click to show menu")); // tạo 1 tooltip
Lưu ý thuộc tính uFlags ở trên gồm 3 giá trị kết hợp OR với nhau:
- NIF_MESSAGE: cho phép dùng window message với thuộc tính uCallbackMessage
- NIF_ICON: cho phép chỉ icon vào thuộc tính hIcon
- NIF_TIP: cho phép dùng tooltip với thuộc tính szTip. Ta dùng hàm _tcscpy để copy chuỗi vào biến szTip.
Để biết thêm chi tiết các thuộc tính trên, bạn tham khảo MSDN.

Như vậy, sau khi thiết lập các thông tin cần thiết cho cấu trúc NOTIFYICONDATA, ta chỉ việc gọi hàm Shell_NotifyIcon với tham số NIM_ADD để thu nhỏ xuống systray:
Code:
  Shell_NotifyIcon(NIM_ADD, &tnid);
Lưu ý tham số thứ 2 yêu cầu 1 con trỏ, tức là cần truyền vào địa chỉ của cấu trúc NOTIFYICONDATA nên ta cần để biến CODE
tnid
với dấu & đằng trước.
Khi ứng dụng đã xuất hiện ở system tray, ta ko cần hiện dạng dialog trên màn hình, do đó ta gọi hàm ShowWindow để ẩn ứng dụng (nếu ko ẩn thì ta sẽ có cả ứng dụng dialog hiện hành và trayicon).
Code:
 ShowWindow(SW_HIDE);
Bạn có thể làm tương tự khi muốn xóa icon khỏi system tray với tham số NIM_DELETE truyền vào hàm Shell_NotifyIcon.

Hãy xem hàm đầy đủ như sau kết hợp cả hai tính năng thêm và xóa icon ở system tray, với tham số truyền vào kiểu BOOLEAN.
Code:
//Hàm thu nhỏ/xóa TrayIcon
//If this code works, it's written by W_Hat Zorro. If not, I don't know who wrote it.
//Nếu bMinimize=TRUE: thu nhỏ
//  bMinimize=FALSE: xóa trayicon
BOOL CSample1Dlg::MinimizeOnTray(BOOL bMinimize)
{
  BOOL res; 
  NOTIFYICONDATA tnid; 
  HICON hicon;

//Xóa trayicon
  if(!bMinimize)
  {
tnid.cbSize = sizeof(NOTIFYICONDATA); 
tnid.hWnd = m_hWnd; 
tnid.uID = IDI_TRAY1;    
res = Shell_NotifyIcon(NIM_DELETE, &tnid); 

ShowWindow(SW_SHOW);

return TRUE;
  }
// thu nhỏ ứng dụng xuống system tray
  hicon = AfxGetApp()->LoadIcon(IDI_TRAY1);
  if(hicon==NULL)
  {
 return FALSE;
  }

  tnid.cbSize = sizeof(NOTIFYICONDATA); 
  tnid.hWnd = m_hWnd; 
  tnid.uID = IDI_TRAY1; 
  tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; 
  tnid.uCallbackMessage = WM_NOTIFYOFINCONONTRAY; 
  tnid.hIcon = hicon; 
  _tcscpy(tnid.szTip, _T("Right click to show menu")); 

  res = Shell_NotifyIcon(NIM_ADD, &tnid); 

  if(hicon!=NULL) 
  {
    DestroyIcon(hicon);
  }
  
  ShowWindow(SW_HIDE);

  return TRUE;
}
Bạn xem lại project trong Visual C++ vừa tạo để xem code thật sự của phần trên.
Như vậy ta đã thực hiện xong phần thu nhỏ và xóa trayicon. Phần kế tiếp ta sẽ viết hàm nhận thông điệp khi người dùng nhấp chuột phải lên trayicon và từ đó cho hiện menu lựa chọn.

2. Tạo menu popup cho biểu tượng system tray
Như đã đề cập ở phần trên, ta đã định nghĩa 1 user defined message có tên là WM_NOTIFYOFINCONONTRAY. Khi người dùng thực hiện các thao tác nhấn phím, rê chuột lên trayicon hoặc nhấp chuột lên nó, Windows sẽ gửi đến ứng dụng chúng ta 1 message chính là message được gán vào thuộc tính tnid.uCallbackMessage ở trên. Vì đây là message tự định nghĩa nên ta phải tự viết hàm xử lý biến cố cho nó, ta sẽ phải tuân theo chuẩn của Windows khi viết hàm này. Với MFC, hàm xử lý message sẽ có dạng sau:
Code:
 afx_msg void MessageProc(WPARAM wParam, LPARAM lParam);
Với ứng dụng chúng ta sẽ đặt tên là:
Code:
afx_msg void OnNotifyOfIconOnTray(WPARAM wParam, LPARAM lParam)
;
Ở đây afx_msg là macro được MFC định sẵn chỉ định hàm xử lý thông điệp. Thông số WPARAM sẽ cho biết giá trị uID đã được gán trước trong cấu trúc NOTIFYICONDATA, LPARAM sẽ là giá trị cụ thể thông điệp hệ thống (nhấn phím nào, chuột trái, phải...)
Trong cửa sổ WorkSpace, bạn chọn tab ClassView và nhấp đúp và tên class CSample1Dlg để mở file header của class. Sau đó bạn gõ thêm dòng định nghĩa hàm ở trên, kết quả như hình vẽ sau:
<img src=\'http://www.whatvn.com/images/vc/sample1/33.gif\' border=\'0\' alt=\'user posted image\' />
Kế đến ta cần viết thêm mã để hàm OnNotifyOfIconOnTray có hiệu lực. Trên cửa sổ ClassView bạn nhấp đúp vào 1 hàm nào đó của class CSample1Dlg để mở cửa sổ code của class này (nằm trong file sample1Dlg.cpp). Tìm đến phần BEGIN_MESSAGE_MAP(CSample1Dlg, CDialog) và thêm dòng ON_MESSAGE( WM_NOTIFYOFINCONONTRAY, OnNotifyOfIconOnTray) như hình vẽ:
<img src=\'http://www.whatvn.com/images/vc/sample1/34.gif\' border=\'0\' alt=\'user posted image\' />
Đến đây ta sẽ cần tạo 1 menu popup cho trayicon, bạn trở lại phần Resource tab, nhấn chuột phải lên mục Menu và chọn Insert Menu. Resource Editor sẽ tự động tạo cho bạn 1 menu có id là IDR_MENU1 và 1 menu item rỗng có viền lấm chấm sẽ được tạo ở phần editor bên phải, bạn nhấn chuột phải lên nó và chọn Properties như hình bên dưới:
<img src=\'http://www.whatvn.com/images/vc/sample1/35.gif\' border=\'0\' alt=\'user posted image\' />
Trong hộp thoại Properties, bạn nhập vào ô Caption chuỗi "test" và nhấn Enter. Sau khi nhấn Enter trình editor tự động chuỗi bạn vừa nhập vào menuitem và tạo thêm 1 menuitem rỗng ngay bên dưới. Bạn làm tương tự để tạo thêm 2 item sổ xuống lần lượt là "&Show" và "&Exit". Kết quả như hình sau:
<img src=\'http://www.whatvn.com/images/vc/sample1/36.gif\' border=\'0\' alt=\'user posted image\' />
Vì menuitem đầu tiên có caption là "Test" nên trình Resource Editor của VC++ sẽ tự tạo cho các item của nó những số ID duy nhất có phần bắt đầu dạng ID_TEST_Caption. Ví dụ như ID_TEST_SHOW, ID_TEST_EXIT.

Tương tự như các nút bấm trên dialog ở trên, mỗi menuitem cần 1 hàm xử lý sự kiện cho nó khi người dùng chọn item đó trên menu. Bước này ta sẽ dùng trình ClassWizard của VC++.
Bạn nhấn chuột phải lên menuitem có tên "Show" và chọn ClassWizard...từ menu sổ xuống.
Một hộp thoại hiện ra hỏi bạn "Adding a class", ta chưa dùng chức năng này nên bạn chọn Cancel để bỏ qua.
Trong hộp thoại MFC ClassWizard, ở listbox Object IDs, bạn chọn ID có tên ID_TEST_SHOW và ô bên phải có tên "Messages", bạn nhấp đúng vào mục COMMAND. Hộp thoại Add Member Function sẽ hiện ra, bạn nhập tên hàm xử lý là OnTestShow (thường thì tên hàm được chọn sẵn cho bạn), bạn nhấn OK. Bạn làm tương tự với ID_TEST_EXIT. Như hình sau:
<img src=\'http://www.whatvn.com/images/vc/sample1/37.gif\' border=\'0\' alt=\'user posted image\' />
Sau khi tạo xong 2 hàm, ở phần Member functions bạn nhấp đúp vào hàm OnTestShow và sẽ được đưa trở lại cửa sổ code, ở hàm OnTestShow bạn nhập thêm dòng MinimizeOnTray(FALSE);, tham số FALSE truyền vào hàm MinimizeOnTray ta đã tạo ở trên sẽ dùng để xóa icon khỏi systemtray và hiện lại cửa sổ dialog (bạn xem lại hàm MinimizeOnTray ở trên). Tương tự với hàm OnTestExit, bạn thêm dòng OnOK();, đây là hàm ngầm định của ứng dụng dạng dialog dùng để kết thúc ứng dụng.
Code:
void CSample1Dlg::OnTestShow() 
{
  MinimizeOnTray(FALSE);
}
void CSample1Dlg::OnTestExit() 
{
  OnOK();
}
Nhân tiện bạn viết thêm code cho hàm OnMinimize ở trên ta đã tạo sẵn 1 hàm gọi MinimizeOnTray với tham số bằng TRUE để khi nguời dùng nhấn nút sẽ thu nhỏ ứng dụng:
Code:
void CSample1Dlg::OnMinimize() 
{
  MinimizeOnTray(TRUE);
}
Như vậy ta đã xong phần viết lệnh xử lý cho menu, bây giờ bạn về cuối cửa sổ code và viết thêm hàm OnNotifyOfIconOnTray như ta đã đề cập ở trên, code như sau:
Code:
void CSample1Dlg::OnNotifyOfIconOnTray(WPARAM wParam, LPARAM lParam)
{
  UINT uID; 
  UINT uMouseMsg; 

  uID = (UINT) wParam; 
  uMouseMsg = (UINT) lParam; 

  if(uMouseMsg == WM_RBUTTONUP && uID == IDI_TRAY1)
  { 
CPoint mouse;
GetCursorPos(&mouse); //lấy vị trí nhấp chuột
this->SetFocus(); //set focus cho ứng dụng

CMenu lMenu;
lMenu.LoadMenu(IDR_MENU1);
CMenu* pMenu = lMenu.GetSubMenu(0);
pMenu->TrackPopupMenu(TPM_RIGHTALIGN|TPM_RIGHTBUTTON, mouse.x, mouse.y, this, NULL);
  } 
}
Như đã đề cập, hàm xử lý message OnNotifyOfIconOnTray có 2 tham số wParam, lParam. Để sử dụng chúng ta sẽ ép kiểu (type-cast) về kiểu UINT (unsigned integer - số nguyên không dấu) với wParam chứa ID của trayicon và lParam chứa thông điệp Windows gửi cho ứng dụng.
Ở đây ta chỉ cho hiện menu khi người dùng nhấp chuột phải lên trayicon nên bạn sẽ thấy lệnh if với điều kiện uMouseMsg == WM_RBUTTONUP, WM_RBUTTONUP là windows message có sẵn, bạn xem MSDN để biết thêm các message khác và với ID là IDI_TRAY1 như ta đã thiết lập khi gọi hàm Shell_NotifyIcon ở trên, ta sẽ cho hiện menu popup.
Cách thức sử dụng 1 menu dạng popup như ta đã tạo bên trên, bạn dùng lớp MFC có tên CMenu với phương thức LoadMenu và tham số truyền vào là ID của menu đã tạo, ở đây là IDR_MENU1. Lưu ý đây là dạng menubar có 1 item "Test" và sổ xuống 1 popup, nên ta cần dùng hàm GetSubMenu để lấy đúng menu popup, tham số 0 chỉ item đầu tiên của menubar (lưu ý trong C/C++, PHP, Java... các phần tử trong 1 mảng, list bắt đầu từ 0), hàm này sẽ trả về 1 con trỏ đến menupop up, nên bạn cần khai báo thêm 1 biến con trỏ để lưu giữ menu đó.
Code:
CMenu* pMenu = lMenu.GetSubMenu(0);
Công việc cuối cùng là gọi hàm TrackPopupMenu của lớp CMenu, hàm này được khai báo như sau:
Code:
BOOL TrackPopupMenu( UINT nFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = NULL );
nFlags chỉ định kiểu menu xuất hiện. Giá trị TPM_RIGHTALIGN chỉ menu sẽ hiện canh lề bên phải vị trí nhấp chuột, TPM_RIGHTBUTTON chỉ định right-click
int x, int y chỉ định vị trí nhấp chuột, ta sẽ dùng hàm GetCursorPos để lấy vị trí này
CWnd* pWnd chỉ định con trỏ đến ứng dụng sẽ nhận thông điệp xử lý chuột, ta dùng con trỏ định trước this. Đây là dạng con trỏ chỉ đến cửa sổ hiện tại
LPCRECT lpRect = NULL chỉ định phạm vi hiệu lực của menu. Giá trị ngầm định của tham số này là NULL, chỉ định nếu người nhấp chuột ngoài vùng này thì menu tự động đóng lại.
Lưu ý bạn cần thêm hàm this->SetFocus(); để chỉ định điểm nhập focus cho ứng dụng trayicon hỗ trợ thêm cho việc nếu người nhấp chuột ngoài vùng này thì menu tự động đóng lại.

Để tiết kiệm thời gian, bạn copy phần code của hàm OnNotifyOfIconOnTray tôi đã tạo sẵn ở trên và dán vào cuối vùng code của CSample1Dlg.
Đến đây là hoàn thành phần tạo ứng dụng có khả năng thu nhỏ xuống system tray. Bạn nhấn Ctrl-F5 để biên dịch và chạy ứng dụng, nhấn lên nút "Minimize on tray" để thu nhỏ và nhấp chuột phải lên trayicon để sử dụng menu.

Toàn bộ project VC++6.0 tôi đã tạo sẵn các bạn có thể download tại đây:
<a href=\'http://www.whatvn.com/data/sample1.zip\' target=\'_blank\'>http://www.whatvn.com/data/sample1.zip</a>
LGi is offline   Trả Lời Với Trích Dẫn
Trả lời



Quyền Sử Dụng Ở Diễn Ðàn
Bạn không được quyền gởi bài
Bạn không được quyền gởi trả lời
Bạn không được quyền gởi kèm file
Bạn không được quyền sửa bài

vB code đang Mở
Smilies đang Mở
[IMG] đang Mở
HTML đang Tắt
Chuyển đến


Website sử dụng phần mềm vBulletin phiên bản 3.6.8
do Công ty TNHH Jelsoft giữ bản quyền từ 2000 - 2024.
Múi giờ GMT +7. Hiện tại là 09:09 PM.

Hội CHS Lê Quý Đôn-Long An giữ bản quyền nội dung của website này

Tự động[F9]TELEX VNI VIQR VIQR* TắtKiểm chính tảDấu cũ
phan mem quan ly ban hang | thuê vps