syghの新フラグメント置き場

プログラミングTipsやコード断片の保管場所です。お絵描きもときどき載せます。

HDDの移行作業

(これは2011-06-27に書いた故OCNブログの記事を移植したものです)

自作PCを最初(2007年)に組んだときの160GBと後から追加した400GBだけではそろそろ容量が足らなくなってきた上に、メインOS(Windows 7)のシステム ドライブが圧迫されてきたので、以前買っておいた1TBのHDDに移行しました*1。ちなみに個人的な好みにより、旧HDD、新HDDいずれも日立GST製です。

といっても、OSのクリーン インストールはさすがにきついので、市販のコピーソフト+フリーのパーティション編集ソフトを使用しました。

以下、作業手順。

(1) NeoSmart EasyBCDを使い、WinXP x86 + WinVista x86 + Win7 x64のマルチブート構成を解除して、Win7のシングルブートに一本化。

(2) LB コピー ワークス(CDブート)を使い、Windows PE上でブート パーティションとシステム パーティションを古いHDD(2台)から新しいHDD(1台)へまるごとコピー。

(3) MiniTool Partition Wizard Home Editionを使い、パーティションの空き領域を増やす。

ちなみに(3)はWindows 7標準の[ディスクの管理]スナップイン ツールでも一応できますが、細かいカスタマイズがGUIで直感的に行なえるPartition Wizardを選択しました。


それより手こずったのが(2)の作業。実は後からインスコしたWin7のインストール ドライブ(C:)のほうはシステムではなくブート パーティション扱いで、以前マルチブートしていたWinXPのインストール ドライブ(P:)のほうがシステム パーティション扱いになっていました。で、このシステム パーティションのある160GB HDDを取り外して400GB HDDだけの構成にすると「DISK BOOT FAILURE,INSERT SYSTEM DISK AND PRESS ENTER」とBIOSに言われてWindows 7が起動できません。160GB HDDはもう要らないんだよ……Windows 7のインストールDVDを使ってスタートアップ修復を実行、再起動するも効果なし。ログを見るとパーティション テーブルにシステム パーティションが見つからないことが原因らしい。そりゃそうですね。やはりツールでは修復のしようがないらしいです。

https://sygh-jp.github.io/content_hosting/software_ss/active_system_partition_ss_2011_06_26a.png

いろいろ調べてみたけど、システム パーティションを変更する方法はなさそうなので、仕方ないHDDの統合はあきらめてOSのクリーン インストールから始めるか……
と思ったんですが、実はLB コピー ワークスはパーティションのRawコピーができます。
古いHDDの(P:)をまず新しいHDDにコピー、さらに(C:)を新しいHDDにコピーしたのち、古いHDDを外した状態でLB コピー ワークスを使って以前と同じドライブ レターを割り当てることで、物理HDD×2を別のHDD×1に完全統合し、無事起動できるようになりました。やったね!

ファイルの読み書き速度もかなり速くなりました。ただ……新しいHDDはHDS721010CLAなんだけど、あんまり評判は良くないみたいです。自分はシーク音に関しては別にあまり気にしないんですが、耐久性に難があるのはちょっとね……やはりHDDは定期的にクローンを作ってまるごと交換するようにしたほうがいいかもしれません*2

ついでにデータ用ドライブ(X:)は論理ドライブ化しました。ちなみにVistaの入っていた(V:)とか古いデータ用ドライブ(E:)はもう要らないのでコピーしていません。

あと、プライマリ パーティション(or拡張パーティション)は1つの物理ディスク上に4つまでしか作成できないので、複数のHDDを統合するときは注意する必要があります。

https://sygh-jp.github.io/content_hosting/software_ss/active_system_partition_ss_2011_06_27a.png

その後のお話

2014年7月、今度はHDDの調子が悪くなり、今にも死にそうな状態に陥ったので、またHDDを交換しました。せっかくなのでM/BやCPU/GPU、OSを刷新する良い機会だと思い、今度はHDDはコピーせず、まっさらの新しいHDD(Western Digital Red 1TB)にWindows 8.1クリーンインストールして、ユーザーデータだけ古いHDDから抽出する形で移行しました*3。日立GSTは先行きが怪しかったので、日立に次いで信頼性の高いWDを選択しましたが、今回は日立のHDDが突然死せずに最後までギリギリ動作してくれたので助かりました。自分はPC自作派ですが、可能な限りバルク品は買わないなどの注意を払っているせいか致命的なハズレパーツ(初期不良/欠陥製品)に当たったことはほとんどなく、そういう意味では幸運なのかもしれません。

なお日立がHDD事業を売却した今、個人的におすすめのHDDメーカーはWDです。

ハードディスクはどこのメーカー製が一番壊れにくいのかが2万5000台の調査結果でついに明らかに - GIGAZINE

*1:昔のHDDは使わずに長期間放置しておくとメカ的な吸着やグリスの固化により故障してしまうという問題があったそうですが、比較的新しい製品に関しては冷暗所で保管すれば問題ないそうです

*2:もちろんユーザーデータに関してはUSBメモリ/外付けHDD(一時的)やDVD/BD(半恒久的)にバックアップを取るのは当たり前なんですが、時間をかければ再構築できるOS・アプリケーション領域に関してもバックアップをとっておくと、いざというときに短時間で復旧できます。

*3:Windows Vista以降では、M/B交換後にOSを再インストールすることなくHDDをそのまま利用するのが難しくなっています。

ライセンスゆるめの日本語無償フォント

(これは2010-10-16に書いた故OCNブログの記事を加筆修正したものです)

当方お勧めの無料で使える日本語フォントです。漫画やイラスト、Webサイト/ゲーム制作などで使えます。いずれも商用利用可ですが、不特定多数に公開する作品などで利用する際は事前・事後連絡が必要なもの、ライセンス表記/ライセンスファイルの添付が必要なもの、またフォントデータ自体の二次配布を禁止しているものもあるので、詳しいライセンス運用形態はそれぞれの公式フォント配布サイトで確認してください。

「梅フォント」はUbuntuのUIに使っている人もいるらしいです。なんでもJIS 2004対応だそうで。パッケージは.7zという見慣れない圧縮フォーマットですが、Windowsユーザーの方はExplzhなどでどうぞ(Explzhは非常に良くできたアーカイバで、拡張性や機能性が高く更新も頻繁です)。
なお、「SOフォント」や「DQフォント」は原作ゲームの著作権的にはどうもグレーな感じがします*1。そのあたりは自己責任でどうぞ。おそらく二次創作の範疇に入ると思われるため、一般流通ルートで販売される作品に原作ゲームの許可を得ず使用するのは危険な気がします。*2

Windows標準の日本語フォントである、「MS 明朝」、「MS ゴシック」、そして「メイリオ」と比較した、MS Office Word 2007上のスクリーンショットは下記(超適当)。*3

https://sygh-jp.github.io/content_hosting/software_ss/jp_font_test_01.png

Unicodeの対応度を調べるつもりで、ハートマーク(U+2665H)と版権マーク(U+00A9H)を表示させてみたところ、実際に対応しているらしいのは「梅フォント」だけでした。スクリーンショットでは「しねきゃぷしょん」や「みかちゃん」も表示できてるっぽく見えますが、Wordではマッピングできない文字があるとき、場合によってはドキュメントの既定のフォント(日本語版ではデフォルトで「MS 明朝」と「Century」)が使用されるので、実際にゲームなどのアプリケーションで表示させようとすると中黒(・)とか豆腐(□)になってしまうんではないかと。通常のGDIであればフォントリンク、WPFやDirectWriteであれば合成フォント機構があるので、表示できない文字はデフォルトでメイリオあたりが代わりに使われるはずです*4。いずれWPF/DirectWriteで表示させるサンプル プログラムでも作ろうかと思います。

あと「梅ゴシック」のASCIIバックスラッシュ(U+005CH)が円記号ではなくバックスラッシュになってるのは謎です……たぶん「梅明朝」とは違ってプログラミング用途のテキストエディターなどで使われることも想定して、円記号ではなくバックスラッシュのグリフが用意されているものと思われます。

各種日本語フォントの表示をJavaScriptでテストできる便利なサイトも見つけました。これはサーバーサイドにフォントがインストールされているようです。

http://fonthack.jp/

最後に

英数字のフォントはせいぜい100文字程度なのに対して、日本語のフォントはJIS第一水準でも3,000文字近くにのぼります。日本語のフォントが概して高価なのも納得できます。デジタルデータはコピーが簡単なので、時として忘れてしまいがちですが、イラストやフォントの制作には膨大な労力がかかっていることを忘れないようにしましょう。
なおダイナフォントなどのように、商用利用する際には追加の使用料が発生する製品もあります。

*1:フォントもロゴと同じく本来は意匠権で保護されるべきですが、日本という国の制度では2014年時点で対象になっていないそうです。

*2:「~風フォント」を使用するときに注意すべき事例として、今回あえて列挙しました。

*3:ちなみに「MS 明朝」、「MS ゴシック」、「メイリオ」はUnicodeに完全対応していません。カバーしているのはJIS 2004規格のみだそうです。「Arial Unicode MS」はUnicode 2.1規格に対応しているそうです。http://edn.embarcadero.com/jp/article/38786

*4:GDIのフォントリンクはWPF/DirectWriteの合成フォントよりも精度や柔軟性が低く、文字によっては適切なフォントにマッピングされず正しく表示できないということがときどきあります。今回の例でいうとあずきフォントおよびみかちゃんフォントの未対応文字がマッピングされず空白のままになっています。

Visual C++ 2010 SP1でのMFC拡張

(これは2011-05-27に書いた故OCNブログの記事を移植したものです)

Microsoftが.NETに注力を始めてからずいぶんと影が薄くなったのが、Visual C++向けのWin32 APIラッパー/デスクトップアプリケーションフレームワークであるMFCなんですが、VC 2010になっても結局MFCはひっそりと拡張され続けています。2010ではリボン リソース エディター(リボン デザイナー)などの目玉機能が追加された挙げ句、SP1では下記2つのコア機能が追加になっています。

あまり話題にはなっていないけど、WPFをどうしても採用できない案件で、高速かつリッチなUIをネイティブ実装するのに使えそうです。どのみちコードベースでガリガリ書いていく必要があるので、生産性や保守性の観点からはWPFには遠く及ばないんですが、D2D/WAMはCOMゆえに結構面倒な部分があるので(COMを知らない人には使えない、また知っていてもコールバック*1まわりが面倒)、そのラッパーが用意されているのは助かるかもしれません。

D2Dが使えるのはWindows 7/Vista SP2 + Platform Update以降であり、Windows XPではD2Dがサポートされないので当然ハードウェア アクセラレーションが効かないのはともかくとして、多分WAMラッパーのほうもXPでは機能制限があるようです。というかD2D/WAMはXPにはバックポートされていないので多分ラッパーも使えません。
WPFは豊富なアニメーション機能が便利なんですが、そのフレームワークのみをネイティブCOM実装したのがWAMになります。とはいえXAML連携などはできません。

Visual Studio 2010 SP1 用の MFC の追加
https://msdn.microsoft.com/ja-jp/library/gg482719.aspx

チュートリアル: MFC プロジェクトへの D2D オブジェクトの追加
https://msdn.microsoft.com/ja-jp/library/gg482848.aspx

チュートリアル: MFC プロジェクトへのアニメーションの追加
https://msdn.microsoft.com/ja-jp/library/gg466500.aspx

VS 2010 SP1をインストールすると、下記の場所にサンプル コードのアーカイブがインストールされます。ただし中に入っているのは差分サンプルが少しだけです。
%ProgramFiles(x86)%\Microsoft Visual Studio 10.0\Samples\1041\VC2010SP1Samples.zip

MFCソースコードが付属しているし、マニュアルもそれなりにあるので、ヘビーユーザーであれば使い方は自分で調べればよいのですが、もっと有用な実装サンプルが増えないと、初心者には厳しいかもしれません。
また、MFC用に提供されているDirect2Dラッパーは、Direct2D 1.0専用になります。Windows 8に実装されているDirect2D 1.1や、Windows 8.1に実装されているDirect2D 1.2を使いたい場合は、MFCラッパーではなく直接COM APIを操作する必要があります。


しかし結局COMはいつまでも残り続けるしぶとい技術ですね。DCOMはWCFなどによってほぼ完全に置換されましたが、COMはネイティブC++から利用できるソフトウェアコンポーネント再利用技術として、今後もしばらくMSプラットフォームの中核であり続けるでしょう。実はWindows 8.xのWinRTもCOMの拡張技術だったりします。

*1:COMのコールバックはイベントシンクという仕組みを使います。イベントシンクはプロセス透過でDCOMにも使える反面、登録作業がかなり煩雑です。

GDI+でアンチエイリアスEMFを作成

(これは2012-09-08に書いた故OCNブログの記事を移植したものです)

MFCCMetaFileDCWindows拡張メタファイル(EMF)を作成した場合、たとえGdiplus::Graphics::FromHDC()でバインドしたGDI+グラフィックスを使ってそのメタファイルを描画してもアンチエイリアスがかかりません。

アンチエイリアスのかかったWindows拡張メタファイル(EMF+)を作りたい場合、Gdiplus::Metafileを使う必要があります。Gdiplus::EmfTypeEmfPlusOnlyもしくはGdiplus::EmfTypeEmfPlusDualオプションを付けてストリーム上に作成したMetafileに、Graphics::FromImage()でバインドしたGDI+グラフィックスを使って描画すればOK。Metafileコンストラクタの第1引数にはファイルパス文字列のほかにCOMのIStreamを渡せるオーバーロードがあるので、ファイルストリーム上だけでなくメモリーストリーム上にメタファイルを作成することもできます。

なお、Graphics::EnumerateMetafile()を使うことで、アンチエイリアスのかかっていないEMFファイルを開き、アンチエイリアスのかかったEMF+ファイルに変換して書き出す、ということもできます。

「EnumerateMetafile()を使ってEMFをEMF+に変換するにはどうすればいいの?」という質問をいただいたので、参考までに簡単なサンプルを下記にアップロードしておきました。ただし、色の半透明化として使われているタイル パターン ブラシまわりなど、対応が不十分だったり、完全に未対応だったりするEMRコマンドがたくさんあります。ソースコードの2次利用はご自由に、ただし自己責任でどうぞ。

MetafileConvertTest.zip

なお、EnumerateMetafile()で列挙されるレコード(コマンド+付随データ)をそのままMetafile::PlayRecord()に渡しても、アンチエイリアスの効いたEMF+(GDI+コマンド)に自動変換してくれるようなことはありません。EMRレコードの意味的な解析が必要になります。また、EMRコマンド構造体が必要になるので、.NETよりネイティブC++のほうが変換処理を書きやすいはずです。変換プログラムとしては、コマンドラインのフリーウェア「EMF to EMF+ Converter」がありますが、こちらはソースコードが公開されてないようです。ちなみにGDIのコマンドと従来のEMFファイルのコマンドは1対1で対応するため、EMFファイルからGDIコマンドを復元するのは容易ですが、GDI+のDrawRectangle()やDrawEllipse()で描画した図形を、従来のEMF Onlyメタファイル形式で保存すると、PolygonやBezierで記録されてしまいます(1対1で対応しない、つまり情報が欠落してしまう不可逆変換)。なので、EMF OnlyのGDIコマンドを、GDI+のDrawPolygon()やDrawBeziers()で変換すること自体はできますが、元となる描画に使用されたGDI+コマンドを復元するのは困難となります。個人的には、EMFからEMF+に変換するよりは、メタファイル生成プログラムを書き直して最初からEMF+として出力するように修正することをお勧めします。

■ EMF+ (オリジナル)
https://sygh-jp.github.io/content_hosting/my_program_ss/emf_plus_test.png

■ EMF (ダウンコンバート、アンチエイリアスの欠落と半透明効果のタイル化)
https://sygh-jp.github.io/content_hosting/my_program_ss/emf_only_test.png

■ EMF+ (アップコンバート、ただし半透明効果の復元には未対応)
https://sygh-jp.github.io/content_hosting/my_program_ss/emf_plus_convert_output_test.png

ネイティブXPSドキュメントAPIが使えるWindows 7(とVista SP2 + Platform Update)以降では、メタファイルの存在意義が小さくなりつつありますし、WPFではWindowsメタファイル形式がそもそもサポートされていません。が、WordやExcelに直接貼り付けられるEMFは、Windows上でのベクトル画像フォーマットとしてまだ利用価値がありそうです。

ちなみにWin7からVistaにPlatform Updateの形でバックポートされたネイティブAPIは、XPSのほかにもかなりたくさんあります。自分が知っている範囲でも、リボン フレームワーク、Direct2D/DirectWrite、DirectX 11、Windows Animation Manager (WAM)、など。全部COMです。WAMはゲーム開発にも使えそうです。

Windows 8(とWin7 SP1 + Platform Update)以降に搭載されている新しいDirect2D 1.1は、印刷処理やメタファイルへの出力もサポートするらしいんですが、メタファイルってのはどういうフォーマットになるんでしょうか? EMF+をサポートするわけじゃないと思うけど……やっぱりXPSなんでしょうか? いずれ試してみたいと思います。

Visual Studioの読み取り専用ファイル編集確認ダイアログ

(これは2013-05-11に書いた故OCNブログの記事を移植したものです)

Visual Studioで読み取り専用ファイルを編集しようとしたら、「読み取り専用ファイルを編集」というタイトルで、「編集しようとしたファイル XXX はディスクで読み取り専用です。ファイルを書き込み可能にしますか? それともこのまま編集しますか?」という確認ダイアログが出てきて、「書き込み可能に設定」をクリックすると読み取り専用属性が自動解除されて編集を開始できるんですが、「今後このダイアログ ボックスを表示しない(メモリでの編集を許可しない)」というチェックボックスを誤ってONにして閉じてしまったら、ダイアログが二度と出てこなくなってしまった。

https://sygh-jp.github.io/content_hosting/software_ss/vs_readonly_file_edit_confirm_dialog_2013_05_11b.png

「ツール」→「オプション」→「環境」→「ドキュメント」あたりにダイアログの表示を復活させるオプションがあるんじゃないかと思って探しましたが、VS 2012ではどこにも見あたらない。

ネット検索をかけてみても、MSDNに「編集しようとしたファイル <name> はディスクで読み取り専用です。」というページはあるんですが、「ソース管理のためのプラグイン タブ ([オプション] ダイアログ ボックス)」で説明されている[リセット]ボタンとやらがどこにも存在しない。ヘルプはVS 2008のものらしいので、VS 2008はどうなってるのか調べたところ、やはりVS 2008にも該当するボタンなどは見あたらない。

このダイアログが出てこないと、読み取り専用ファイルの属性をIDEで自動解除させることができなくなってしまい不便なので、仕方なく「ツール」→「オプション」→「環境」→「ドキュメント」の、「読み取り専用ファイルの編集を有効にし、保存時に警告する」という項目をONにして対処することに。

https://sygh-jp.github.io/content_hosting/software_ss/vs_readonly_file_save_confirm_dialog_2013_05_11b.png

ただ、このオプションだと、「読み取り専用ファイルの保存前に」確認ダイアログが出るだけで、「読み取り専用ファイルの編集開始時に」確認ダイアログは出ません。自分としては「編集開始時に」確認ダイアログが出てくれたほうがありがたいんですが……誰かダイアログ表示の復活方法、知りませんか?

※2016-06-26追記:
コメントで提供していただいたヒントをもとに調べたところ、やはりレジストリに当該フラグ(の否定値)が保存されているようです。新規セットアップしたWindows 8.1上にて、Visual Studio 2012(11.0)/2013(12.0)/2015(14.0) のレジストリ情報を調べてみたところ、下記の値がすべて1になっていました。もし当該ダイアログを誤って非表示にしてしまった場合、下記を0から1に戻すことで対処できるものと思われます(当然レジストリの編集は自己責任で)。

  • HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\11.0\SourceControl\AllowUncontrolledInMemoryEditing
  • HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\12.0\SourceControl\AllowUncontrolledInMemoryEditing
  • HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\14.0\SourceControl\AllowUncontrolledInMemoryEditing

個人的には、ダイアログに「今後このダイアログを表示させない」チェックボックスを表示するんだったら、ダイアログ表示を再び復活させるオプションをちゃんと分かりやすいところに配置するべきだと思います。さらにそういうのが複数あるんだったら、リスト形式で一元管理できるべきなんですが……(コードエディターでの文字列検索/置換終了後に出てくる確認ダイアログの抑制に関しては、復活オプションが存在しますが、1つのチェックボックスで複数のオプションフラグがまとめて管理されてしまっています)

ショートカットファイル(.lnk)をC#で読込・解析・編集・作成

Windows 8.1用にHDDを移行したとき、昔使っていた古いドライブレターをいくつか廃止して統合したので、参考資料画像群*1のしおりとして残しておいたショートカットファイル(*.lnk)のリンク切れが大量に発生しました(リンク先の実体ファイルは別ドライブの別ディレクトリに移動したため)。今日はこいつらをなんとかしてみます。
Windowsのショートカットファイルはバイナリ形式なので、リンク先などの中身を解析・編集するには、情報操作をするためのShell APIを使ったプログラムを書く必要があるのですが、今回はC#での一括処理を書いてみました*2

ShortcutConverter.zip

参照設定に「Windows Script Host Object Model」アセンブリを追加しています。

といっても、ただショートカットファイルのターゲットパスをC#コードで置換するだけでは芸がないし応用も効かないので、

  1. まず古いショートカットファイルの内部情報を中間XMLとして書き出す(Lnk2Xml)
  2. パスの置換・正常化はテキストエディターで中間XMLに対して行なう
  3. 編集後の中間XMLから新たにショートカットファイルを生成する(Xml2Lnk)

という柔軟な多段処理を行なえるようにモジュールを分割します。
以下、処理の抜粋。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyShellHelpers
{
  public static class ShellShortcutHelper
  {
    public static void XmlToLnk(string strInListXmlFilePath)
    {
      var shell = new IWshRuntimeLibrary.WshShell();

      using (var stream = new System.IO.FileStream(strInListXmlFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
      {
        // LINQ to XML (for XPath) を使って読み出す。

        var xDoc = System.Xml.Linq.XDocument.Load(stream);

        var rootNode = xDoc.Root;

        var query =
          from node in rootNode.Elements("Shortcut")
          select node;

        foreach (var elem in query)
        {
          var nonameData = new
          {
            ShortcutFilePath = elem.Attribute("ShortcutFilePath"),
            TargetPath = elem.Attribute("TargetPath"),
            Arguments = elem.Attribute("Arguments"),
            WorkingDirectory = elem.Attribute("WorkingDirectory"),
            Hotkey = elem.Attribute("Hotkey"),
            WindowStyle = elem.Attribute("WindowStyle"),
            Description = elem.Attribute("Description"),
            IconLocation = elem.Attribute("IconLocation"),
          };

          System.Diagnostics.Debug.Assert(nonameData.ShortcutFilePath != null);
          var objShortcut = shell.CreateShortcut(nonameData.ShortcutFilePath.Value) as IWshRuntimeLibrary.IWshShortcut;

          System.Diagnostics.Debug.Assert(nonameData.TargetPath != null);
          objShortcut.TargetPath = nonameData.TargetPath.Value;

          System.Diagnostics.Debug.Assert(nonameData.Arguments != null);
          objShortcut.Arguments = nonameData.Arguments.Value;

          System.Diagnostics.Debug.Assert(nonameData.WorkingDirectory != null);
          objShortcut.WorkingDirectory = nonameData.WorkingDirectory.Value;

          System.Diagnostics.Debug.Assert(nonameData.Hotkey != null);
          objShortcut.Hotkey = nonameData.Hotkey.Value;

          System.Diagnostics.Debug.Assert(nonameData.WindowStyle != null);
          // 1:通常、3:最大化、7:最小化。
          // http://msdn.microsoft.com/ja-jp/library/cc364498.aspx
          objShortcut.WindowStyle = Int32.Parse(nonameData.WindowStyle.Value);

          System.Diagnostics.Debug.Assert(nonameData.Description != null);
          objShortcut.Description = nonameData.Description.Value;

          System.Diagnostics.Debug.Assert(nonameData.IconLocation != null);
          objShortcut.IconLocation = nonameData.IconLocation.Value;

          // IWshShortcut.Save() メソッドを呼び出すと、設定されたプロパティにしたがってショートカット ファイルを保存する。
          // 逆に言えば Save() を実行するまではストレージに保存されない。
          objShortcut.Save();
        }
      }
    }

    public static void LnkToXml(IEnumerable<string> inShortcutFilePaths, string strOutListXmlFilePath)
    {
      var shell = new IWshRuntimeLibrary.WshShell();

      var xmlSettings = new System.Xml.XmlWriterSettings();
      xmlSettings.Indent = true;
      xmlSettings.IndentChars = "\t";
      xmlSettings.Encoding = new UTF8Encoding();

      using (var stream = new System.IO.FileStream(strOutListXmlFilePath, System.IO.FileMode.Create, System.IO.FileAccess.Write))
      {
        using (var xmlWriter = System.Xml.XmlWriter.Create(stream, xmlSettings))
        {
          // 書き込みにも LINQ to XML を使えるが、今回はオーソドックスな方法を使う。

          xmlWriter.WriteStartDocument();
          xmlWriter.WriteStartElement("Shortcuts");
          foreach (var path in inShortcutFilePaths)
          {
            System.Diagnostics.Debug.Assert(System.IO.File.Exists(path));

            // WshShell.CreateShortcut() で既存のショートカット ファイルを開くこともできる。
            var objShortcut = shell.CreateShortcut(path) as IWshRuntimeLibrary.IWshShortcut;

            xmlWriter.WriteStartElement("Shortcut");

            xmlWriter.WriteStartAttribute("ShortcutFilePath");
            xmlWriter.WriteString(path);
            xmlWriter.WriteEndAttribute();

            // NOTE: 64bit OS 上で 32bit アプリケーションから IWshShortcut を使用すると、
            // もとのショートカット ファイルの [リンク先] に "C:\Program Files\..." と記載されてあっても、
            // TargetPath が "C:\Program Files (x86)\..." に勝手に置き換わってしまう模様。
            // 逆に最初から "C:\Program Files (x86)\..." と記載されているものに関しては、
            // 64bit アプリケーションで実行してもそのままとなる。
            // 結論としては、64bit OS でショートカットを読み出し・作成する場合、必ず 64bit ネイティブ プロセスで処理を実行すること。

            //string strTargetPath = objShortcut.TargetPath;

            xmlWriter.WriteStartAttribute("TargetPath");
            xmlWriter.WriteString(objShortcut.TargetPath);
            xmlWriter.WriteEndAttribute();

            xmlWriter.WriteStartAttribute("Arguments");
            xmlWriter.WriteString(objShortcut.Arguments);
            xmlWriter.WriteEndAttribute();

            xmlWriter.WriteStartAttribute("WorkingDirectory");
            xmlWriter.WriteString(objShortcut.WorkingDirectory);
            xmlWriter.WriteEndAttribute();

            xmlWriter.WriteStartAttribute("Hotkey");
            xmlWriter.WriteString(objShortcut.Hotkey);
            xmlWriter.WriteEndAttribute();

            xmlWriter.WriteStartAttribute("WindowStyle");
            xmlWriter.WriteString(objShortcut.WindowStyle.ToString());
            xmlWriter.WriteEndAttribute();

            xmlWriter.WriteStartAttribute("Description");
            xmlWriter.WriteString(objShortcut.Description);
            xmlWriter.WriteEndAttribute();

            xmlWriter.WriteStartAttribute("IconLocation");
            xmlWriter.WriteString(objShortcut.IconLocation);
            xmlWriter.WriteEndAttribute();

            xmlWriter.WriteEndElement();
          }
          xmlWriter.WriteEndElement();
          xmlWriter.WriteEndDocument();
        }
      }
    }
  }
}

困ったことがあったらすぐに自動化ツールをさらっと書けるのがプログラマーの特権ですね。

XmlToLnk()は匿名型やLINQ to XMLを使うなど、やや技巧的に書いています。
なお、ショートカットファイルを読み込むときの注意点はLnkToXml()内のコメントに記載しています。Visual C#を使うときは、プロジェクト設定で[32 ビットの優先]というチェックボックスを外しておきましょう。

*1:実はいかがわしい画像が大半だということはみんなにはナイショだよ。

*2:VBScriptJScriptVB.NETVC++でもやろうと思えばできると思いますが、自分は現時点で最強のインテリセンスを持つVC#が最も好みです。

SAIの不具合

(これは2011-07-27に書いた故OCNブログの記事を加筆修正したものです)

現在判明しているPaint Tool SAI 1.1.0の不具合。Windows 7 SP1 x64、WACOM Bamboo MTE-450で確認。

  1. ファイルに名前を付けて保存した直後に、タブレットの筆圧感知がときどき効かなくなる。SAIを再起動すると直る。
  2. ビュースクロール(ハンド ツール)がときどき効かなくなる。ウィンドウを最小化、復元すると直る。
  3. ビュースクロール中心がときどき異様にずれる(画像のキャンバス領域外になる)。ウィンドウを最小化、復元すると直る。

いずれも、常に毎回発生するわけではなさそう。再現手順が不明。
その後SAI 1.2.0、Windows 8.1 x64、ドライバー5.05-7で確認した結果、(1)と(2)は修正されているような気配。

ちなみにWACOMドライバー自体にも昔から不具合があり、何かの拍子で筆圧が一切効かなくなることがあります。WACOMドライバーで不具合が発生した場合は、アプリの再起動やOSの再起動では解消できず、[タブレット設定ファイルユーティリティ]で設定をリセットしないといけなくなることがあります。