Windowsは、ファイルポインタを移動して単にEOFを設定したしようなファイルの場合に、 ファイルの書き込みが発生したタイミングで、ファイルの未使用領域を全て0で埋める動作をします。 なので、巨大なファイルを作成した場合、次のファイル書き込みの際に大量のセクタを0で埋める動作をする為に、 膨大な時間がかかる事となります。SetFileValidData関数であらかじめ有効なデータとして設定しておく事で、 Windowsがセクタを勝手に0で埋める事が無くなり、必要なデータだけを高速に書き込む事ができます。
#include <tchar.h>
#include <iostream>
#include <string>
#include <windows.h>
/*
特権設定する
*/
HRESULT EnablePrivilege
(
TCHAR* wpPrivilegeName
, BOOL bEnable
)
{
BOOL bRet;
// トークンハンドル
HANDLE hToken;
/*
プロセスに関連付けられているアクセストークンを開く
*/
bRet = ::OpenProcessToken(
GetCurrentProcess()
, 0
// | TOKEN_ASSIGN_PRIMARY
// | TOKEN_DUPLICATE
// | TOKEN_IMPERSONATE
| TOKEN_QUERY
// | TOKEN_QUERY_SOURCE
| TOKEN_ADJUST_PRIVILEGES
// | TOKEN_ADJUST_GROUPS
// | TOKEN_ADJUST_DEFAULT
// | TOKEN_ADJUST_SESSIONID
, &hToken
);
if ( 0 == bRet ) {
// エラー
return( ::HRESULT_FROM_WIN32( ::GetLastError() ) );
}
// ローカル一意識別子
LUID tLuid;
/*
指定されたシステムで使われているローカル一意識別子(LUID)を取得
*/
bRet = LookupPrivilegeValue(
NULL // システムを指定する文字列のアドレス
, wpPrivilegeName // 特権を指定する文字列のアドレス
, &tLuid // ローカル一意識別子のアドレス
);
if ( 0 == bRet ) {
// エラー
DWORD dwLastError = ::GetLastError();
::CloseHandle( hToken );
return( ::HRESULT_FROM_WIN32( dwLastError ) );
}
TOKEN_PRIVILEGES tTokenPrivileges;
tTokenPrivileges.PrivilegeCount = 1;
tTokenPrivileges.Privileges[ 0 ].Luid = tLuid;
tTokenPrivileges.Privileges[ 0 ].Attributes = ( FALSE != bEnable )? SE_PRIVILEGE_ENABLED : 0;
/*
指定したアクセストークン内の特権を設定
*/
bRet = ::AdjustTokenPrivileges(
hToken
, FALSE
, &tTokenPrivileges
, sizeof( tTokenPrivileges )
, NULL
, NULL
);
if ( 0 == bRet ) {
// エラー
DWORD dwLastError = ::GetLastError();
::CloseHandle( hToken );
return( ::HRESULT_FROM_WIN32( dwLastError ) );
}
// トークンハンドルを閉じる
::CloseHandle( hToken );
// 正常終了
return( S_OK );
}
/*
ファイル書き込み時に未使用領域を0埋めさせないようにする
*/
int _tmain
(
int argc
, _TCHAR* argv[]
)
{
// ロケール変更(wcoutでユニコードを出力する為)
std::wcout.imbue(std::locale("", std::locale::ctype));
// 特権を有効にする( SE_MANAGE_VOLUME_NAME )
EnablePrivilege( SE_MANAGE_VOLUME_NAME, TRUE );
{
// ファイルをオープン
HANDLE hFile = CreateFile(
L"TestLargeFile.bin"
, GENERIC_WRITE
, FILE_SHARE_READ
, NULL
, CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, NULL
);
if ( hFile != INVALID_HANDLE_VALUE ) {
// 作成するファイルのサイズ
LONGLONG llFileSize = 0x0000000200000000;
// ファイルポインタの移動
LONG lHigh = llFileSize >> 32;
DWORD dwRet;
// 8GB先までファイルポインタを進める
LARGE_INTEGER pointer;
pointer.QuadPart = llFileSize;
SetFilePointerEx( hFile, pointer, NULL, FILE_BEGIN ) ;
// EOFを設定
dwRet = ::SetEndOfFile( hFile );
// EOFの位置までを有効なデータとする
dwRet = ::SetFileValidData( hFile, llFileSize );
// EOF - 3バイト手前の位置までファイルポインタを移動
pointer.QuadPart = llFileSize - 3;
SetFilePointerEx( hFile, pointer, NULL, FILE_BEGIN ) ;
// 3バイト書き込み
DWORD dwTest = 0;
WriteFile( hFile, "abc", 3, &dwTest, NULL ) ;
std::wcout << L"3バイト書き込みました。" << std::endl;
// ファイルのクローズ
::CloseHandle( hFile );
}
}
// 正常終了
return( 0 );
}
3バイト書き込みました。