URL スキームを追加するには

Windows において、URL スキームとアプリケーションを関連付ける方法と、新しい URL スキームを追加する方法についてかきます。

Windows の規定の動作では、「http://〜」 というリンクをクリックすると、IEFirefox 等の Web ブラウザでその URL が開かれ、「mailto://〜」というリンクをクリックすると OutlookThunderbird 等のメーラが起動してメール作成画面に移る。

このような動作は、拡張子の関連付けと同様に「フォルダオプション」-->「ファイルの種類」で設定可能である。例えば http://〜 をクリックすると Excel が起動したりということもできる(この例に特に意味は無いが)。

hoge://〜」に対する関連付けは「URL スキーム」と呼ばれるものらしい。手元の環境では、http, https, rlogin, telnet, tn3270, ftp, callto, ldap, mailto, news, nntp, nntp, snews が URL スキームとして各種アプリケーションに関連付けられていた。

さて、これに新しく追加するためにはどうすればよいか、という話題。

調べてみると

  • 「新しい拡張子の作成」では作成できそうに無い。
  • 「HKEY_CLASSES_ROOT」には拡張子が登録されているが、URL スキームは見当たらない。

ということが分かった。

さらに調べると「HKEY_LOCAL_MACHINE\SOFTWARE\CLASSES」以下に URL スキームの設定が存在することが分かった。

※注意: 無保証です。当然ながら、何をしているか分かる方、危険性を正しく理解している方のみ、バックアップをとるなどして十分な注意のうえでお使いください。下手をすると Windows が起動しなくなったり、作業データが消えてしまったり、PC が発火したりする可能性があります。最後のは冗談ですが。

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\mailbox]
"URL Protocol"=""
@="URL:Mailbox Protocol"
"EditFlags"=dword:00000002

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\mailbox\DefaultIcon]
@="C:\\Program Files\\Mozilla Thunderbird\\thunderbird.exe,0"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\mailbox\shell]

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\mailbox\shell\open]

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\mailbox\shell\open\command]
@="\"C:\\Program Files\\Mozilla Thunderbird\\thunderbird.exe\" -osint -mail \"%1\""

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\imap]
"URL Protocol"=""
@="URL:IMAP Protocol"
"EditFlags"=dword:00000002

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\imap\DefaultIcon]
@="C:\\Program Files\\Mozilla Thunderbird\\thunderbird.exe,0"

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\imap\shell]

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\imap\shell\open]

[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\imap\shell\open\command]
@="\"C:\\Program Files\\Mozilla Thunderbird\\thunderbird.exe\" -osint -mail \"%1\""

関連URL:

マイ整数

数値クラスを新しく定義し、演算子 (e.g., 加算) を定義するときの注意点について。
どうも、一時オブジェクト周りの最適化 (RVOとか) が想定していた通りには効いていない事に気づいたので、メモ。

#include <iostream>

#define PP(x) do{std::cout<<x<<std::endl;}while(0)
#define QQQ(x) do{std::cout<<"@@"<<__LINE__<<"@@ ("<<#x<<") = "    \
    <<(x)<<std::endl;}while(0)
#define LLL do{std::cout<<"----"<<__LINE__<<"----"<<std::endl;}    \
    while(0)

template<typename T>
class MyInt
{
public:

    MyInt ( T value = 0 )
        : value_ ( value ) { PP( " 1:" << this ); }

    MyInt ( const MyInt& rhs )
        : value_ ( rhs.value_ ) { PP( " 2:" << this ); }

    ~MyInt () { PP( " 3:" << this ); }

    T  value () const { return value_; }

    const MyInt& operator+= ( const MyInt& rhs ) {
        value_ += rhs.value_;
        return *this;
    }

    MyInt member_plus ( const MyInt& rhs ) const {
        MyInt  temp = *this;
        return temp += rhs;
    }

public:

    T  value_;
};

template<typename T>
MyInt<T>  operator+ ( MyInt<T> lhs, const MyInt<T>& rhs ) {
    return lhs += rhs;
}

template<typename T>
std::ostream&  operator<< ( std::ostream& os, const MyInt<T>& x ) {
    return os << x.value();
}

int main () {
    MyInt<int>  x ( 999 );
    MyInt<int>  y ( 100 );
    LLL;{ MyInt<int>  z ( x ); QQQ( z += y ); } // ※1
    LLL;{ QQQ( x + y ); } // ※2
    LLL;{ QQQ( x.member_plus(y) ); } // ※3
    LLL;return 0;
}

結果:

 1:0xbfeb6224
 1:0xbfeb6220
----69----
 2:0xbfeb6210
@@69@@ (z += y) = 1099
 3:0xbfeb6210
----70----
 2:0xbfeb621c
 2:0xbfeb6218
@@70@@ (x + y) = 1099
 3:0xbfeb6218
 3:0xbfeb621c
----71----
 2:0xbfeb6210
 2:0xbfeb6214
 3:0xbfeb6210
@@71@@ (x.member_plus(y)) = 1099
 3:0xbfeb6214
----76----
 3:0xbfeb6220
 3:0xbfeb6224

副作用なしの加算をしたいとき、まず一時オブジェクトを作ってそちらに副作用ありの加算を適用するというのが普通のはず。
で、MyInt z = x して z += y したときの結果 (※1) が理想。一時オブジェクトは一個しか作られない。一個作られるのは仕方ない(一個も作りたくない時は += するべき)ので、これが最適。

x+y で自動的に作られることを期待したやり方でうまくいくと思っていたけれど、ダメ(※2)。一時オブジェクトが都合2個つくられてしまう。
ひょっとして、メンバ関数として演算子を定義すればよいのかと思ったけど、それに相当すると考えられる※3でもダメ。


こういうとき、どうするのがいいんでしょうかねー。

追記(+ソースコードの余計な関数を修正。詳細は項目を改めて言及)

どうも、const& で返せばよいらしい事が判明。ジョーシキなんかなぁ?

template<typename T>
class MyInt {
///..snip..
    const MyInt& operator+= ( const MyInt& rhs ) {
        value_ += rhs.value_;
        return *this;
    }

    const MyInt& member_plus ( const MyInt& rhs ) const {
        MyInt  temp (*this);
        return temp += rhs;
    }

    const MyInt& member_plus2 ( MyInt rhs ) const {
        return rhs += *this;
    }
///..snip..
};
///..snip..
template<typename T>
const MyInt<T>&  operator+ ( MyInt<T> lhs, const MyInt<T>& rhs ) {
    return lhs += rhs;
}

template<typename T>
std::ostream&  operator<< ( std::ostream& os, const MyInt<T>& x ) {
    return os << x.value();
}

int main () {
    MyInt<int>  x ( 999 );
    MyInt<int>  y ( 100 );
    LLL;{ MyInt<int>  z ( x ); QQQ( z += y ); }
    LLL;{ QQQ( x + y ); }
    LLL;{ QQQ( x.member_plus(y) ); }
    LLL;{ QQQ( x.member_plus2(y) ); }
    LLL;return 0;
}
 1:0xbfbc4734
 1:0xbfbc4730
----69----
 2:0xbfbc4724
@@69@@ (z += y) = 1099
 3:0xbfbc4724
----70----
 2:0xbfbc472c
@@70@@ (x + y) = 1099
 3:0xbfbc472c
----71----
 2:0xbfbc4724
 3:0xbfbc4724
@@71@@ (x.member_plus(y)) = 1099
----72----
 2:0xbfbc4728
@@72@@ (x.member_plus2(y)) = 1099
 3:0xbfbc4728
----77----
 3:0xbfbc4730
 3:0xbfbc4734

const&周りはよく分かってないのでいずれ調べねば。。

マイ整数(その2)

さらに分かったこと
  • 戻り値が「const MyInt&」でなく、「MyInt」であってRVOが効く場合もある(というか、後者がふつうだと思うのだが)。たとえば、「return temp += rhs;」を「temp += rhs; return temp;」とするだけでOKだったらしい。これはイカン。わけがわからなくなってきた。
  • 「const&」で返すということは、関数内で作成したオブジェクト(temp)の参照を返しているのではないのかねー、と思った方は、私と同意見です。でも、一時オブジェクトに対するconst参照があると、そのオブジェクトは『意外と長生き』するようです。私には説明できません。詳しくは参考URLなどをどうぞ。
  • 基本データ型との連携は、以下のような感じで書くのが一時オブジェクト最適化的にはいいらしいです。「10 + x」とかやっても一時オブジェクトの生成がひとつですみます。
template<typename T>
inline MyInt<T>  operator+ ( const T& lhs, const MyInt<T>& rhs ) {
    MyInt<T>  temp = lhs;
    temp += rhs;
    return temp;
}
  • 上の例に関しても、「SVOのためにreturnの中でコンストラクタをよべ。一文でかけ。」という(私が理解している範囲の)原則と、反しているような気がします。
  • 要するに、もう少しちゃんと調べる必要がありそうです、ということ。
  • 環境かいてませんでした。g++ (GCC) 4.2.3 (Debian 4.2.3-5) です。