.NET プリンターのプロパティ画面を表示するには

.NET VistaではPageSetupDialogでプリンタ設定ボタンが表示されないで書いた
プリンターのプロパティ画面を表示する方法ですが、プリンタを変更するとプリンタドライバに依存するDEVMODE 構造体部分がメモリ確保されていないためエラーになることがあり
Marshal.AllocCoTaskMemを使った方法に訂正します。


Imports System.Runtime.InteropServices

Public Class Form1
    <DllImport("winspool.drv", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Private Shared Function OpenPrinter( _
    <MarshalAs(UnmanagedType.LPTStr)> ByVal pPrinterName As String, ByRef hPrinter As IntPtr, ByVal pDefault As IntPtr) As Int32
    End Function

    <DllImport("winspool.drv", SetLastError:=True)> _
    Private Shared Function ClosePrinter(ByVal hPrinter As IntPtr) As Int32
    End Function

    <DllImport("winspool.drv", SetLastError:=True)> _
    Private Shared Function DocumentProperties ( _
        ByVal hwnd As IntPtr, _
        ByVal hPrinter As IntPtr, _
        <MarshalAs(UnmanagedType.LPTStr)> ByVal pDeviceName As String, _
        ByVal pDevModeOut As IntPtr, _
        ByVal pDevModeIn As IntPtr, _
        ByVal fMode As Int32) As Int32
    End Function

    Private Const DM_OUT_BUFFER As Short = 2
    Private Const DM_IN_PROMPT As Short = 4
    Private Const DM_IN_BUFFER As Short = 8

    <DllImport("kernel32", SetLastError:=True)> _
    Private Shared Function GlobalLock(ByVal hmem As IntPtr) As IntPtr
    End Function
    
    <DllImport("kernel32", SetLastError:=True)> _
    Private Shared Function GlobalFree(ByVal hmem As IntPtr) As Int32
    End Function
    
    <DllImport("kernel32", SetLastError:=True)> _
    Private Shared Function GlobalUnlock(ByVal hMem As IntPtr) As Int32
    End Function


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim printer As New Printing.PrinterSettings
        Dim ret As Boolean = ShowPrinterProperty(Me.Handle, printer) 
    End Sub
        
    Private Function ShowPrinterProperty(ByVal hwnd As IntPtr, ByVal printer As Printing.PrinterSettings) As Boolean
        Dim ret As Int32

        'プリンタのハンドルを取得する
        Dim hPrinter As IntPtr = IntPtr.Zero
        ret = OpenPrinter(printer.PrinterName, hPrinter, IntPtr.Zero)
        If (ret = 0 OrElse hPrinter = IntPtr.Zero) Then
            Return False
        End If

        Try

            'プリンタのDEVMODE構造体のサイズを取得する
            Dim iDevModeSize As Integer = DocumentProperties(IntPtr.Zero, hPrinter, printer.PrinterName, IntPtr.Zero, IntPtr.Zero, 0)

            'Input用のDEVMODE構造体のハンドルを取得する
            Dim hDevModeIn As IntPtr = printer.GetHdevmode()
            Dim pDevModeIn As IntPtr = GlobalLock(hDevModeIn)

            'OutOut用のDEVMODE構造体のメモリを確保する
            Dim pDevModeOut As IntPtr = Marshal.AllocCoTaskMem(iDevModeSize)
            Try

                '設定画面を開く
                ret = DocumentProperties(hwnd, hPrinter, printer.PrinterName, pDevModeOut, pDevModeIn, DM_IN_PROMPT Or DM_IN_BUFFER Or DM_OUT_BUFFER)
                If (ret = 1) Then
                    printer.SetHdevmode(pDevModeOut)
                End If

                '
                Return True

            Finally
                GlobalUnlock(hDevModeIn)
                GlobalFree(hDevModeIn)
                Marshal.FreeCoTaskMem(pDevModeOut)
            End Try
        Finally
            ClosePrinter(hPrinter)
        End Try
    End Function
End Class

Java認定資格 SJC-P 310-055

Java認定資格 SJC-P 310-055 に合格しましたヽ(*´∀`)ノ

会社が受験料を払ってくれたのですが、「落ちた場合は自己負担で受かるまで受験すること」だったので
バカ高い試験だし結構勉強しましたw


今までJavaを使った仕事はしたことがありません。
数年前に一度だけJavaの仕事の話があって、ちょっと勉強したぐらいです。
結局その仕事も流れて、業務では未経験です。

試験勉強は以下の2冊の参考書を使いました。
Sunテキスト Sun Certified Programmer for Java 2 Platform 5.0 [Exam310-055] SKILL-UP TEXT (SKILL-UP TEXT―Sunテキスト) (単行本)
徹底攻略 Java2 プログラマ問題集 Platform5.0 対応 (ITプロ/ITエンジニアのための徹底攻略) (単行本(ソフトカバー))

試験は60問で35問以上(58%)で合格です。時間は180分。
内容は思っていたより難しくなく、上記の2冊を勉強したら合格点は取れると思います。
私は52問正解(86%)でした。

試験はセパレータで区切られた席に座って行うのですが
隣の人のキーボードやマウスの「カチッカチッ」という音や、椅子が「ギギー」となるので結構集中できません。

私の隣には二人のおじさんが試験を受けておられ、すごい勢いで「カチッカチッ」となるので
同じ試験を受けているわけではないけれど「お~すっげ~」と感心してしまいました。
あと隣の人の独り言も気になりますw
「ここがこうで・・・」とかブツブツいって試験が終わったら「よっしゃー」と言って帰られましたw
防音用にヘッドホンがあるのですが、あんまり役に立ちませんでした。

試験問題はパソコンで行うのですが、グレーの背景に読みにくいフォントです。
時間はたっぷりあるので、ゆっくり問題を解いても見直す時間はあります。
が、ドラッグドロップ問題は見直すと前回の選択がクリアされた状態で表示されますヽ(`Д´#)ノ
私は面倒だったのでドラッグドロップ問題は見直ししませんでした・・・
できればドラッグドロップ問題は1回目でしっかり選択しておくことをお勧めします。

あと1問だけメモ用紙がいるような問題がありました。
受け付けの方は説明してくれませんでしたが、試験前にもらえるプラケースのなかに
プラスチックの下敷きとマジックが入っていて、これをメモにするそうです。
試験終了後に教えてもらえました(´Д⊂ヽ
メモ用紙がないのでヘルプ画面を立ち上げて検索文字を入力するテキストボックスをメモにしてました・・・私。

一番の難関は試験時間が長いので、途中で「もういいやぁ」という気分に襲われます。
ここをグッとこらえて、試験時間いっぱい全部使ってやるという気持ちで耐えるのですが
今度は寒さに襲われ、トイレに行きたくなりますw
女性は膝かけなど用意された方が良いと思います♪

あいつぐ仕様変更・・・プログラマー殺すに刃物はいらぬ

今回の開発は納期が短いので仕様が30%程しか確定してない状態で開発が始まった。
嫌だなぁとは思ったけれど、仕事だし「やれ」と言われればやるしかない。
そんな大きなプロジェクトでもないから大丈夫かなと思った自分が甘かった(つд⊂)

ある程度の前提条件などを聞き出して、開発を始めた。
もちろんプログラムには変更が入る可能性も含めて幅を持たせたし
ざっくり作って、仕様が確定するまであまり作りこまないようにした。

とりあえずプロトタイプを提出し仕様を固めてもらう。
固まったところから作りこみを始める。

けれど仕様が固まったところなのに、仕様変更が入る。
前提条件を覆すような仕様変更も入る。
2度3度・・・5度、6度・・・。

何度作っても、仕様変更。仕様変更。仕様変更。
作っても作っても終わらない。
同じところばっかり作ってるような気がして、全体の終わりも見えない。

もうね、ソースコードの美しさとかどうでもよくなるね。
心が挫けて、心が折れて、鬱っぽくなる・・・
変に悲しくて、変に攻撃的で。うわぁ~自分ヤバイって感じ初めて経験しました。・゚・(ノД`)・゚・。

私はプログラムは美しく書きたいんです。
仕様変更で少しずつ崩れていくのは仕方ないにしても、新規に作成する時だけは美しくしておきたいんです。
この気持ちが相次ぐ仕様変更には耐えられない。
せめて前提条件だけは変えてくれるな( ゚д゚ )クワッ!!

プログラマー殺すに刃物はいらぬ、仕様を三回変えれば良い とはよく言ったもんだ。

前提条件を覆すような仕様変更を何度も言われたら、そのシステムはもうどうでもいいわ。
愛着ゼロ。モチベーションもゼロ。
そんなプログラマからよいシステムなんて作れませんよ?わかってる?道連れだよ?

仕様はもうちょっとよく考えてね!

.NET Windowsサービス

Windowsサービスは滅多に作らないので、いつも作り方を忘れてしまうのでメモ。

VisualStudio2008の場合
Windowsサービスプロジェクトを作成します。

サンプルコードとして、1分間隔でイベントログを出力するようにします。
自動で作成される「Service1.vb」にコードを書きます。
Public Class Service1

    Private _timer As Timers.Timer

    Protected Overrides Sub OnStart(ByVal args() As String)
        ' サービスを開始するコードをここに追加します。このメソッドによって、
        ' サービスが正しく実行されるようになります
        Me._timer = New Timers.Timer
        Me._timer.Interval = 1000 * 60
        AddHandler Me._timer.Elapsed, AddressOf Timer_Elapsed
        Me._timer.Start()
    End Sub

    Protected Overrides Sub OnStop()
        ' サービスを停止するのに必要な終了処理を実行するコードをここに追加します。
        Me._timer.EndInit()
        Me._timer.Dispose()
    End Sub

    Private Sub Timer_Elapsed(ByVal sender As Object, ByVal e As Timers.ElapsedEventArgs)
        EventLog.WriteEntry("Timer_Elapsed")
    End Sub

End Class

次にインストーラを作成します。
まず、「Service1.vb」のデザイナ上で右クリックし「インストーラの追加」をクリックします。

「ProjectInstaller.vb」が追加されます。
ServiceInstallerコントロールのServiceNameプロパティを変更します。(サービス一覧に表示される名前です)


ソリューションにセットアッププロジェクトを追加します。


ソリューションエクスプローラで追加したセットアッププロジェクトを右クリックし「追加」→「プロジェクト出力」を選択します。


「プロジェクトの出力グループの追加」ダイアログより「プライマリ出力」を選択します。


ソリューションエクスプローラで追加したセットアッププロジェクトを右クリックし「表示」→「カスタム動作」を選択します。


表示されたカスタム動作エディタの「カスタム動作」を右クリックし「カスタム動作の追加」を選択します。


表示される「プロジェクトから項目を選択」ダイアログで「アプリケーションフォルダ」を選択し「OK」をクリックします。

つづいて表示されるダイアログから「WindowsService1(アクティブ)のプライマリ出力」を選択し「OK」をクリックします。


カスタム動作エディタの「インストール」、「確定」、「ロールバック」、「アンインストール」のそれぞれにプライマリ出力が追加されます。


あとはセットアッププロジェクトをビルドしインストールを実行します。
インストールの途中でユーザー名とパスワードを求められるので、
ユーザ名にコンピュータ名¥ログインユーザ名
パスワードにログインパスワードを入力します。

インストールが終了したらサービスを起動します。
Vistaの場合
コントロールパネル→管理ツール→サービスを起動し、サービス一覧に表示される「TestService」を開始します。
コントロールパネル→管理ツール→「イベントビューアを起動し、一分間隔でイベントログが出力されていることを確認します。

.NET LinkLabelのTabStopを表示する

LinkLabelのTabStopプロパティはプロパティウインドウにもインテリセンスにも表示されません。
インテリセンスには表示されませんが無視してLinkLabel1.TabStop=Falseとコードで記述すれば、フォーカスされなくなります。

それでもデザイナで設定したいときはLinkLabelを継承したカスタムコントロールを作成します。
Imports System.ComponentModel
Public Class CsLinkLabel
    Inherits System.Windows.Forms.LinkLabel

    Private _TabStop As Boolean = False

    <DefaultValue(False), Browsable(True), EditorBrowsable(EditorBrowsableState.Always), Category("動作")> _
     Public Shadows Property TabStop() As Boolean
        Get
            Return Me._TabStop
        End Get
        Set(ByVal value As Boolean)
            Me._TabStop = value
            MyBase.TabStop = Me._TabStop
        End Set
    End Property

    Protected Overrides Sub OnHandleCreated(ByVal e As System.EventArgs)
        MyBase.OnHandleCreated(e)
        MyBase.TabStop = Me._TabStop
    End Sub

End Class

.NET MDI子フォーム(FormBoderStyle=None)を表示するときにちらつく

MDI親フォームからFormBorderStyle=Noneの子―フォームを開くとき
一瞬だけタイトルバーが表示されてしまいます。


現象を確認出来るコード

MDI親フォームでは開くメニューを用意し、Clickイベントで子―フォームを開きます。
Public Class FrmMdi
    Private Sub 開くToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles 開くToolStripMenuItem.Click
        '開いているフォームをすべて閉じる
        For Each f As Form In Me.MdiChildren
            f.Close()
        Next
        '新しいフォームを開く
        Dim frm As New FrmChild
        frm.MdiParent = Me
        frm.FormBorderStyle = Windows.Forms.FormBorderStyle.None
        frm.Dock = DockStyle.Fill
        frm.Show()
    End Sub
End Class
MDI子フォームは新規作成したフォームのままで特にコードはかきません。
Public Class FrmChild
End Class

開くボタンを連打すると子フォームのタイトルバーが一瞬表示されると思います。


この現象を防ぐために、子フォームのOnLoadをオーバーライドし下記のように記述します。
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
    Me.FormBorderStyle = Me.FormBorderStyle
    MyBase.OnLoad(e)
End Sub

これでタイトルバーが一瞬表示される現象が発生しなくなるのですが
今度は子フォームを閉じるとき一瞬画面全体が上に移動してしまう現象が発生しちらつきますw
たぶんタイトルバーの高さ分だけ移動していると思われます。
こちらは回避方法がわかりません。

そこでMDI親フォームの開くボタンではフォームを開いてから、他のフォームを閉じることにし、閉じているところを隠しました。
根本的な解決ではありませんが・・・

Private Sub 開くToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles 開くToolStripMenuItem.Click
        ''開いているフォームをすべて閉じる
        'For Each f As Form In Me.MdiChildren
        '    f.Close()
        'Next

        '新しいフォームを開く
        Dim frm As New FrmChild
        frm.MdiParent = Me
        frm.FormBorderStyle = Windows.Forms.FormBorderStyle.None
        frm.Dock = DockStyle.Fill
        frm.Show()

        '開いているフォームをすべて閉じる
        For Each f As Form In Me.MdiChildren
            If f IsNot frm Then
                f.Close()
            End If
        Next

    End Sub