2010年9月10日 星期五

CFile 的注意事項

以下是我在使用 CFile 時曾經發生不順利的地方,雖然網路上查 CFile 的個類方法太方便了,但還是記下來備忘。

CFile 若有下 modeCreate 的屬性時,會產生一個全新的空檔案,若是希望 CFile 在檔案存在的情況下使用 Open 舊檔案不產生新檔,必須再加上 CFile::modeNoTruncate 屬性。


fileLog.Open(strFullFilePath, File::modeReadWrite|CFile::modeNoTruncate|CFile::modeCreate)


若 ThreadA 在寫入檔案的同時,要讓 ThreadB 讀取,通常用於解碼時,A 由前往後解,解到某個比例就通知 B 使用,常發生在下載 pdf、ppt 投影片檔或影片預覽等狀況,這時要用 shareDenyWrite 和 shareDenyRead,例如


int main()
{
  ::BeginThread(ThreadA);
  return TRUE;
}

UINT ThreadA()
{
  BOOL bFinish = FALSE;
  CFile file;
  file.Open(_T("Temp.xml"),
  CFile::modeCreate|CFile::modeWrite|CFile::shareDenyWrite);
  while(!bFinish)
  {
    file.write();
    if(/*下載了了5%或300KB之類的*/)
    {
      ::BeginThread(ThreadB);
    }
    bFinish = /*下載完沒*/;
  }
  return TRUE;
}

UINT ThreadB()
{
  CFile file;
  file.Open(_T("Temp.xml"), CFile::modeRead|CFile::shareDenyRead);
  while(file.Read(btBuf, 1024))
  {
  }
  return TRUE;
}


若要讓執行緒 A 與執行緒 B 同時讀一個檔案,則是兩處 Open 都要加上 shareDenyNone,理由很直觀,因為 ThreadA 讀時要允許 ThreadB 也可以讀,ThreadB 在讀時,ThreadA 又不一定已經讀完關閉了,所以也要允許別人可以讀,例如


int main()
{
  ::BeginThread(ThreadA);
  ::BeginThread(ThreadB);
  return TRUE;
}

UINT ThreadA()
{
  CFile file;
  file.Open(_T("Temp.xml"), CFile::modeRead|CFile::shareDenyNone);
  while(file.Read(btBuf, 1024))
  {
  }
  return TRUE;
}

UINT ThreadB()
{
  CFile file;
  file.Open(_T("Temp.xml"), CFile::modeRead|CFile::shareDenyNone);
  while(file.Read(btBuf, 1024))
  {
  }
  return TRUE;
}


有時在記憶體不能亂用的平台上,像手機或 Pad 等硬體,例如 Windows mobile 每一個 Application 只能使用 32MB 的主記憶體,這種情況就比較麻煩,尤其產生的是無法由檔頭得知容量的檔案類型,如 mp3 是由一個一個 frame 組成的,以取樣率 128Kbits 的 mp3 檔來看,意即每個 frame 為 16KBytes (一秒),再由多少個 frame 計算出整首歌的總時間,好死不死我們的 mp3 要從網路上下載下來,不可能等到下載完才播,所以我們要產生一個空的 mp3 讓其他行程知道這個 mp3 的總長度,但又不能一次 new 出 5MB 的空間塞給 CFile,這很可能會爆記憶體,這時可以使用一次 100KBytes 的方式塞入檔案,


int main()
{
  DWORD dwWrote = 0;
  ULONGLONG ullSize = file.GetLength(); // 例如 5MB
  DWORD dwMaxBytes = (DWORD)ullSize - 1;
  do {
    if(dwMaxBytes - dwWrote >= 102400) // 100KBytes
    {
      BYTE bt[10240] = {0};
      fileMP3.Write(bt, 10240);
      dwWrote += 10240;
    }
    else
    {
      int nLastBytes = dwMaxBytes - dwWrote;
      BYTE* bt = new BYTE[nLastBytes];
      ::ZeroMemory(bt, nLastBytes);
      fileMP3.Write(bt, nLastBytes);
      dwWrote = (DWORD)ullSize;
      delete [] bt;
    }
  } while(dwWrote < ullSize);
  fileMP3.SeekToBegin(); // 記得把檔案指標移到檔頭以免等等從尾巴開始寫
  return TRUE;
}


如此就可以用比較不會爆記憶體的方式產生一個空的檔案。

沒有留言:

張貼留言