2012年2月9日木曜日

.NETプログラム例外落ちイベント(CLR20r3)を読み取る

.NETで作ったプログラムが例外落ちしますと、イベントビューアに記録が残ります。この情報をどのようにして読み取っていくか、につきまして。例:

ログの名前: Application
ソース: Windows Error Reporting
日付: 2012/02/09 14:27:58
イベント ID: 1001
タスクのカテゴリ: なし
レベル: 情報
キーワード: クラシック
ユーザー: N/A
コンピューター: DD5
説明:
障害バケット 、種類 0
イベント名: CLR20r3
応答: 使用不可
Cab ID: 0

問題の署名:
P1: wazato.exe
P2: 1.0.0.0
P3: 4f335959
P4: wazato
P5: 1.0.0.0
P6: 4f335959
P7: 4
P8: 10
P9: wazato.WazatoException
P10:


P1からP9までの意味する所につきましては、情報源が見つかりました。

How do you read a runtime (CLR20r3) error report?
http://social.msdn.microsoft.com/Forums/ar/clr/thread/eabef2f0-631b-41b8-91f1-431bbcb9b4df

clr20r3 from a window's service calling a dot net dll
http://social.msdn.microsoft.com/forums/en-US/clr/thread/3fca118c-cbba-4be8-ac64-02af7a4ec3ca/

要約しますと:

P1: exeファイル名の名前(32文字制限付き)
P2: exeファイルのアセンブリ バージョン
P3: exeファイルのタイムスタンプ(16進数)… new DateTime(1970, 1, 1).AddSeconds(P3).ToLocalTime()
P4: 欠陥アセンブリの名前(64文字制限付き)
P5: 欠陥アセンブリのバージョン
P6: 欠陥アセンブリのタイムスタンプ(16進数)… new DateTime(1970, 1, 1).AddSeconds(P6).ToLocalTime()
P7: 欠陥アセンブリの型とメソッド(16進数)。MethodDef
P8: 欠陥メソッドのIL命令(16進数)。オフセット
P9: スローされた例外の種類(32文字制限付き)

P9の文字列は長い場合、末尾のExceptionを省略してみたり、160ビット位(32進数×32桁)のハッシュ値にされたりしてしまいます。

ハッシュ関数はSHA-1か何かかと思いますが、未だ計算方法は判っていません。

P7とP8につきまして:

ildasmを使います。

[ファイル]→[ダンプ]で、.ilファイルを出力できます。このファイルを見ながら検証していきます。

P7が4。
上から見て4番目のメソッドかなとも思いますが、確証は有りません。
MemberInfo.MetadataToken の一部を抽出している模様です。
Assembly.LoadFile(P4_Assembly).GetModules()[0].ResolveMethod(0x06000004);

このようなコードで、対象メソッドの情報がゲットできるようです。

ildasm を用いる場合、Ctrl+M または [表示]→[メタ情報]→[表示!] で、一覧表示。
06000004 を探します。
TypeDef #1 (02000002)
-------------------------------------------------------
    TypDefName: wazato.Program  (02000002)
    Flags     : [NotPublic] [AutoLayout] [Class] [Abstract] [Sealed] [AnsiClass] [BeforeFieldInit]  (00100180)
    Extends   : 01000001 [TypeRef] System.Object
    Method #1 (06000001) 
    -------------------------------------------------------

    ...


    Method #4 (06000004) [ENTRYPOINT]
    -------------------------------------------------------
        MethodName: Main (06000004)
        Flags     : [Private] [Static] [HideBySig] [ReuseSlot]  (00000091)
        RVA       : 0x00002056
        ImplFlags : [IL] [Managed]  (00000000)
        CallCnvntn: [DEFAULT]
        ReturnType: Void
        No arguments.
        CustomAttribute #1 (0c000010)
        -------------------------------------------------------
            CustomAttribute Type: 0a000010
            CustomAttributeName: System.STAThreadAttribute :: instance void .ctor()
            Length: 4
            Value : 01 00 00 00                                      >                <
            ctor args: ()


P8が10。IL_0010を探します。丁度throwしているのが判ります。

...
  .method private hidebysig static void A() cil managed
...
  .method private hidebysig static void B() cil managed
...
  .method private hidebysig static void C() cil managed
...
  .method private hidebysig static void  Main() cil managed
  {
    .entrypoint
    .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 )
    // コード サイズ       17 (0x11)
    .maxstack  8
    IL_0000:  call       void [System.Windows.Forms]System.Windows.Forms.Application::EnableVisualStyles()
    IL_0005:  ldc.i4.0
    IL_0006:  call       void [System.Windows.Forms]System.Windows.Forms.Application::SetCompatibleTextRenderingDefault(bool)
    IL_000b:  newobj     instance void wazato.WazatoException::.ctor()
    IL_0010:  throw 
  } // end of method Program::Main

ちなみにC#のソースコードはこうです:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.IO;
namespace wazato {
    static class Program {
        static void A() { }
        static void B() { }
        static void C() { }
        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main() {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            throw new WazatoException();       
        }
    }
    public class WazatoException : ApplicationException {
    }
}

[2022/06/01 追記]

.NET Framework 4.x のアプリで、クラッシュダンプを作成する方法がわかりました。

Visual Studio 2022 にて、クラッシュダンプファイルの内容を確認できました。

WinDbg のコマンドライン ツール CDB を使って、クラッシュダンプの stack trace を覗く方法です。


2012年1月18日水曜日

Reg-Free COM on .NET 2.0の実践

.NET 2.0 C#から、MFCで作ったようなCOM DLLを、レジストリに登録しないで呼び出す方法を、備忘したいと思います。
MFCにはCOleDataSourceクラスとか、OLEと連携する上で役立つクラスが多数有ります。

これらを.NETだけで実現しようとすると、膨大な手数になります。

C#でドラッグ・アンド・ドロップできるアプリ(バックアップの復元ソフト)を作る際に、活用できました。

具体的な手法は又の機会に譲るとして、ここでは要素技術のみを列挙いたします:

Handling Shell Data Transfer Scenarios
  FILEDESCRIPTOR
  FILECONTENTS
IAsyncOperation

Registration-Free Activation of COM Components: A Walkthrough
  Assembly Manifests

手順:

マニフェストファイルを用意します。紫色の部分を変えていきます。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
      type="win32"
      name="DiffBkRestore"
      processorArchitecture="x86"
      version="1.0.0.0"
      />
  <file name="VFCopy.dll">
    <comClass clsid="{8FADBC22-CF67-4AEE-9B67-451C106B187E}" threadingModel="Apartment" />
  </file>
</assembly>


mt.exeでexeの中に入れ込みます。外付けのmanifestファイルでは、Reg-Freeが機能しませんでした。
紫色の部分を変えます。

mt.exe -manifest DiffBkRestore.manifest -outputresource:bin\x86\release\DiffBkRestore.exe;#1

私はNSISでセットアップを構築しています。NSISのビルド実行時にmt.exeが動くように作りこみするのも絶妙な手でしょう。

!system '"C:\Program Files (x86)\Microsoft Visual Studio 8\SDK\v2.0\Bin\mt.exe" -manifest DiffBkRestore.manifest -outputresource:bin\x86\release\DiffBkRestore.exe;#1' = 0
;C:\Program Files (x86)\Microsoft Visual Studio 8\VC\bin\mt.exe
;C:\Program Files (x86)\Microsoft Visual Studio 8\Common7\Tools\Bin\mt.exe
;C:\Program Files (x86)\Microsoft Visual Studio 8\SDK\v2.0\Bin\mt.exe

mt.exeのパスが大量にメモってあるのは、環境が変わった際に探しやすいようにする為です。

where mt.exeで探してきました。

NSISでは、!systemでシェルコマンドを実行できます。失敗したらビルドが停止するように、" = 0"を最後に足しています。指定しないと失敗しても止まりません→ほぼ確実に見落します。

後、注意点として、processorArchitecturex86等に固定しないといけません。

.NETのアセンブリは、x86でビルドしましょう。

Any CPUでビルドしてしまうと、64ビットWindowsでは、x64で起動してしまいます。これではx86で作ったCOM DLLを読み込みできません。具体的には、BadImageFormatExceptionが発生すると思います。

2011年12月8日木曜日

VACUUM FULL on PostgreSQL 8.2

PostgreSQL 8.2系で、大多数のラージオブジェクトを廃棄しました。容器も小さくするにはVACUUM FULLを掛ける必要が有りました。

主にサムネイルの保存に使っています。20万個有ったものを削除し、1000個程が残っただけでした。

しかし、テーブルの容器が5.5GB程まで膨れ上がっていて、通常のVACUUMでは小さくなりません。これも小さくしたいと思いました。

インデックスが入っているのでloidを指定する検索は速いようです。しかし、SELECT COUNTによる数え上げには時間が掛かります。400秒程。通常のVACUUM掛けても検索時間はまったく変わりません。

次のようにSQL文を発行した所、容器が小さくなりました!

REINDEX TABLE pg_largeobject;
VACUUM FULL pg_largeobject;

クエリーは、2272953 ミリ秒で結果なしでうまく帰りました。 ※約38分

掃除前:
2011/12/08  09:57     1,073,741,824 2613
2011/12/07  17:45     1,073,741,824 2613.1
2011/12/08  10:02     1,073,741,824 2613.2
2011/12/08  10:02     1,073,741,824 2613.3
2011/12/08  09:57     1,073,741,824 2613.4
2011/12/08  09:49       225,402,880 2613.5
               6 個のファイル       5,594,112,000 バイト


掃除後:
2011/12/08  10:17           638,976 2613
2011/12/08  10:17                 0 2613.1
2011/12/08  10:17                 0 2613.2
2011/12/08  10:17                 0 2613.3
2011/12/08  10:17                 0 2613.4
2011/12/08  10:17                 0 2613.5
               6 個のファイル             638,976 バイト


容器が小さくなり、大幅に使用量が削減されました。

2011年10月24日月曜日

ブログの投稿記事をバックアップするツール

ブログの投稿記事をHTMLで保存し、関連付け・リンクされている画像も一緒に保存してくれるツールを開発いたしました。

但し、最適化のため、対応しているブログのみバックアップできます:
  • keikai.topblog.jp
  • blog.livedoor.jp(一部)
  • blog.zaq.ne.jp(一部)
非対応のブログにつきましては、ParseTemplate.xmlを自分で書き換える必要が有ります。

入手は、こちらから:
http://code.google.com/p/blogbkup/downloads/list

使い方(バックアップの設定方法):
  1. プログラム(例:BlogBkup_0_2_portable.exe)を入手し、
  2. セットアップします。
  3. [スタート]→[ブログバックアップ]→[ブログバックアップを開く]を選択します。
  4. フォルダで右クリックし、
  5. [新規作成]→[ブログバックアップ 設定]を選択します。
  6. 新しい項目、「新規 ブログバックアップ 設定」が出てきますので、ファイル名を自由に設定します。付けた名前を覚えておいてください
    例:「社長ブログ」など。
  7. ダブルクリックします。
  8. 「BlogBkup設定」画面が出て参ります。「ブログURL」に、執筆されているブログのトップページURLを貼り付けます。
  9. 「対応しているかどうか、クリックして確認」をクリックします。
  10. 終わるまで待ち、
  11. 1以上の「記事数」がヒットするひな形があれば、バックアップできる可能性は高いです。
  12. フロッピーディスクのアイコン(保存)をクリックし、保存します。
  13. 再生ボタン(バックアップ開始)をクリックし、バックアップを開始します。
バックアップの確認方法:
  1. 以前に付けた名前と同じ名称のフォルダが作られます。フォルダを開きます。
  2. 多数のHTMLファイルと、imgフォルダが有ります。
  3. HTMLファイルに記事が、imgフォルダに記事からリンクしている画像が入っています。
2回目以降:
  1. 作成したアイコンをダブルクリックします。

2011年9月7日水曜日

Access MDBファイルのリンクテーブル(ODBC)接続先を変更するソフト

EditMdbLink

Microsoft Access データベース(Mdbファイル)の、ODBCを経由したリンクテーブル接続先を容易に更新できるソフトです。

次のような場合に有効です:

SQL Serverからリンクテーブルを貼り付けている。開発するとき、お客様に納品するとき、別々のサーバに接続したい。

入手は、こちらから。

2011年9月6日火曜日

loを作成し、書き込む方法

PostgreSQLで、ラージオブジェクトを作成し、書き込む方法。

SELECT lo_creat(0);
結果: 17242

SELECT lo_open(17242, 393216);
結果: 0

SELECT lowrite(0, E'\x31\x32\x33'::bytea);
結果: 3

SELECT lo_close(0);
結果: 0

注意点:同じトランザクション内で実行いたします。そうでなければ、lo_openで開いたハンドルがlowriteに渡る頃には閉じていて、使えないようになります。こちらに注意書きが有りました。

2011年9月2日金曜日

WinDbgで、エラー報告データを解析(.NETfx 2.0向け)

備忘録です:

WinDbgを起動する。

[File]→[Open Crash Dump...]

次の場所を開く(Windows Server 2003):
%USERPROFILE%\Local Settings\Application Data\PCHealth\ErrorRep\QSignoff

1556130.cab等、.cabファイルの方を開く。

~~~

打ち込む(.NETfx 2.0対応のため):
.load C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos.dll

結果、次の様なメッセージ:
0:000> .load C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos.dll
------------------------------------------------------------
sos.dll needs a full memory dump for complete functionality.
You can create one with .dump /ma <filename>
------------------------------------------------------------


~~~
SOS デバッガ拡張 (SOS.dll) コマンド一覧は、こちら。

~~~

打ち込む(.NETfxアプリのダンプかどうか確認する為):
!Threads

結果:
0:000> !Threads
ThreadCount: 3
UnstartedThread: 0
BackgroundThread: 2
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
                                      PreEmptive   GC Alloc           Lock
       ID OSID ThreadOBJ    State     GC       Context       Domain   Count APT Exception
   0    1 1a18 001650e8      a020 Enabled  0116c2ac:0116dfe8 001617e8     1 MTA Sync2ss.RequestFailureException (0116b16c)
   2    2 1a3c 00171858      b220 Enabled  00000000:00000000 001617e8     0 MTA (Finalizer)
   4    3 18f0 001afb08   200b220 Enabled  01140178:01141fe8 001617e8     1 MTA


~~~

打ち込む(落ちる原因となった例外の情報を得るため):
!pe

結果:
0:000> !pe
Exception object: 0116b16c

Exception type: Sync2ss.RequestFailureException
Message: SS.Auth失敗
InnerException: System.Net.WebException, use !PrintException 01169cb0 to see more
StackTrace (generated):
    SP       IP       Function
    0012F150 00C7125A Sync2ss!Sync2ss.Sync+AutoAuth.Auth()+0x4b2
    0012F290 00C70A4C Sync2ss!Sync2ss.Sync+<GetRootEnts>d__e.MoveNext()+0x94
    0012F30C 6C2FB7A3 System_Core_ni!System.Linq.Enumerable.First[[System.__Canon, mscorlib]](System.Collections.Generic.IEnumerable`1<System.__Canon>, System.Func`2<System.__Canon,Boolean>)+0x6b
    0012F340 00C706BF Sync2ss!Sync2ss.Sync.Find(System.String)+0x177
    0012F3B8 00C7033C Sync2ss!Sync2ss.Sync.Run()+0x64
    0012F444 00C7018A Sync2ss!Sync2ss.Program.Main(System.String[])+0x11a

StackTraceString: <none>
HResult: 80131500


* 上に出ていますException typeMessageStackTraceが確認できたら原因追求がし易くなるはずです。

~~~

打ち込む(オブジェクトの型・フィールド等を確認する):
!do 0116b16c

結果:
0:000> !do 0116b16c
Name: Sync2ss.RequestFailureException
MethodTable: 007551b8
EEClass: 00c80bf4
Size: 72(0x48) bytes
 (C:\Program Files\Sync2ss\Sync2ss.exe)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
79330b24  40000b5        4        System.String  0 instance 00000000 _className
7932ff98  40000b6        8 ...ection.MethodBase  0 instance 00000000 _exceptionMethod
79330b24  40000b7        c        System.String  0 instance 00000000 _exceptionMethodString
79330b24  40000b8       10        System.String  0 instance 0116b148 _message
7932a480  40000b9       14 ...tions.IDictionary  0 instance 00000000 _data
79330cb8  40000ba       18     System.Exception  0 instance 01169cb0 _innerException
79330b24  40000bb       1c        System.String  0 instance 00000000 _helpURL
79330740  40000bc       20        System.Object  0 instance 0116b280 _stackTrace
79330b24  40000bd       24        System.String  0 instance 00000000 _stackTraceString
79330b24  40000be       28        System.String  0 instance 00000000 _remoteStackTraceString
79332d70  40000bf       34         System.Int32  1 instance        0 _remoteStackIndex
79330740  40000c0       2c        System.Object  0 instance 00000000 _dynamicMethods
79332d70  40000c1       38         System.Int32  1 instance -2146233088 _HResult
79330b24  40000c2       30        System.String  0 instance 00000000 _source
793333ec  40000c3       3c        System.IntPtr  1 instance        0 _xptrs
79332d70  40000c4       40         System.Int32  1 instance -532459699 _xcode


今回得られた有用な情報は、ここまでです。