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

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 を指定していなかったのが原因でした。これを指定することで、クライアント証明書選択ダイアログ(っていうのかな?)が出て正常に認証が済み、無事ネットワークドライブを割り当てることができました。

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

AirPrint via Windows Shared Printers

iPhone の iOS4.2 になってから、無線LAN経由でプリンタ出力できるようになってますね。

ただ、これ、現状ではHPの一部のプリンタしか対応していないと思うので、大部分の(ほとんどすべての(笑))iPhone/iPod/iPadユーザは使えてないでしょ?

でも、AirPrint でググると、別途PC or Mac を経由してプリントできる記事が出てくるので、今日の昼休みに手順を覚えて、仕事から帰ってから家のPCで試してみました、遅まきながら。

手順自体は、下記URLで説明されてます。英語ですが簡単な英語なので読めるでしょう。ググれば日本語で紹介されているブログ記事がいっぱい出てきます。

http://jaxov.com/2010/11/how-to-enable-airprint-service-on-windows/

要は、AirPrint.zipを任意のディレクトリに解凍して、Windowsサービスとして登録して起動し、そのPCで利用しているプリンタを「共有する」の設定を行うだけ。

この AirPrint.zip の出所がいまいちハッキリしないので若干不安ですが、一応セキュリティーチェックをかけても異常はないので大丈夫でしょう(^^ゞ

ところで、いろんな記事で、iTunes10が必要と、書かれていますが、必要なのは、Apple?のBonjour サービスだけかと思います。実際、僕のPCはSafari for Windows をインストールしたときに一緒にBonjourサービスが一緒にインストールされていましたので、iTunesをインストールしなくても動作しています。iPhone/iPod/iPadを同期しているPCならともかく、そうでないPCの場合は、別途Bonjourサービスだけインストールすればいいんじゃないかと思います。

ちなみに、Bonjourサービスは http://www.apple.com/jp/ftp-info/reference/bonjourforwindows105.html からダウンロードできるみたいです。

IMG_0088

ただ・・・・これ、用紙選択できひん!!!

うちのデフォルトの用紙サイズは、A5。 なんか、ほとんどのアプリがA4で決め打ち?

インターネット上にフォルダを置きたい(その2)

「インターネット上にフォルダを置きたい」 のつづきです。

無事、自分専用のフォルダをクラウド(笑)へ配置することができました。クライアント証明書をインストールしたPCから通常のフォルダ(正確にはネットワークドライブだけど)のように扱えます。目的の9割は達成。

あとは、携帯端末からのアクセスできるようにしなければなりません。僕はiPhone4を持っているので、safariか、WebDAVクライアント・アプリを入れれば目的はほぼ100%達成できます。

が・・・そうは問屋は下ろさない。クライアント証明書をiPhone4にインストールする手段がわからん・・・。

アップルのサイトで検索すると・・・

  1. iPhone構成ユーティリティー」っつーのをPCにインストールする。
  2. プロファイルを作成する。
  3. 作成したプロファイルを添付してiPhone4へメールで送信。
  4. 添付ファイルをタップして、プロファイルをインストール。

という手順みたいです。
(追記 2010/10/30)
わざわざメールで送信しなくても、iPhone4を接続するとiPhone構成ユーティリティで直接インストールできます。

これから先は、Windows7+iPhone4という前提ですので、あしからず。

さっそく、iPhone構成ユーティリティーをインストールし、起動。

操作自体は難しくなく、専門用語がズラッと出てきますが、スルー(^^。
構成プロファイルで「資格情報を構成」を選んで、「構成」をクリックすると、証明書ストアに登録されている証明書が全部一覧表示されるので、その中からクライアント証明書を見つけ出して選択。

iphone4

ただ、これ、クライアント証明書をWindowsの証明書マネージャでインポートするとき、「このキーをエクスポート可能にする」にチェックをいれていないと、iPhone構成ユーティリティーでエラー(例外?)でプロファイルに追加できません。デフォルトでチェックが入っていません。

p12 import

あとは、「書き出す」で、プロファイルを書き出して、iPhone4へメール経由でインストール。

ですが・・・・

フォルダにアクセスしても・・・クライアント証明書が効かず、エラーでアクセスできません!(;。;)

やっぱ・・・おれおれ証明書では無理なのかなぁ・・・う~ん・・・。教えてエロイ人!

検索しても、そんなことをしている人がほとんどいないのか、情報が得られず。

う~ん・・・iPhone・携帯端末用に、ダイジェスト認証に切り替えるべきか・・・?

サーバー側で作ったディレクトリのシンボリックリンクを作ってそれに対してアクセス制限かけることにしようかな・・・。やっぱユーザー/パスワード認証か~・・・。

インターネット上にフォルダを置きたい

意味不明なタイトルです。適切なタイトル名が思いつかんかった。

ネット上に個人的なフォルダを持ちたい場合は、無料のサービスがいろいろありますね。実際、僕もマイクロソフトのSkyDriveサービスのアカウントを取得してファイルの置き場にしています。25GBもあるのでまぁ容量的な問題はないのですが、いかんせん使い勝手が悪い。転送速度が遅い。他の無料のサービスもそうなんですが、便利なことは便利なんですが、細かいところで少なからず不満があります。

で、今回、ちょっとWindows7の挙動が掴めず、ちょっとハマったので、その備忘録です。

ちょい、長めの記事なので・・・

続きを読む