よたよたと調べ物をするところ

与太郎プログラマのブログ

WindowsにElixirをインストールする方法

chocolateyをインストールして、chocolateyでElixirをインストールする。
1.管理者モードで起動したコマンドプロンプトで次を実行する。

@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%systemdrive%\chocolatey\bin

2.コマンドプロンプトを閉じる。もう一度管理者モードでコマンドプロンプトを起動して次を実行する。

cinst elixir

via. 2行でWindowsにElixirをインストールするたった一つの冴えているようなやり方

Ruby 1.9.2 と Rails 3.2 で、rake test に色をつける。ついでに自動テストも。

仕事で使わないとほんと腰が重い。Ruby 1.9とRails3をやっと使ってみる。
Rails1や2のころはテストを書く作業が大変だった。とくにfixturesのメンテナンスが洒落にならない。最近はfixturesではなくモックを使うプラグインが出て来ているとかで、多少楽になっていればいいなと思う。

試しに作ったプロジェクトで rake test とテストを実行したら色がついてなくて素っ気なさすぎるので、まず成功は緑、失敗は赤で表示するよう設定。ついでにautotestも入れてみよう。

via. http://seanbehan.com/ruby/color-output-with-testunit-autotest-and-ruby-1-9/


RailsプロジェクトのGemfileを修正してテスト実行時に、"test-unit"、"minitest"、minitestで結果表示を書式化する"turn"、"autotest"、以上を使うよう設定。

group :development, :test do
  gem 'test-unit'
  gem 'minitest'
  gem 'turn'
  gem 'ZenTest'
  gem 'autotest-rails-pure'
end

そんでbundleを実行して必要なGemをインストール。

$ bundle install

test/test_helper.rb に以下を追加。

require 'test/unit/ui/console/testrunner'
class Test::Unit::UI::Console::TestRunner
  def guess_color_availability 
    true 
  end
end

テストを走らせてみる。

$ rake test

テスト結果が色付きで表示されれば成功。続いて自動テストを実行する。

$ bundle exec autotest

以後ソースコードを保存するたびにテストが走る。RSpecとかShouldaとかはまたおいおい。

.NETのフォームで2段階の継承をしてからイベントを作成するとフォームデザイナーがエラーを吐く件

VB.NET2010で確認。

  1. Form1を作成し、ボタンを1つ配置。ModifiersをProtectedにして継承先でイベント作成を可能にする。そしてビルドを実行。
  2. Form1を継承してForm2を作成。ビルドを実行。
  3. Form2を継承してForm3を作成。ビルドを実行。
  4. Form3にてボタンのClickイベントを記述。ビルドを実行。画面を閉じる。
  5. Form3を開こうとすると「値を Null にすることはできません。パラメーター名: objectType 」と表示されフォームデザイナーが起動しない。(無視して続行すれば開く)

ただしビルドはできるし、実行も問題ない。でも設計画面が開けないのではなあ。
.NETってフォームの継承は事実上1段だけで、多段継承は危ないってことかしら。でも画面で多段継承できないと、なんのための継承なんだか、という噺になるような。
念のためフィードバックセンターに出しておこう。

.NETでいろいろなコントロールの読み出し専用制御を一括で行うパネルを作ってみる

.NETのUIコントロールについて、設計の一貫性のなさに煩悶した。特に「TextBox系にしかReadOnlyがない」には参った。
データの表示と編集を同じフォームで処理したいケースは普通によくあるのではないか。みんなどうしているの。

ということで
http://d.hatena.ne.jp/zecl/20090201/p1
を参考にさせてもらい、配置したコントロールについて読み出し専用制御をするパネル「ReadOnlyPanel」を作ってみた。TextBox系はReadOnlyプロパティで、それ以外はメッセージを補足して入力操作を禁止(ついでにWM_ENABLEも捕まえて淡色表示をあるていど防止)することで、文字入力した部分は表示のみのときでもコピーができるようにした。

以下ソースコード

Imports System.Runtime.InteropServices
Imports System.Security
Imports System.Security.Permissions
Imports System.ComponentModel

''' <summary>
''' 配置したコントロールについて一括でReadOnly制御ができるパネル。
''' </summary>
''' <remarks></remarks>
Public Class ReadOnlyPanel
    Inherits Panel

    '本パネルのReadOnlyプロパティ値保持用
    Private _readonly As Boolean = True

    '本パネルで管理するコントロールと、メッセージ処理用リスナを保持する辞書
    Private _control_list As New Dictionary(Of Control, ReadOnlyMessageListenr)

    ''' <summary>
    ''' 本パネルに配置したコントロールを読み出し専用にするかを設定/取得します。
    ''' </summary>
    <DefaultValue(False)> _
    Public Property [ReadOnly] As Boolean
        Get
            Return _readonly
        End Get
        Set(ByVal value As Boolean)
            '値を保存
            _readonly = value
            '本パネルでReadOnly管理するコントロールのReadOnlyを処理
            For Each ctrl_and_listener As KeyValuePair(Of Control, ReadOnlyMessageListenr) In _control_list
                If TypeOf ctrl_and_listener.Key Is ReadOnlyPanel Then
                    '子がEditorPanelなら、そのReadOnlyで制御
                    DirectCast(ctrl_and_listener.Key, ReadOnlyPanel).ReadOnly = value
                ElseIf TypeOf ctrl_and_listener.Key Is TextBoxBase Then
                    'TextBox系なら、ReadOnlyで制御
                    'まずFalseにして、
                    DirectCast(ctrl_and_listener.Key, TextBoxBase).ReadOnly = False
                    If value Then
                        'Trueなら背景色もあわせて制御
                        DirectCast(ctrl_and_listener.Key, TextBoxBase).ReadOnly = True
                        DirectCast(ctrl_and_listener.Key, TextBoxBase).BackColor = ctrl_and_listener.Value.BackColor
                    End If
                End If
            Next
        End Set
    End Property

    ''' <summary>
    ''' 任意のコントロールをコンテナとして扱うかを調べて返します。
    ''' </summary>
    Private Function IsContainer(ByRef aControl As Control) As Boolean
        If TypeOf aControl Is IContainerControl Then
            Return True
        ElseIf TypeOf aControl Is Panel Then
            Return True
        ElseIf TypeOf aControl Is GroupBox Then
            Return True
        Else
            Return False
        End If
    End Function

    ''' <summary>
    ''' 本パネルにコントロールを追加したときに、ReadOnly制御を差し込む処理を追加します。
    ''' </summary>
    Protected Overrides Sub OnControlAdded(ByVal e As ControlEventArgs)
        MyBase.OnControlAdded(e)
        If Not Me.DesignMode Then DoControlAddedHander(Me, e)
    End Sub
    '#-- 内部処理
    Private Sub DoControlAddedHander(ByVal sender As Object, ByVal e As ControlEventArgs)
        Dim hook As ReadOnlyMessageListenr = Nothing
        If Not TypeOf e.Control Is ReadOnlyPanel Then
            If TypeOf e.Control Is TabControl Then
                AddHandler e.Control.ControlAdded, AddressOf DoControlAddedHander
                AddHandler e.Control.ControlRemoved, AddressOf DoControlRemovedHander
            ElseIf IsContainer(e.Control) Then
                AddHandler e.Control.ControlAdded, AddressOf DoControlAddedHander
                AddHandler e.Control.ControlRemoved, AddressOf DoControlRemovedHander
                For Each ctrl In e.Control.Controls
                    DoControlAddedHander(e.Control, New ControlEventArgs(ctrl))
                Next
            End If
            If Not (TypeOf e.Control Is ReadOnlyPanel Or TypeOf e.Control Is TabControl) Then
                hook = New ReadOnlyMessageListenr(Me, e.Control)
            End If
        End If
        _control_list.Add(e.Control, hook)
    End Sub

    ''' <summary>
    ''' 本パネルからコントロールを削除したときに、ReadOnly制御を差し込む処理を削除します。
    ''' </summary>
    Protected Overrides Sub OnControlRemoved(ByVal e As ControlEventArgs)
        If Not Me.DesignMode Then DoControlRemovedHander(Me, e)
        MyBase.OnControlRemoved(e)
    End Sub
    '#-- 内部処理
    Private Sub DoControlRemovedHander(ByVal sender As Object, ByVal e As ControlEventArgs)
        If Not TypeOf e.Control Is ReadOnlyPanel Then
            If TypeOf e.Control Is TabControl Then
                RemoveHandler e.Control.ControlAdded, AddressOf DoControlAddedHander
                RemoveHandler e.Control.ControlRemoved, AddressOf DoControlRemovedHander
            ElseIf IsContainer(e.Control) Then
                RemoveHandler e.Control.ControlAdded, AddressOf DoControlAddedHander
                RemoveHandler e.Control.ControlRemoved, AddressOf DoControlRemovedHander
                For Each ctrl In e.Control.Controls
                    DoControlRemovedHander(e.Control, New ControlEventArgs(ctrl))
                Next
            End If
        End If
        _control_list.Remove(e.Control)
    End Sub

#Region "ReadOnly制御用メッセージリスナクラス"
    Protected Class ReadOnlyMessageListenr
        Inherits NativeWindow

        Public Const WM_ENABLE As UInteger = &HA
        Public Const WM_SETFOCUS As UInteger = &H7
        Public Const WM_KILLFOCUS As UInteger = &H8
        Public Const WM_KEYDOWN As UInteger = &H100
        Public Const WM_KEYUP As UInteger = &H101
        Public Const WM_CHAR As UInteger = &H102
        Public Const WM_CUT = &H300
        Public Const WM_PASTE As UInteger = &H302
        Public Enum WM_MOUSE As Integer
            WM_MOUSEFIRST = &H200
            WM_MOUSEMOVE = &H200
            WM_LBUTTONDOWN = &H201
            WM_LBUTTONUP = &H202
            WM_LBUTTONDBLCLK = &H203
            WM_RBUTTONDOWN = &H204
            WM_RBUTTONUP = &H205
            WM_RBUTTONDBLCLK = &H206
            WM_MBUTTONDOWN = &H207
            WM_MBUTTONUP = &H208
            WM_MBUTTONDBLCLK = &H209
            WM_MOUSEWHEEL = &H20A
        End Enum

        <StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)> _
        Public Structure ComboBoxInfo
            Public Size As Integer
            Public RectItem As Rectangle
            Public RectButton As Rectangle
            Public ButtonState As Integer
            Public ComboBoxHandle As IntPtr
            Public EditBoxHandle As IntPtr
            Public ListBoxHandle As IntPtr
        End Structure

        <SuppressUnmanagedCodeSecurity()> _
        <DllImport("user32.dll", CharSet:=CharSet.Auto)> _
        Public Shared Function GetComboBoxInfo(ByVal comboBoxHandle As IntPtr, ByRef pComboBoxInfo As ComboBoxInfo) As Boolean
        End Function

        Private _editor_panel As ReadOnlyPanel
        Private _control As New Control
        Private _control_backcolor As Color
        Private _is_textbox_base As Boolean
        Private _is_tabcontrol As Boolean

        ''' <summary>
        ''' リッスン対象コントロールの初期背景色を保持します。
        ''' </summary>
        Public ReadOnly Property BackColor As Color
            Get
                Return _control_backcolor
            End Get
        End Property

        ''' <summary>
        ''' コンストラクタ
        ''' </summary>
        Public Sub New(ByRef aEditorPanel As ReadOnlyPanel, ByRef aControl As Control)
            AddHandler aControl.HandleCreated, AddressOf Me.OnHandleCreated
            AddHandler aControl.HandleDestroyed, AddressOf Me.OnHandleDestroyed

            _editor_panel = aEditorPanel
            _control = aControl
            _control_backcolor = aControl.BackColor
            _is_textbox_base = TypeOf _control Is TextBoxBase
            _is_tabcontrol = TypeOf _control Is TabControl
        End Sub

        ''' <summary>
        ''' ハンドル生成時のイベントハンドラ
        ''' </summary>
        Private Sub OnHandleCreated(ByVal sender As Object, ByVal e As EventArgs)
            If TypeOf sender Is ComboBox Then
                Dim cbi As ComboBoxInfo
                cbi.Size = Marshal.SizeOf(cbi)
                If GetComboBoxInfo(sender.Handle, cbi) Then
                    AssignHandle(cbi.EditBoxHandle)
                End If
            Else
                AssignHandle(CType(sender, Control).Handle)
            End If

        End Sub

        ''' <summary>
        ''' ハンドル破棄時のイベントハンドラ
        ''' </summary>
        Private Sub OnHandleDestroyed(ByVal sender As Object, ByVal e As EventArgs)
            ReleaseHandle()
        End Sub

        ''' <summary>
        ''' メッセージ処理
        ''' </summary>
        Protected Overrides Sub WndProc(ByRef m As Message)
            '編集用パネルのReadOnlyがTrueのとき
            If _editor_panel.ReadOnly Then
                '淡色表示をさせないため、WM_ENABLEを処理しない
                If m.Msg = WM_ENABLE And m.WParam = 0 Then
                    m.Result = 0
                    Exit Sub
                End If
                'TextBox系以外はReadOnlyプロパティ制御できないので、
                '特定のメッセージを無視することでReadOnly相当状態を実現する。
                If Not _is_textbox_base Then
                    Select Case (m.Msg)
                        Case WM_SETFOCUS, WM_KILLFOCUS
                            m.Result = 0
                            Exit Sub
                        Case WM_KEYDOWN, WM_KEYUP, WM_CHAR, WM_CUT, WM_PASTE
                            m.Result = 0
                            Exit Sub
                        Case WM_MOUSE.WM_MOUSEFIRST,
                            WM_MOUSE.WM_MOUSEMOVE,
                            WM_MOUSE.WM_MOUSEMOVE,
                            WM_MOUSE.WM_LBUTTONDOWN,
                            WM_MOUSE.WM_LBUTTONUP,
                            WM_MOUSE.WM_LBUTTONDBLCLK,
                            WM_MOUSE.WM_RBUTTONDOWN,
                            WM_MOUSE.WM_RBUTTONUP,
                            WM_MOUSE.WM_RBUTTONDBLCLK,
                            WM_MOUSE.WM_MBUTTONDOWN,
                            WM_MOUSE.WM_MBUTTONUP,
                            WM_MOUSE.WM_MBUTTONDBLCLK,
                            WM_MOUSE.WM_MOUSEWHEEL
                            m.Result = 0
                            Exit Sub
                    End Select
                End If
            End If
            '通常のメッセージ処理を実行
            MyBase.WndProc(m)
        End Sub
    End Class
#End Region

End Class

QuillとS2Dao.NETで、データベース接続文字列を動的に変更する

via. http://d.hatena.ne.jp/senbei3/20090410


app.configのquillセクションにはデータベース情報()を記述する。そこにはDB接続文字列を書く訳で、ユーザー名とパスワードが必要。
ASP.NETだとweb.configを暗号化すればよいらしいけれどWindowsフォームアプリケーションではそうもいかない。
というわけで、app.configのDB接続文字列にはユーザー名とパスワードは書かずにおいて、アプリ実行時に動的に差し込む方法。当然DBアクセス前に行う必要がある。例えばメインフォームのLoadイベントでやる場合。


メインフォームのメンバ変数に以下を追加。

Protected _dataSourceDict As SelectableDataSourceProxyWithDictionary


メインフォームのLoadイベントに以下を記述。

'DIでデータソース辞書を取得
QuillInjector.GetInstance.Inject(Me)
'app.configのquillセクションで定義したデータソースを取得
Dim ds As DataSourceImpl = _dataSourceDict.DataSourceCollection("sampleDb")
'データベース接続文字列にユーザー名とパスワードを追加
ds.ConnectionString &= ";user id=username;password=mypass"


これで実行時にユーザー名とパスワードを動的に設定できる。

S2Dao.NET -> OLEDB -> SQL Serverで、IDENTITY操作にハマったのでメモ

Quillでのお話。S2Containerでも同じかな。
app.configのを記述するときに、

Provider=SQLOLEDB.1;Data Source=serverName;Initial Catalog=databaseName;User ID=userName;Password=userPassword;

と、Providerにバージョン番号を付けると、DB種類を認識できずにS2DaoがStandardDBとして扱ってしまう。
これは

Provider=SQLOLEDB;Data Source=serverName;Initial Catalog=databaseName;User ID=userName;Password=userPassword;

とプロバイダ識別名だけを記述すればOK。


DB種類が判別できない状態ではEntityのID属性が正しく働かない。
例えば、

<ID(IDType.IDENTITY)>  _
Public Property Id() As Integer
   Get
      Return _id
   End Get
   Set
      _id = value
   End Set
End Property

と書いてIDを自動発番に任せる。
ところがStandatdDBは当然ID属性をサポートしておらず、ほんとは"select @@identity"が必要なところでnullを返す。結果、Insert後のID取得処理で例外が発生する。


これで2時間もハマって悔しい!