Windows7 SP1

先週Windows7 Service Pack 1がリリースされて、先日早速適用してました。

まず、SP1のディスクイメージをダウンロードしてDVDに焼いてからやろうかな・・・と思ったんだけど、Windows Server 2008R2とWindows7 用のものが一緒に入っているイメージしかなく、ファイルサイズもバカでかいので Windows Updateから直接適用させました。。。

これが、失敗の元。(ーー;;;

コントロールパネルから、Windows Updateを起動して、ダウンロードが始まりました。ファイルサイズは90MBぐらい・・・これぐらいなら、すぐ終わるでしょう・・・。

・・・30分経過・・・50%から全く進まない。・・・・1時間経過・・・ダウンロード、終わらん・・・1時間10分経過・・・・イライライライライライラ・・・・・

ん?もしかして・・・Security Essentials のリアルタイム保護を外さないといけないのか??? そうこうしているうちに、1時間30分経過。ダウンロードが終わる気配もなく、ハードディスクのアクセスランプがチカチカしっぱなし。。。

しびれをきらして、キャンセルボタンをクリック。はぁ・・・時間の無駄遣い・・・。

公開されているSP1のイメージファイル(ISOファイル)をダウンロード。ものの10分ほどで終了。どゆこと?(笑)
DVDに焼いてから、DVDに入っているSP1のインストーラを起動して適用したら、あっけなく終わった( ̄。 ̄;)

なんなんだよ、Windows Update・・・orz

SP1を適用して、再起動したら、あきらかにメモリの使用量が増えてました。普段なら起動直後のアイドル状態で、1.4GBくらいだったんですが・・・2GB超になってます・・・何回か再起動してたら元に戻りましたが・・・なんなんでしょうね・・・???

いいかげん、あたらしいPCがほちい。

IE9とか、その他雑記。

自分でやってみることの大切さ。

なにかしらのプログラム or スクリプトを書く方なら、「車輪の再発明」という言葉は聞いたことがあると思います。一般的には、やや否定的な意味合いが強いです。まぁ、要は・・・

すでにあるプログラムなりライブラリなりを使わず、自分でなんでもやるのは、非効率だし、バカらしいからやめなよ!

っていう風に僕は解釈しているんですが・・・(^^;;
「車輪の再発明」だとしても、自分でやってみたい、というのはプログラマ本来の姿なんじゃないでしょうかねぇ。

Internet Explorer 9 RC

Internet Explorer 9のRC版が公開された、という記事をどっかのサイトで見て、入れてみました。
Internet Explorer 自体、5~6年前から全く使わなくなって、サイトの管理のなかで、HTMLを書く作業があるときだけ起ち上げてIEでの表示を確認する、ということぐらいにしか使ってなかった。Internet Explorer を使わない(使いたくない)理由は、いろいろあって、

  • 良いか悪いかを別にして、Web Standard から(W3Cの仕様から)大分ずれてる。
  • 特にIE6はバグだらけ。
  • 今となっては ECMA Script実行速度が遅い
  • カラープロファイルが埋め込まれた画像などを正しく表示できない。
  • イマイチなユーザーインターフェイス
  • (OSのバージョンアップと連動しているからなんでしょうけど・・・) バージョンアップするのが遅い。
  • Internet Explorer を使っている、という時点で、パワーユーザーから軽蔑される(笑)
  • OS(Windows)の基幹システムとガッチリ結びついているので、セキュリティという点で心配。
  • その他もろもろ 挙げればキリがない。

写真を撮るのも趣味の一つなので、特にカラープロファイルを処理してくれないブラウザというのは、いただけない。

かくして、いろいろ不満があったので、消極的だったんですが、IE9のβ版が、上記の不満のすべてではないにしろ、結構まともに改善されていたので、RC版も入れてみました。

以前は、クライアント認証が必要なサイトにアクセスしたら落ちてたりして使えなかったけど・・・かなり良くなってますね~。

まぁ、もっともパソコンでブラウザ起ち上げてWebサイトやブログを見る・・というのは、スマートフォンだとか、iPadなどのタブレットデバイスに置き換えられていってるので、あんまり意味なかったりして・・・(^_^;;;;

ま、それを言っちゃおしまいですが。。。

次と前と戻ると過去と新しいと未来

言葉・・・といいますか、あるアクションの表現の仕方っていうのは、本当に難しい。ま、どーでもいいことなんですが(^^;;;

時系列で記事が並ぶブログなんかで、ページのナビゲーション(次へとか、前へとか)にどんな語を割り当てればいいのかすごく悩むときがあります。ちょっと神経質すぐる感覚もありますが・・・。

それに、画面の左側に配置しているのが、より新しい記事へのリンクで、右側に配置するのが古い記事へのリンク・・・にするのがいいのか? といった問題があります。これは感覚的なことなので、ブログを組む人によってバラバラなことが多いです。実際、この辺はUIデザインとかと絡んでそうなので、統一されないとは思いますが・・・。何か文書でそういった指針といったものはあるんでしょうかねぇ?

ケース 1 : 「次へ」 と 「前へ」

割とこのタイプが多いです。おそらく英語の Next と Prev(previosの略) を単純に訳しただけだと思います。たまに、英語で next と back というのがあると思いますが、これは go と back とごちゃまぜになっている感じがします。・・・ま、どっちでもいいんだけど。

トップのページから順番に見ていくぶんには問題ないんだけど・・・、検索サイトから来た、とかで途中のページからスタートしたとき・・・仮に最新のページへと辿りたいとした場合・・・「次へ」をクリックすればいいのか、「前へ」をクリックすればいいのか・・・迷って手がフリーズしてしまうことが多々あります。

「次へ」というのが、過去へ向いている語なのか、未来へ向かう語なのか?浅学の僕には分かりません。「次」は「継ぎ」と同源と、広辞苑(電子辞書)に載っていったのでこの感覚からすれば、未来に向いている語なんでしょうか?

ケース2 : 記事のタイトルを表示

このタイプも結構見かけます。が、これも最新記事へ辿りたいだとか、過去に遡りたいといった場合、どちらの記事タイトルが新しいか、古いか、というわからないのでこれも不便。

ケース3 : 日付を表示

これはあまり見かけません。これだと同じ日付の複数の記事がある場合は、上記と同様のことが起こる可能性がありますし・・・、時間も表示すればいい話ですが、それだと冗長感あります。ナビゲーションとしてはイマイチイケテナイ気がなんとなく感覚的にあります。

ケース4 : 数字を表示

個人的にはこれが一番分かりやすい、と思います。が、単に数字のテキストを入れるだけでは、何のリンクか分からないのでそもそもナビゲーションとして認識されない恐れがあると感じます。かといって数字画像を使うのは・・・ちょっと違う気がします。

ケース5 : 矢印を表示

「<<」 とか「>>」などを使うところもありますが、これも、どちらが過去で未来か?という根源的な疑問にぶち当たってしまいます。

ケース6 : newer と older

これが最も分かりやすい。が!、日本語にすると、かっこ悪い。直訳すれば「より新しい」とか「より古い」とかになるんだけど、いい意訳が見つからない。

で、結論。

結局、上記のケースをうまく組合わればいいんじゃないかな、という、至極当然で普通の結論に至りました(笑)

長々とどうでもいいこと書いて、すみません。

ネットワークプレースの作り方がわからん

Windows7 では、WebDAVをネットワークドライブとして割り当てることができました。
「ネットワークドライブとWebDAV over SSL」

ですが、WindowsXP(SP3)では失敗してしまいました。
XPでは、ネットワークドライブの割り当てに、WebDAVは使えないようです。

XPではWebフォルダ(ネットワークプレース?)を作成することで解決できるようです・・・が、肝心のネットワークプレースをWindowsAPIを使って作成方法がマイクロソフトからは公開されてないみたいです。

すみません、Windowsのネットワーク関係のことがよく分かってません。ので、まるでトンチンカンなことを書いてるかもしません。備忘録なので、ご注意を。)

ネットワークプレースの仕組み自体はカンタン。。。というか、ネットワークプレース自体がフォルダーリンクなので、

  1. ディレクトリを作成
  2. 作ったディレクトリの中に target という名のショートカットを作成
  3. そのショートカットにリードオンリー属性をつける。
  4. 下記内容のDesktop.ini(要システム・隠し属性)ファイルを作成
    [.ShellClassInfo]
    CLSID2={0AFACED1-E828-11D1-9187-B532F1E9575D}
    Flags=2
    
  5. ディレクトリにリードオンリー属性に。

これだけ。ショートカットはIShellLink/IPersistFile インターフェイスで作れます。

だだ、この方法では WebDAV(https/http)のネットワークプレースは作れません。
理由は至極当然で、上記(2)のWebDAVのアドレスへのショートカットが原因で、具体的には、https/http などのURIリンク先を単純にIShellLink::SetPath()に渡しても、ショートカットファイル自体は作れても、エクスプローラからWebフォルダとしては認識されません。

リンク先が、HTTP/HTTPSのURLではなく、FTP(ftp://server_name/directory/)や共有フォルダ(\\server_name\folder)のURIならうまくいきます。

このHTTP/HTTPSへのショートカットファイルは、普通のショートカットファイルではなく、どうやら特殊な(非公開の)方法で作られる必要があるようです。
もしかしたら公式のMSDNドキュメントに記述されているのかもしれません。が、結構検索かけて探してみたけどわかりませんでした。ご存じの方は教えて欲しいです (--;;;
WebDAV APIというのがあって、DavAddConnectionなどのようなAPIはありますが、Vista以降用のもののようですし、そもそも DavAddConnectionなどのAPIはSSL接続用、というようなことが書かれてますので、ちょっと違うみたいです。

で、もうちょっとグーグル先生に粘って聞いてみると・・・マイクロソフトのユーザーフォーラム?でそれらしき情報がありました。
【How to create web folders programmatically using Windows Script Host】

英文を要約すると、target.lnk そのもののバイト配列を用意してファイルに書き込むという、かなり力技で強引な(^^;;解決方法が提示されてます。
エクスプローラシェルでWebフォルダを作った後、そのtarget.lnkを解析したみたいですね。

この情報を元に、整理してC/C++で書き直して見ました。

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

#define URI_LIMIT_SIZE 1024

#include <windows.h>
#include <strsafe.h>
#include <shlobj.h>
/*
 pURI : WebDAVへのhttp/httpsから始まるURLアドレス
 pDir : ネットワークプレースを作成するディレクトリ
 pName : ネットワークプレースの名前
*/
HRESULT CreateURILinkByEmbedByteArray(PCTSTR pURI,PCTSTR pDir,PCTSTR pName)
{
  HRESULT hres = S_OK;

  TCHAR szPath[MAX_PATH] = {0};
  TCHAR szDir[MAX_PATH] = {0};

  WCHAR szName[URI_LIMIT_SIZE] = {0};
  WCHAR szURI[URI_LIMIT_SIZE] = {0};
  
  HANDLE hFile = NULL;
  DWORD dwLen = 0;
  
  BYTE part1[] = 
    { 0x4c,0x00,0x00,0x00,0x01,0x14,0x02,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,
      0x00,0x00,0x00,0x46,0x81,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x14,0x00,
      0x1f,0x50,0xe0,0x4f,0xd0,0x20,0xea,0x3a,0x69,0x10,0xa2,0xd8,0x08,0x00,0x2b,0x30,
      0x30,0x9d,0x14,0x00,0x2e,0x00,0x00,0xdf,0xea,0xbd,0x65,0xc2,0xd0,0x11,0xbc,0xed,
      0x00,0xa0,0xc9,0x0a,0xb5,0x0f,0xa4,0x00,0x4c,0x50,0x00,0x01,0x42,0x57,0x00,0x00,
      0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,
      0x00,0x00};
  BYTE part2[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
  BYTE nameLen[] = {0x00, 0x00};
  BYTE uriLen[] = {0x00, 0x00};

  int cbLen = lstrlen(pName);

  part1[0x4d] = part1[0x77] = (cbLen <= 44) ? 0x00 : 0x01;

  nameLen[0] = cbLen % 0x100;
  nameLen[1] = cbLen / 0x100;

//ワイド文字に変換。ユニコードでビルドする場合は、単にコピーするだけ。
#ifdef UNICODE
  StringCchCopy(szURI,URI_LIMIT_SIZE,pURI);
  StringCchCopy(szName,MAX_PATH,pName);
#else
  MultiByteToWideChar(CP_ACP,0,pURI,-1,szURI,URI_LIMIT_SIZE);
  MultiByteToWideChar(CP_ACP,0,pName,-1,szName,MAX_PATH);
#endif

  cbLen = lstrlen(pURI);
  uriLen[0] = cbLen % 0x100;
  uriLen[1] = cbLen / 0x100;
  
  //ディレクトリの作成
  StringCchPrintf(szDir,
                  MAX_PATH,
                  TEXT("%s%s%s"),
                  pDir,
                  (szDir[lstrlen(szDir) - 1] != TEXT('\\')) ? TEXT("\\") : TEXT(""),
                  pName);
  SHCreateDirectoryEx(NULL,szDir,NULL);

  //target.lnkのパスを生成  
  StringCchPrintf(szPath,MAX_PATH,TEXT("%s\\%s"),szDir,TEXT("target.lnk"));

  //シェルリンク・ファイルへの書込み
  hFile = CreateFile(szPath,
                     GENERIC_WRITE,
                     0,
                     NULL,
                     CREATE_NEW,
                     FILE_ATTRIBUTE_NORMAL,
                     NULL);

  if(hFile == INVALID_HANDLE_VALUE || GetLastError() == ERROR_ALREADY_EXISTS)
    {
      hres = E_FAIL;
      goto cleanup;
    }

  //埋め込み
  WriteFile(hFile,reinterpret_cast<PVOID>(part1),sizeof(part1),&dwLen,NULL);
  WriteFile(hFile,reinterpret_cast<PVOID>(nameLen),2,&dwLen,NULL);
  WriteFile(hFile,reinterpret_cast<PVOID>(szName),sizeof(WCHAR) * (lstrlenW(szName) + 1),&dwLen,NULL);
  WriteFile(hFile,reinterpret_cast<PVOID>(uriLen),2,&dwLen,NULL);
  WriteFile(hFile,reinterpret_cast<PVOID>(szURI),sizeof(WCHAR) * (lstrlenW(szURI) + 1),&dwLen,NULL);
  WriteFile(hFile,reinterpret_cast<PVOID>(part2),sizeof(part2),&dwLen,NULL);

  CloseHandle(hFile);
  
  //ファイルにRO属性を設定
  SetFileAttributes(szPath,FILE_ATTRIBUTE_READONLY);

  //ディレクトリにRO属性
  SetFileAttributes(szDir,FILE_ATTRIBUTE_READONLY);

cleanup:
  return hres;
}

一応、会社で使っているWindows XP SP3ではネットワークプレースをプログラムから作成できました。
が!、当然ながら、Windows7では、うまくいきません(笑)
Windows7/Vista から仕様が変わったようです。当たり前か(^^;;;

う~ん・・・なんかこう、すんなり旨い方法がないものだろうか・・・。

ネットワークドライブとWebDAV over SSL

Windows7だとWebDAVをネットワークドライブとして割り当てることができるので、ログオン時はいつも再接続させたい・・・のですが、クライアント認証を使ってアクセスする関係上、自動的に再接続できません。「ログオン時に再接続する」にチェックを入れているのですが、「再接続できませんでした」とかなんとかいうメッセージが出てエラーになってしまいます。

というわけで、今まではデスクトップに、以下のようなコマンドファイルを置いてその都度ダブルクリックすることで回避してました。

;---- allocate_network_drive.cmd ----
net use M: https://webdav_over_ssl/my_folder/
start M:

ただ、ダブルクリックすると、コマンドプロンプトのウィンドウが出たりして、なんかスマートじゃありません。。。

net use なんちゃら の部分を、自前でコーディングすれば事足ります。幸いにも、Windows API には WNetAddConnection2() という便利なAPIがあるので、このAPIをコールすれば一発でできそうです(^^)

早速、C++超手抜きネットワークドライブ・クラスを書いてみました。今年から積極的にC#を使おうとか、宣言しながら、早速C++使ってます。すみません(笑) 直接API叩くのがカンタンなので・・・。

#pragma comment(lib,"mpr.lib")
/**********************************************************
 簡易ネットワークドライブ・クラス 
 NetworkDrive.h
***********************************************************/
#include <windows.h>
#include <strsafe.h>
#include <tchar.h>

class CNetworkDrive
{
private:
  PTSTR m_strURI;
  PTSTR m_strDevice;

public:
  //コンストラクタ&デストラクタ
  CNetworkDrive(PCTSTR uri,PCTSTR device)
    {
      m_strURI    = CreateAndCopyString(uri);
      m_strDevice = CreateAndCopyString(device);
    }
  virtual ~CNetworkDrive()
    {
      DestroyString(m_strURI);
      DestroyString(m_strDevice);
    }

  DWORD Allocate(bool bRemember = false)
    {
      NETRESOURCE NetResource = {0};

      NetResource.dwType       = RESOURCETYPE_DISK;
      NetResource.lpLocalName  = m_strDevice;
      NetResource.lpRemoteName = m_strURI;
      
      return WNetAddConnection2(&NetResource,NULL,NULL,CONNECT_INTERACTIVE | (bRemember ? CONNECT_UPDATE_PROFILE : 0));
    }
  
  DWORD Cancel(bool bUpdate = false,bool bForce = true)
    {
      DWORD dwFlags = bUpdate ? CONNECT_UPDATE_PROFILE : 0;
      BOOL bwForce = bForce ? TRUE : FALSE;

      return WNetCancelConnection2(m_strDevice,dwFlags,bwForce);
    }

//雑関数
private:
  static PTSTR CreateAndCopyString(PCTSTR src)
    {
      DWORD dwNum = 0;
      PTSTR dest = NULL;
      
      if((dwNum = lstrlen(src)) > 0)
        {
          dest = new TCHAR[dwNum+1];
          StringCchCopy(dest,dwNum+1,src);
        }
      
      return dest;
    }

  static void DestroyString(PTSTR str)
    {
      if(str)
        delete [] str;
    }
  
};

これを、使って、以下のようなテストコードで試してみたところ、うまく行きました。

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

#include <windows.h>
#include <tchar.h>
#include "NetworkDrive.h"

#define URI TEXT("https://my_webdav_site/my_folder/")
#define DEVICE TEXT("Z:")

//スタートアップ
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
  return MessageBox(NULL,
                    (CNetworkDrive(URL,DRIVE).Allocate() == NO_ERROR) ? TEXT("接続しました。") : TEXT("失敗ました。"),
                    TEXT("メッセージ"),
                    MB_OK);
}

最初、エラーで接続できなくてググっても分からず、全く原因が分からくて途方に暮れてたのですが・・・結局ドキュメントを見落としてました。。。英語なんでサラッと流し読みしたのが悪かった(^_^;)

WNetAddConnection2 APIに渡す最後の引数のフラグに、CONNECT_INTERACTIVE を指定していなかったのが原因でした。これを指定することで、クライアント証明書選択ダイアログ(っていうのかな?)が出て正常に認証が済み、無事ネットワークドライブを割り当てることができました。

やっぱりドキュメントはちゃんと読まないといけませんねぇ・・・