TrickDiary このページをアンテナに追加 RSSフィード

2007-12-29

[][][] Transactional NTFS (TxF)

ちょっと別件で調べものしてて偶然 Transactional NTFS (TxF) なるものを発見。この機能を一言で説明すると「各種ファイル操作をトランザクションベースで実行することで各種ファイル操作を一括でのコミット及びロールバックを実現する機能」。

WinFSVista での採用が見送られてガッカリしてたけど、個人的に WinFS に一番期待していた機能が実は Vista には実装されてビックリ。てか、なんでみんなこの素晴らしい機能を華麗にスルーしてんだ? 検索してもロクに情報がヒットしないし。この機能といい、Property System といい、WinFS 自体の採用は見送られたけど、その実 WinFS で実現しようしていた feature はほとんど実装されている!?

あまりにみんなが華麗にスルーしてるんで本当に実装されてんのか心配になってちょっとサンプルコードを書いて確かめてみました。

# コンパイルには Windows SDK があれば十分です(コンパイラ等も付属してます)。

#if !defined(WINVER)
#define WINVER 0x600
#endif

#if !defined(_WIN32_WINNT)
#define _WIN32_WINNT WINVER
#endif

#include <windows.h>
#include <ktmw32.h>
#include <stdlib.h>
#include <iostream>
#include <string>

#pragma comment(lib, "ktmw32.lib")

//////////////////////////////////////////////////////////////////////////////
//
//  Win32 Error
//

class win32_error :public std::runtime_error
{
    DWORD code;
  public:
    win32_error(DWORD X_code = GetLastError())
        :std::runtime_error(win32_error::make_error_message(X_code)), code(X_code) { }
    static const std::string make_error_message(DWORD X_code = GetLastError());
    DWORD get_error_code() const { return code; }
    const char * get_error_message() const { return what(); }
};
const std::string win32_error::make_error_message(DWORD X_code)
{
    LPVOID	lpMsgBuf;
    FormatMessageA(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL, X_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPSTR)&lpMsgBuf, 0, NULL);
    const std::string result = (const char *)lpMsgBuf;
    LocalFree(lpMsgBuf);
    return result;
}

//////////////////////////////////////////////////////////////////////////////
//
//  command-line interface
//

int main(int argc, char *args[])
{
    argc, args; // 警告避け
    
    try
    {
    
        std::cout << "Command list:" << std::endl;
        std::cout << "\t" << "move src dst" << std::endl;
        std::cout << "\t" << "copy src dst" << std::endl;
        std::cout << "\t" << "del target" << std::endl;
        std::cout << "\t" << "dir" << std::endl;
        std::cout << "\t" << "commit" << std::endl;
        std::cout << "\t" << "rollback" << std::endl;
        std::cout << "\t" << "exit" << std::endl;
        std::cout << std::endl;
        
        HANDLE transaction = INVALID_HANDLE_VALUE;
        
        std::string current_command;
        std::string current_src;
        
        while(false == std::cin.eof())
        {
            if (INVALID_HANDLE_VALUE == transaction)
            {
                transaction = CreateTransaction(NULL, 0, 0, 0, 0, NULL, L"サンプル トランザクション");
                if (INVALID_HANDLE_VALUE == transaction)
                {
                    throw win32_error();
                }
            }
        
            try
            {
                std::string commandline;
                std::cin >> commandline;
                
                if (!current_command.empty())
                {
                    if
                    (
                        "move" == current_command ||
                        "copy" == current_command
                    )
                    {
                        if (current_src.empty())
                        {
                            current_src = commandline;
                        }
                        else
                        {
                            if ("move" == current_command)
                            {
                                if (!MoveFileTransacted(current_src.c_str(), commandline.c_str(), NULL, NULL, 0, transaction))
                                {
                                    throw win32_error();
                                }
                            }
                            else
                            if ("copy" == current_command)
                            {
                                BOOL cancel = FALSE;
                                if (!CopyFileTransacted(current_src.c_str(), commandline.c_str(), NULL, NULL, &cancel, 0, transaction))
                                {
                                    throw win32_error();
                                }
                            }
                            current_command.clear();
                            current_src.clear();
                        }
                    }
                    else
                    if ("del" == current_command)
                    {
                        if (!DeleteFileTransacted(commandline.c_str(), transaction))
                        {
                            throw win32_error();
                        }
                        current_command.clear();
                    }
                }
                else
                if
                (
                    "move" == commandline ||
                    "copy" == commandline ||
                    "del" == commandline
                )
                {
                    current_command = commandline;
                }
                else
                if ("dir" == commandline)
                {
                    WIN32_FIND_DATA find_data = { 0, };
                    HANDLE find_handle = FindFirstFileTransacted("*", FindExInfoStandard, &find_data, FindExSearchNameMatch, NULL, 0, transaction);
                    if (INVALID_HANDLE_VALUE == find_handle)
                    {
                        throw win32_error();
                    }
                    do
                    {
                        std::cout << find_data.cFileName << std::endl;
                    } while(FindNextFile(find_handle, &find_data));
                    FindClose(find_handle);
                }
                else
                if ("commit" == commandline)
                {
                    if (!CommitTransaction(transaction))
                    {
                        throw win32_error();
                    }
                    //  コミットを実行したトランザクションは継続して使えないので破棄
                    CloseHandle(transaction);
                    transaction = INVALID_HANDLE_VALUE;
                }
                else
                if ("rollback" == commandline)
                {
                    if (!RollbackTransaction(transaction))
                    {
                        throw win32_error();
                    }
                    //  ロールバックを実行したトランザクションは継続して使えないので破棄
                    CloseHandle(transaction);
                    transaction = INVALID_HANDLE_VALUE;
                }
                else
                if ("exit" == commandline)
                {
                    break;
                }
                else
                {
                    std::cout << commandline << " is unknown command!" << std::endl;
                }
            }
            catch(const std::exception &e)
            {
                std::cout << e.what() << std::endl;
                current_command.clear();
                current_src.clear();
            }
        }
        if (INVALID_HANDLE_VALUE != transaction)
        {
            CloseHandle(transaction);
        }
    }
    catch(const std::exception &e)
    {
        std::cout << e.what() << std::endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}

実に素晴らしいです、この機能。

ここで使用している以外にも CreateFileTransacted() を始め多種多様な APIトランザクションに対応しているようです。

追記

ファイル関連だけでなくレジストリまわりも RegCreateKeyTransacted() などの API で対応しているようですね。誠に素晴らしい。

追記2

レジストリ関連のは Transactional Registry (TxR) と呼ばれているようです。

2007-07-19

[][] ネットワークトラブルシューティング

Windows Vista ってネットワークまわりの安定性がウンコで、設定や環境が変化したわけでもないのに頻繁に機能しなくなる。おまけに[診断と修復]をやっても解決しないことが多い。で、最近の経験則としてはどうも同問題が発生した時でも[ネットワーク接続]から問題の発生している接続をいったん[無効]にしてから[有効]に戻してやると回復する可能性が高いみたい。

2007-04-22

[][] ReadyBoost に関する嘘

Windows Vista の有名な機能である ReadyBoost ですが、どうもあの機能は嘘だと言っても過言にはならない程度に誇張されて紹介されているようです。と、いうのも、よく「 USB メモリを使って」と言われる部分です。USB ってそんなに転送速度が高いインターフェイスではない(Hi-Speedモードの400Mbpsってのは飽くまで理論値で実測有効値はその十分の一でもでれば良好なほうです。→ReadyBoost対応が謳われているものを含めUSBメモリの転送速度を調べた限りではそんな感じだったのですが、USB接続のHDDの転送速度を調べたところ100Mbps程度の速度がありました。→結論:USBメモリじゃReadyBoostはやっぱりまともに機能しない。)ので外付けの USB 接続の HDD のキャッシュとしてならまだしも PC 本体に内蔵されているようなHDDのキャッシュとしてなんて速度的にありえない話です。

2007-02-26

[][][][] HKCR配下のレジストリキーが勝手にコピーされる?

vista対策】

http://pc10.2ch.net/test/read.cgi/tech/1162813293/312-313n

312 :デフォルトの名無しさん :2007/02/27(火) 00:32:42 
MSが仕様だと言い張るので、Vistaのすばらしい機能を紹介します。 
レジストリのHKEY_CURRENT_USERのキーを同一PC上のユーザにコピーする「機能」です。 

(条件) 
・実行ファイル名に"UPDATA"や"SETUP"などの文字を含む 例)MyUpdate.exe つまり、UACにひっかかるexeであること。 
・そのexeにmanifestをつけないこと。あってもいいけど、実行権限を指定しなければok。 

(方法) 
・そのexeの中で、HKCUのキーを書き込むだけでそのキーが全ユーザにコピーされます。 
・MFCアプリなら、CWinApp::GetProfileStringなどで、参照するだけでOK!ナイスすぎ。 
 しかも、書き込んだキーだけじゃなく、SetRegistryKeyで設定したグループ以下を 
 全部丸ごとコピーしてくれるから、とっても簡単! 
 通常は、会社名を設定するから、あなたの会社のソフトをまとめて面倒みてくれます。 
 microsoftをSetRegistryKeyにしたら・・・うは〜 
・書き込むって言ったけど、RegCreateKeyExをやっても、そのキーが丸ごとコピーされマス! 

(ただし) 
・すでにキーが設定されていれば、上書きはされません。 
 キーが空だった場合のみ,ほかのユーザのHKCUのキーが新規で追加されます。 
 でも、普通デフォルト設定はキーを空にするよね。 
・コピーする内容がパスだった場合、ご親切にもパスのユーザー名の部分だけ置き換えてくれます。 
 たとえば、c:\User\papa\秘密.txt という文字列を書き込んだ場合は、ママのレジストリには 
 c:\User\mama\秘密.txt と置き換えてコピーしてくれるんです!開発者のことよく考えてくれるね〜  

このすごい機能は、RedirectHKCUKeysと言うらしいです。 
これで、パパのクレジット番号だろうが、パスワードだろうが、ママがいちいち入力しなくても 
親切にコピーしてくれるから、すごく便利だね! 
一度おためしあれ。 


313 :312:2007/02/27(火) 00:43:18 
あ、ちょっと間違えました。 

実行ファイル名に"UPDATA"や・・・ 
↓ 
実行ファイル名に"UPDATE"や・・・ 

動作確認はしてないけど、これちょっと酷いってか、ヤバイね。確かにインストーラを作る立場で見ればありがたい挙動ではあるんだけどねぇ。