25/05/2018, 09:07

Xử lý tập tin trong lập trình c trên windows

Tập tin là một đơn vị lưu trữ cơ bản để máy tính phân biệt các khối thông tin khác nhau, được lưu trữ trên các thiết bị lưu trữ phụ như là đĩa, băng từ, và được tổ chức theo các nhóm gọi là thư mục. Để xử lý tập tin, ta có thể dùng ...

Tập tin là một đơn vị lưu trữ cơ bản để máy tính phân biệt các khối thông tin khác nhau, được lưu trữ trên các thiết bị lưu trữ phụ như là đĩa, băng từ, và được tổ chức theo các nhóm gọi là thư mục.

Để xử lý tập tin, ta có thể dùng các hàm trong C chuẩn như fopen, fclose, fread, fwrite, fseek, … trong môi trường Windows. Các hàm này được hỗ trợ trong thư viện stdio.h. Chúng ta sẽ không bàn về các hàm này ở đây.

Trong phần này, chúng ta sẽ tìm hiểu các hàm thao tác trên tập tin của Win32® cho phép các ứng dụng tạo, mở, cập nhật và xoá các tập tin, cũng như tìm hiểu các thông số hệ thống về tập tin.

Tạo và mở tập tin

Win32® API cung cấp hàm CreateFile để tạo một tập tin mới hoặc mở một tập tin đã có sẵn.

HANDLE CreateFile(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);

Trường lpFileName trỏ đến chuỗi ký tự zero xác định tên tập tin cần mở hoặc tạo. Trường dwDesiredAccess xác định cách thức truy cập đối tượng. Một ứng dụng có thể thực hiện truy cập đọc, ghi, đọc-ghi, sử dụng một hay kết hợp các giá trị sau :

Trường dwDesiredAccess xác định cách truy cập đối tượng
Giá trị Ý nghĩa
0 Xác định truy vấn thiết bị đến một đối tượng. Một ứng dụng có thể truy vấn thuộc tính thiết bị mà không cần phải truy cập thiết bị.
GENERIC_READ Xác lập hình thức truy cập đọc. Dữ liệu có thể đọc từ tập tin, đồng thời dịch chuyển con trỏ tập tin. Để truy cập đọc-ghi, ta kết hợp với cờ GENERIC_WRITE.
GENERIC_WRITE Xác lập hình thức truy cập ghi. Dữ liệu có thể được ghi vào tập tin, đồng thời dịch chuyển con trỏ. Để có thể truy cập đọc-ghi, ta kết hợp với cờ GENERIC_READ.

Trường dwShareMode thiết lập các bit cờ xác định cách chia sẻ đối tượng (tập tin). Nếu dwShareMode bằng 0, đối tượng không thể chia sẻ. Khi đó, ta không thể thao tác trên đối tượng cho đến khi đóng handle. Để chia sẻ đối tượng, ta kết hợp một trong các cờ sau :

Trường dwShareMode xác định cách chia sẻ đối tượng
FILE_SHARE_DELETE Sử dụng trong Windows NT : Thao tác trên đối tượng chỉ thực hiện nếu yêu cầu truy cập xoá.
FILE_SHARE_READ Thao tác trên đối tượng chỉ thực hiện nếu yêu cầu truy cập đọc.
FILE_SHARE_WRITE Thao tác trên đối tượng chỉ thực hiện nếu yêu cầu truy cập ghi.

Trường lpSecurityAttributes trỏ đến cấu trúc SECURITY_ATTRIBUTES xác định handle đối tượng có được chuyển cho các tiến trình con hay không. Ở đây chúng ta không dùng, và thiết lập giá trị là NULL.

Trường dwCreationDisposition xác lập thao tác tạo tập tin mới hay mở tập tin đã có. Dùng một trong các giá trị sau :

Trường dwCreationDisposition xác lập thao tác tập tin
CREATE_NEW Tạo mới một tập tin. Hàm này thất bại nếu tập tin đã có.
CREATE_ALWAYS Tạo mới một tập tin. Nếu tập tin đã tồn tại, hàm sẽ tạo chồng lên, đồng thời xoá các thuộc tính hiện hành của tập tin.
OPEN_EXISTING Mở một tập tin. Hàm thất bại nếu tập tin chưa có sẵn.
OPEN_ALWAYS Mở một tập tin nếu có sẵn. Nếu tập tin chưa tồn tại, hàm sẽ tạo tập tin như sử dụng cờ CREATE_NEW.
TRUNCATE_EXISTING Mở một tập tin. Khi mở, hệ thống khởi tạo kích thước tập tin lại về 0 byte. Tiến trình gọi cần mở tập tin ít nhật với dạng truy cập GENERIC_WRITE. Hàm thất bại nếu không tồn tại tập tin.

Trường dwFlagsAndAttributes xác định các thuộc tính và cờ cho tập tin. Ta có thể kết hợp các thuộc tính sau :

Trường dwFlagsAndAttributes xác định các thuộc tính và cờ cho tập tin.
FILE_ATTRIBUTE_ARCHIVE Tập tin archive. Ứng dụng dùng thuộc tính này để đánh dấu tập tin có thể sao lưu hoặc loại bỏ.
FILE_ATTRIBUTE_HIDDEN Tập tin ẩn. Không hiển thị trong danh sách các tập tin thông thường trong các thư mục.
FILE_ATTRIBUTE_NORMAL Tập tin không có thuộc tính nào khác. Thuộc tính này thường được dùng duy nhất.
FILE_ATTRIBUTE_OFFLINE Dữ liệu tập tin không có sẵn. Dữ liệu được chỉ định di chuyển vật lý vào vùng lưu trữ offline.
FILE_ATTRIBUTE_READONLY Tập tin chỉ đọc. Ứng dụng không thể ghi hoặc xoá dữ liệu trong tập tin.
FILE_ATTRIBUTE_SYSTEM Tập tin là một phần của hệ điều hành, hoặc được sử dụng đặc biệt trong hệ thống.
FILE_ATTRIBUTE_TEMPORARY Tập tin được dùng cho vùng lưu trữ tạm. Sau khi ứng dụng kết thúc, tập tin sẽ được xóa.

Các cờ xác định tập tin khá phức tạp, chúng ta không bàn kỹ ở đây. Trường cuối cùng là hTemplateFile xác định handle truy cập GENERAL_READ đến tập tin tạm. Tập tin tạm có vai trò hỗ trợ các thuộc tính tập tin và thuộc tính mở rộng cho tập tin được tạo. Trong Windows 95, giá trị hTemplateFile cần được gán bằng NULL.

Nếu thành công hàm trả về handle của tập tin xác định. Ngược lại, giá trị trả về là INVALID_HANDLE_VALUE.

Lưu ý, việc thiết lập giá trị dwDesiredAccess cho phép ứng dụng có thể truy vấn các thuộc tính thiết bị mà không thực sự truy cập thiết bị. Điều này rất hữu dụng, ví dụ trong trường hợp ứng dụng muốn xác định kích thước cùng các định dạng ổ đĩa mềm mà không cần phải có đĩa trong ổ đĩa.

Khi tạo một tập tin, hàm CreateFile thực hiện các chức năng sau:

  • Kết hợp các cờ và thuộc tính tập tin được xác định bởi cờ dwFlagsAndAttributes với giá trị là FILE_ATTRIBUTE_ARCHIVE.
  • Thiết lập kích thước tập tin bằng 0.
  • Chép các thuộc tính mở rộng của tập tin tạm vào tập tin mới nếu biến hTemplateFile xác định.

Khi mở một tập tin có sẵn, hàm CreateFile thực hiện các chức năng sau :

  • Kết hợp các cờ xác định bởi dwFlagsAndAttributes với các thuộc tính của tập tin hiện có. Hàm CreateFile sẽ bỏ qua các thuộc tính của tập tin xác định bởi cờ dwFlagsAndAttributes.
  • Thiết lập kích thước tập tin dựa vào giá trị của dwCreationDisposition.
  • Bỏ qua giá trị của biến hTemplateFile.

Nếu hàm tạo một tập tin trên ổ đĩa mềm không có đĩa mềm, hoặc trên CD-ROM không có đĩa CD, hệ thống sẽ đưa ra một hộp thoại thông điệp (message box) yêu cầu người dùng đưa đĩa mềm hoặc đĩa CD vào. Để hệ thống không thực hiện thao tác trên, cần thiết lập giá trị uMode trong hàm SetErrorMode là SEM_FAILCRITICALERRORS.

UINT SetErrorMode(UINT uMode);

Trong ví dụ sau, hàm CreateFile mở một tập tin đã có để đọc :

HANDLE hFile;

hFile = CreateFile("MYFILE.TXT", // mở tập tin MYFILE.TXT

GENERIC_READ, // mở để đọc

FILE_SHARE_READ, // chia sẻ để đọc

NULL, // không bảo mật

OPEN_EXISTING, // chỉ mở tập tin đã có

FILE_ATTRIBUTE_NORMAL, //Tập tin thường

NULL); // không có thộc tính tạm

if (hFile == INVALID_HANDLE_VALUE)

{

ErrorHandler("Could not open file."); // lỗi xử lý

}

Để xoá tập tin trên, trước hết ta đóng tập tin lại.

CloseHandle(hFile);

DeleteFile("MYFILE.TXT");

Trong ví dụ sau, hàm tạo một tập tin mới và mở ở chế độ ghi.

HANDLE hFile;

hFile = CreateFile("MYFILE.TXT", // tập tin MYFILE.TXT

GENERIC_WRITE, // tạo để ghi

0, // không chia sẻ

NULL, // không bảo mật

CREATE_ALWAYS, // ghi chồng nếu đã có

FILE_ATTRIBUTE_NORMAL | // tập tin bình thường

FILE_FLAG_OVERLAPPED, // không đồng bộ I/O

NULL); // không thuộc tính tạm

if (hFile == INVALID_HANDLE_VALUE)

{

ErrorHandler("Could not open file."); // lỗi xử lý

}

 

Tạo tập tin tạm

Các ứng dụng có thể nhận một tập tin duy nhất cho tập tin tạm bằng cách sử dụng hàm GetTempFileName. Để xác định đường dẫn đến thư mục chứa tập tin tạm được tạo, ta dùng hàm GetTempPath.

Hàm GetTempFileName tạo tên một tập tin tạm. Tên tập tin đầy đủ gồm đường dẫn nối với một chuỗi ký tự số thập lục phân thể hiện tên tập tin, và phần mở rộng là .TMP.

UINT GetTempFileName(LPCTSTR lpPathName, LPCTSTR lpPrefixString, UINT uUnique, LPTSTR lpTempFileName);

Trường lpPathName trỏ đến một chuỗi ký tự (kết thúc bằng ký tự NULL) xác định đường dẫn của tập tin, dùng các ký tự ANSI. Nếu trường này bằng NULL, hàm thất bại.

Trường lpPrefixString trỏ đến một chuỗi ký tự (kết thúc bằng ký tự NULL). Hàm sử dụng 3 ký tự đầu tiên của chuỗi như phần tiền tố của tập tin. Các ký tự sử dụng phải là ky tự ANSI.

Trường uUnique xác định một số nguyên không dấu (mà) hàm chuyển thành chuỗi ký tự thập lục phân sử dụng trong việc tạo tập tin tạm.

Trường lpTempFileName trỏ đến vùng nhớ đệm chứa tên tập tin tạm. Trường này là một chuỗi ký tự kết thúc NULL các ký tự ANSI. Độ dài vùng nhớ đệm được xác định bởi giá trị MAX_PATH của thư mục tương ứng.

Tập tin tạo được sẽ có dạng như sau :

pathpreuuuu.TMP

Trong đó path là đường dẫn, xác định bởi giá trị lpPathName; pre là 3 ký tự đầu của chuỗi lpPrefixString; và uuuu là giá trị thập lục phân của uUnique.

Khi thoát khỏi hệ điều hành (tắt máy chẳng hạn), các tập tin tạm tạo bằng hàm này sẽ tự động bị xoá.

Để tránh các lỗi khi chuyển chuỗi ANSI, ứng dụng cần gọi hàm CreateFile trước để tạo tập tin tạm.

Nếu giá trị uUnique bằng 0, hàm GetTempFileName xác lập một con số duy nhất dựa trên thời điểm hiện tại của hệ thống. Nếu tập tin đã có, hệ thống tự tăng lên một số mới cho đến khi có một tên duy nhất.

Nếu thực hiện thành công, hàm trả về con số duy nhất xác định trong trường uUnique. Ngược lại, giá trị trả về là 0.

Để thu nhận đường dẫn tập tin tạm, ta dùng hàm GetTempPath.

DWORD GetTempPath(DWORD nBufferLength, LPTSTR lpBuffer);

Trường nBufferlength xác định kích thước vùng đệm chuỗi ký tự xác định bởi lpBuffer. Trường lpBuffer trỏ đến vùng đệm nhận chuỗi ký tự xác định đường dẫn tập tin tạm. Chuỗi ký tự kết thức bằng ký tự ‘’, ví dụ : C:TEMP.

Nếu thành công, hàm trả về độ lớn xác định kích thước chuỗi zero. Nếu giá trị trả về lớn hơn nBufferLength, giá trị trả về sẽ là kích thước vùng đệm cần để chứa đường dẫn. Ngược lại, giá trị trả về là 0 nếu hàm thất bại.

Sao chép và di chuyển tập tin

Để chép (copy) một tập tin, ta cần mở ở chế độ chỉ đọc. Sau đó dùng hàm CopyFile để chép vào một tập tin mới.

BOOL CopyFile(LPCTSTR lpExistingFileName, LPCTSTR lpNewFileName, BOOL bFailIfExists);

Trường lpExistingFileName và lpNewFileName trỏ đến chuỗi (kết thúc NULL) xác định tên tập tin đã có và tên tập tin mới. Trường bFialIfExists xác định cách tạo tập tin với tên mới trên. Nếu trường được thiết lập là TRUE, và tập tin có tên lpNewFileName đã tồn tại, hàm thất bại. Nếu trường được thiết lập là FALSE và tập tin đã tồn tại, hàm sẽ tạo tập tin mới chồng lên tập tin cũ.

Nếu thành công, hàm trả về giá trị khác 0. Ngược lại, giá trị trả về là 0.

Để di chuyển (move) một tập tin, trước hết cần phải đóng tập tin lại (nếu đang mở). Ta dùng hàm MoveFile. Hàm này thực hiện thao tác đổi tên một tập tin hay thư mục (bao gồm cả các tập tin con trong thư mục).

BOOL MoveFile(LPCTSTR lpExistingFileName, LPCTSTR lpNewFileName);

Hai trường trên lần lượt trỏ đến tên tập tin (thư mục) hiện có và tên tập tin (thư mục) mới. Tên tập tin (thư mục) mới cần phải chưa có trong đường dẫn của nó. Tên tập tin mới có thể trên một hệ thống hay ổ đĩa khác, trong khi tên thư mục mới phải cùng ổ đĩa với thư mục cũ.

Nếu thành công, giá trị trả về khác 0. Ngược lại, giá trị trả về là 0.

Ví dụ sau minh họa việc tạo và sử dụng tập tin tạm để copy một tập tin. Đầu tiên ứng dụng mở tập tin ORIGINAL.TXT bằng cách sử dụng hàm CreateFile. Sau đó ứng dụng sử dụng hàm GetTempFileNameCreateFile để tạo tập tin tạm. Ứng dụng đọc từng khối 4K dữ liệu vào vùng đệm, chuyển nội dung trong vùng đệm sang chữ hoa, và viết chúng xuống tập tin tạm. Sau khi chuyển toàn bộ tập tin trên sang tập tin tạm, ta đổi tập tin tạm thành ALLCAPS.TXT bằng cách dùng hàm MoveFile.

HANDLE hFile;

HANDLE hTempFile;

DWORD dwBytesRead, dwBytesWritten, dwPos;

char szTempName[MAX_PATH];

char buffer[4096];

/* Mở tập tin có sẵn */

hFile = CreateFile("ORIGINAL.TXT", // tên tập tin

GENERIC_READ, // mở để đọc

0, // không chia sẻ

NULL, // không bảo mật

OPEN_EXISTING, // tập tin đã có sẵn

FILE_ATTRIBUTE_NORMAL, // tập tin thông thường

NULL); // không thuộc tính tạm

if (hFile == INVALID_HANDLE_VALUE)

{

ErrorHandler("Could not open file."); // xử lý lỗi

}

/* Tạo tập tin tạm */

GetTempFileName("TEMP", // thư mục chứa tập tin tạm

"NEW", // phần đầu tập tin tạm

0, // tạo tên duy nhất

szTempName); // vùng đệm tên

hTempFile = CreateFile((LPTSTR) szTempName, // tên tập tin

GENERIC_READ | GENERIC_WRITE, // mở dạng đọc-ghi

0, // không chia sẻ

NULL, // không bảo mật

CREATE_ALWAYS, // tạo chồng nếu tập tin đã có

FILE_ATTRIBUTE_NORMAL, // tập tin thông thường

NULL); // không thuộc tính tạm

if (hTempFile == INVALID_HANDLE_VALUE)

{

ErrorHandler("Could not create temporary file.");

}

/* Đọc khối 4K vào vùng đệm. Chuyển tất cả các ký tự trong vùng đệm sang dạng chữ hoa. Viết vùng đệm vào tập tin tạm. */

do

{

if (ReadFile(hFile, buffer, 4096, &dwBytesRead, NULL))

{

CharUpperBuff(buffer, dwBytesRead);

WriteFile(hTempFile, buffer, dwBytesRead, &dwBytesWritten, NULL);

}

} while (dwBytesRead == 4096);

/* Đóng cả hai tập tin */

CloseHandle(hFile);

CloseHandle(hTempFile);

/* Chuyển tập tin tạm vào tập tin mới ALLCAPS.TXT */

if (!MoveFile(szTempName, "ALLCAPS.TXT"))

{

ErrorHandler("Could not move temp. file.");

}

Hàm CloseHandle đóng một tập tin đang mở. Xem phần 7.3.6.

Đọc và ghi dữ liệu vào tập tin

Mỗi tập tin đang mở có một con trỏ tập tin xác định byte kế tiếp sẽ được đọc hoặc ghi. Khi một tập tin mở lần đầu tiên, hệ thống thiết lập con trỏ tập tin tại vị trí đầu tập tin. Mỗi khi đọc hoặc ghi vào một byte, con trỏ tập tin tự động dịch chuyển. Để thay đổi vị trí này, ta dùng hàm SetFilePointer.

DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod);

Trường hFile là handle của tập tin chứa con trỏ tập tin cần di chuyển. Handle này cần được tạo dưới dạng truy cập GENERIC_READ hoặc GENERIC_WRITE.

Trường lDistanceToMove và lpDistanceToMoveHigh xác định số byte con trỏ cần dịch chuyển. Nếu lpDistanceToMoveHigh bằng NULL, 32-bit thấp của lDistanceToMove xác định số byte cần di dời con trỏ. Ngược lại, hai trường trên thiết lập một giá trị 64-bit (không dấu) thể hiện số byte cần di dời con trỏ.

Trường dwMoveMethod xác định điểm gốc mà con trỏ cần di dời. Nếu trường này bằng FILE_BEGIN, điểm gốc là điểm đầu tiên (byte thứ 0) của tập tin. Nếu là FILE_CURRENT, điểm gốc là vị trí hiện tại của con trỏ. Và FILE_END xác định điểm gốc là vị trí cuối hiện tại của tập tin.

Nếu thành công, và lpDistanceToMoveHigh bằng NULL, giá trị trả về là DWORD thấp của con trỏ tập tin. Nếu lpDistanceToMoveHigh khác NULL, hàm trả về DWORD thấp của con trỏ tập tin, và trỏ trường này đến DWORD cao của con trỏ tập tin.

Nếu hàm thất bại và lpDistanceToMoveHigh bằng NULL, giá trị trả về là 0xFFFFFFFF. Để biết thêm thông tin lỗi, ta dùng hàm GetLastError. Nếu hàm trả về 0xFFFFFFFF và lpDistanceToMoveHigh, có thể hàm thành công hoặc thất bại, cần phải gọi hàm GetLastError để xác định. Đoạn chương trình sau trình bày vấn đề này :

/* Trường hợp 1: Gọi hàm với lpDistanceToMoveHigh == NULL */

/* Cố gắng di chuyển con trỏ tập tin của hFile một đoạn */

dwPtr = SetFilePointer(hFile, lDistance, NULL, FILE_BEGIN);

if (dwPtr == 0xFFFFFFFF) // Kiểm tra thất bại

{

dwError = GetLastError() ; // Nhận mã lỗi

. . . // Xử lý lỗi

} // Cuối phần xử lý lỗi

 

/* Trường hợp 2: Gọi hàm với lpDistanceToMoveHigh != NULL */

/* Cố gắng di chuyển con trỏ tập tin hFile một đoạn dài */

dwPtrLow = SetFilePointer(hFile, lDistLow, & lDistHigh, FILE_BEGIN);

/* Kiểm tra thất bại */

if (dwPtrLow==0xFFFFFFFF&&(dwError=GetLastError())!=NO_ERROR)

{

// Xử lý lỗi

// . . .

} // Cuối phần xử lý lỗi

Để di chuyển tiến, ta thiết lập độ dịch chuyển một giá trị dương. Ngược lại, thiết lập giá trị âm để di chuyển lùi con trỏ tập tin. Nếu giá trị con trỏ tập tin sau khi dịch chuyển âm, hàm thất bại, và mã lỗi mà hàm GetlastError trả về là ERROR_NEGATIVE_SEEK.

Nếu muốn di chuyển con trỏ đến cuối tập tin để ghi tiếp, ta cũng có thể dùng hàm SetEndOfFile.

BOOL SetEndOfFile(HANDLE hFile);

Trường hFile là handle của tập tin cần di chuyển con trỏ đến cuối tập tin cần được tạo với dạng truy cập GENERAL_WRITE. Nếu thành công, hàm trả về giá trị khác 0. Ngược lại, giá trị trả về là 0.

Hàm ReadFile đọc dữ liệu từ một tập tin, từ điểm xác định bởi con trỏ tập tin. Sau khi đọc, con trỏ tập tin dịch chuyển một đoạn ứng với số byte thật sự đọc được. Tương tự, để ghi vào tập tin ta dùng hàm WriteFile.

BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped);

BOOL WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped);

Handle tập tin hFile cần được tạo với dạng truy cập GENERAL_READ hoặc GENERAL_WRITE.

Trường lpBuffer trỏ đến vùng nhớ đệm nhận dữ liệu đọc từ tập tin, hay chứa dữ liệu cần ghi. Trường nNumberOfBytesToRead xác định số byte cần đọc. Trường lpNumberOfBytesRead trỏ đến các byte đọc được. Hàm ReadFile thiết lập giá trị này bằng 0 trước khi thực hiện các thao tác khác để kiểm tra lỗi.

Trường nNumberOfBytesToWrite xác định số byte cần ghi. Nếu trường này bằng 0, hàm không ghi dữ liệu, nhưng vẫn được gọi thực hiện. Trường lpNumberOfBytesWritten xác định số byte ghi được, trường này cũng được hàm WriteFile thiết lập về 0 trước khi thực hiện các thao tác khác để kiểm tra lỗi.

Cuối cùng, trường lpOverlapped trỏ đến cấu trúc OVERLAPPED. Nếu tập tin được mở với cờ FILE_FLAG_OVERLAPPED, giá trị này phải khác NULL. Để đơn giản ta thiết lập giá trị bằng NULL, hàm sẽ đọc (ghi) tập tin từ vị trí con trỏ hiện tại và hàm chỉ trả kết quả về sau khi đọc (ghi) xong.

Hàm ReadFile sẽ dừng với một số lý do sau : thao tác ghi hoàn tất cuối đường ống ghi, đã đọc hết số byte yêu cầu, hoặc xảy ra lỗi khi đọc.

Nếu thành công, các hàm trả về giá trị khác 0. Ngược lại, giá trị trả về bằng 0.

Khoá và mở khoá tập tin

Mặc dù hệ thống cho phép nhiều ứng dụng có thể mở và ghi vào cùng một tập tin, các ứng dụng không nên thực hiện song song. Để ngăn chặn ứng dụng khác ghi vào phần dữ liệu của mình, ta có thể sử dụng hàm LockFile. Để mở khoá tập tin, cho phép các ứng dụng khác truy cập vùng dữ liệu, ta dùng hàm UnlockFile.

BOOL LockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh);

BOOL UnlockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh);

Trường hFile là handle của tập tin được mở dưới dạng GENERAL_READ hoặc GENERAL_WRITE, hoặc cả hai.

Trường dwFileOffsetLow và dwFileOffsetHigh xác định word thấp và cao của byte offset đầu tiên cần khoá hay mở khoá.

Trường nNumberofBytesToLockLow (nNumberofBytesTo UnlockLow) và nNumberOfBytesToLockHigh (nNumberOf BytesToUnlockHigh) xác định word thấp và cao độ dài khoảng byte cần khoá hay mở khóa.

Nếu thành công, hàm trả về giá trị khác 0. Ngược lại, giá trị trả về là 0.

Ví dụ sau đây nối một tập tin vào một tập tin khác. Ứng dụng sử dụng hàm CreateFile mở tập tin ONE.TXT để đọc, và TWO.TXT để viết. Sau đó ứng dụng nối phần dữ liệu của tập tin ONE.TXT vào cuối tập tin TWO.TXT bằng cách đọc (dùng hàm ReadFile) và ghi (dùng hàm WriteFile) từng khối 4K dữ liệu. Trước khi viết vào tập tin thứ hai, ứng dụng dịch chuyển con trỏ đến cuối tập tin bằng cách dùng hàm SetFilePointer, sau đó khóa vùng cần ghi dùng hàm LockFile. Sau khi thực hiện thao tác ghi xong, ta mở khoá, dùng hàm UnlockFile, để các ứng dụng khác có thể sử dụng tập tin này.

HANDLE hFile;

HANDLE hAppend;

DWORD dwBytesRead, dwBytesWritten, dwPos;

char buff[4096];

/* Mở một tập tin đã có */

hFile = CreateFile("ONE.TXT", // mở tập tin ONE.TXT

GENERIC_READ, // mở để đọc

0, // không chia sẻ

NULL, // không bảo mật

OPEN_EXISTING, // chỉ mở tập tin đã tồn tại

FILE_ATTRIBUTE_NORMAL, // tập tin bình thường

NULL); // không có thuộc tính tạm

if (hFile == INVALID_HANDLE_VALUE)

{

ErrorHandler("Could not open ONE."); // xử lý lỗi

}

/* Mở tập tin đã có. Nếu chưa có, tạo tập tin mới */

hAppend = CreateFile("TWO.TXT", // mở tập tin TWO.TXT

GENERIC_WRITE, // mở để ghi

0, // không chia sẻ

NULL, // không bảo mật

OPEN_ALWAYS, // mở tập tin cũ hoặc tạo mới

FILE_ATTRIBUTE_NORMAL, // tập tin bình thường

NULL); // không có thuộc tính tạm

if (hAppend == INVALID_HANDLE_VALUE)

{

ErrorHandler("Could not open TWO."); // xử lý lỗi

}

/* Nối tập tin thứ nhất vào cuối tập tin thứ hai. Khoá tập tin thứ hai để ngăn chặn các tiến trình khác truy cập khi đang ghi. Mở khoá sau khi ghi xong */

do

{

if (ReadFile(hFile, buff, 4096, &dwBytesRead, NULL))

{

dwPos= SetFilePointer(hAppend, 0, NULL, FILE_END);

LockFile(hAppend, dwPos, 0, dwPos+dwBytesRead, 0);

WriteFile(hAppend, buff, dwBytesRead, &dwBytesWritten, NULL);

UnlockFile(hAppend, dwPos, 0, dwPos+dwBytesRead, 0);

}

} while (dwBytesRead == 4096);

/* Đóng cả hai tập tin */

CloseHandle(hFile);

CloseHandle(hAppend);

 

Đóng và xoá tập tin

Để sử dụng hiệu quả tài nguyên hệ thống, ứng dụng cần đóng các tập tin khi không cần dùng nữa bằng cách gọi hàm CloseHandle. Nếu ứng dụng bị ngắt và tập tin vẫn đang mở, hệ thống sẽ tự động đóng tập tin này.

BOOL CloseHandle(HANDLE hObject);

Nếu thành công, hàm trả về giá trị khác 0. Ngược lại, giá trị trả về là 0. Để biết các thông tin lỗi mở rộng, ta dùng hàm GetLastError.

Để xoá một tập tin, ta dùng hàm DeleteFile. Lưu ý rằng tập tin này cần phải đóng trước khi bị xóa.

BOOL DeleteFile(LPCTSTR lpFileName);

Trường lpFileName trỏ đến chuỗi (kết thúc bằng ký tự NULL) xác định tập tin cần xoá. Nếu thành công, hàm trả về khá trị khác 0. Ngược lại, giá trị trả về là 0. Hàm thất bại nếu tập nếu tập tin cần xoá không tồn tại. Trong Windows NT, hàm không thể xoá các tập tin đang mở. Tuy nhiên, trong Windows 95, tập tin đang mở vẫn có thể bị xoá.

7.3.7 Xử lý thư mục

Khi ứng dụng tạo một tập tin mới, hệ điều hành sẽ thêm tập tin này vào một thư mục xác định. Để tạo và xoá một thư mục, ta dùng hàm CreateDirectoryRemoveDirectory.

BOOL CreateDirectory(LPCTSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes);

BOOL RemoveDirectory(LPCTSTR lpPathName);

Trường lpPathName trỏ đến chuỗi (kết thúc bằng ký tự NULL) xác định đường dẫn thư mục cần tạo (hay cần xoá). Kích thước chuỗi mặc định giới hạn cho đường dẫn là MAX_PATH ký tự. Trong trường hợp xoá thư mục, đường dẫn cần xác định thư mục là thư mục rỗng.

Trường lpSecurityAttributes trỏ đến cấu trúc SECURITY_ATTRIBUTES chứa trường lpSecurityDescriptor xác định mô tả mật của thư mục mới. Để đơn giản, ta gán giá trị lpSecurityAttributes bằng NULL, khi đó thư mục mới nhận các giá trị mô tả mật mặc định.

Nếu thành công, các hàm trả về giá trị khác 0. Ngược lại, giá trị trả về là 0.

Thư mục cuối của đường dẫn đang sử dụng gọi là thư mục hiện hành. Để xác định thư mục hiện hành, ứng dụng gọi hàm GetCurrentDirectory. Để thay đổi thư mục hiện hành, ứng dụng gọi hàm SetCurrentDirectory.

DWORD GetCurrentDirectory(DWORD nBufferLength, LPTSTR lpBuffer);

Trường nBufferLength xác định kích thước vùng đệm của chuỗi ký tự thể hiện thư mục hiện hành. Vùng đệm này phải dài đủ để chứa cả ký tự NULL cuối chuỗi. Trường lpBuffer sẽ chứa chuỗi ký tự này.

Nếu thành công, hàm trả về giá trị xác định số ký tự được ghi vào vùng đệm, không chứa ký tự NULL. Ngược lại, giá trị trả về là 0. Nếu vùng đệm lpBuffer không đủ lớn, giá trị trả về xác định kích thước cần thiết của vùng đệm, gồm cả byte NULL đáng dấu kết thúc chuỗi.

BOOL SetCurrentDirectory(LPCTSTR lpPathName);

Trường lpPathName trỏ đến chuỗi ký tự (kết thúc bằng NULL) xác định đường dẫn mới. Nếu thành công, hàm trả về giá trị khác 0. Ngược lại, giá trị trả về là 0.

0