スキップしてメイン コンテンツに移動

第12回北海道開発オフに参加してきた

2009/09/13に第12回北海道開発オフがあったので参加してきた。

今回はなんと参加者20人。ランチも二手に分かれなければならないほどの大盛況。

今回はノートPCを臨時に調達したから、前回設計した「「0時に強制的にシャットダウンするアプリケーション」の実装を行うことにした。とりあえずは実行したら即シャットダウンするまでを目指す。

ソースは以下

IShutdownインターフェイス

’Shutdownを行うメソッド群のインターフェース
Public Interface IShutdown
    'Shutdownを実行する
    Function Shutdown() As Boolean
End Interface

WinShutdownクラス

Windowsで実際にシャットダウンを行うクラス。シャットダウンはWinAPIを使用することになるが、何を使えば良いのだろう。調べてみるとDOBON.NETのこのページが見つかった。ExitWindowsEx関数を使用するとシャットダウンができる、とある。ただしWindowsNT系からはSE_SHUTDOWN_NAMEセキュリティ特権とやらを有効にする必要があるとのこと。これらAPIをPrivateで定義してShutdownメソッドてそれらを使用してシャットダウンする手順を踏む。

Imports System
Imports System.Runtime.InteropServices

'IShutdownインターフェースを実装した、Windows向けシャットダウンクラス
Public Class WinShutdown
    Implements IShutdown

    'Shutdownを実行する(IShutdownインターフェースの実装)
    Public Function Shutdown() As Boolean Implements IShutdown.Shutdown
        Const TOKEN_ADJUST_PRIVILEGES As Integer = &H20
        Const TOKEN_QUERY As Integer = &H8
        Const SE_PRIVILEGE_ENABLED As Integer = &H2
        Const SE_SHUTDOWN_NAME As String = "SeShutdownPrivilege"

        Dim procHandle As Intptr = GetCurrentProcess()

        'トークンを取得する
        Dim tokenHandle As IntPtr
        OpenProcessToken(procHandle, TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY, tokenHandle)

        'LUIDを取得する
        Dim tp As New TOKEN_PRIVILEGES()
        tp.Attributes = SE_PRIVILEGE_ENABLED
        tp.PrivilegeCount = 1
        LookupPrivilegeValue(Nothing, SE_SHUTDOWN_NAME, tp.Luid)

        '特権を有効にする
        AdjustTokenPrivileges(tokenHandle, False, tp, 0, IntPtr.Zero, IntPtr.Zero)

        ExitWindowsEx(ExitWindows.EWX_SHUTDOWN Or ExitWindows.EWX_FORCE, 0)
    End Function

    Private Enum ExitWindows
        EWX_LOGOFF = &H0
        EWX_SHUTDOWN = &H1
        EXW_REBOOT = &H2
        EWX_POWEROFF = &H8
        EWX_RESTARTAPPS = &H40
        EWX_FORCE = &H4
        EWX_FORCEIFHUNG = &H10
    End Enum

    <DllImport("user32.dll", SetLastError:=True)> _
    Private Shared Function ExitWindowsEx(ByVal uFlags As ExitWindows, ByVal dwReason As Integer) As Boolean
    End Function

    <DllImport("kernel32.dll")> _
    Private Shared Function GetCurrentProcess() As IntPtr
    End Function

    <DllImport("advapi32.dll", SetLastError:=True)> _
    Private Shared Function OpenProcessToken(ByVal ProcessHandle As IntPtr, _
                                             ByVal DesiredAccess As Integer, _
                                             ByRef TokenHandle As IntPtr) As Boolean
    End Function

    <DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
    Private Shared Function LookupPrivilegeValue(ByVal lpSystemName As String, _
                                                 ByVal lpName As String, _
                                                 ByRef lpLuid As Long) As Boolean
    End Function

    <StructLayout(LayoutKind.Sequential, Pack:=1)> _
    Private Structure TOKEN_PRIVILEGES
        Public PrivilegeCount As Integer
        Public Luid As Long
        Public Attributes As Integer
    End Structure

    <DllImport("advapi32.dll", SetLastError:=True)> _
    Private Shared Function AdjustTokenPrivileges(ByVal TokenHandle As IntPtr, _
                                                  ByVal DisableAllPrivileges As Boolean, _
                                                  ByRef NewState As TOKEN_PRIVILEGES, _
                                                  ByVal BufferLength As Integer, _
                                                  ByVal PreviousState As IntPtr, _
                                                  ByVal ReturnLength As IntPtr) As Boolean
    End Function
End Class

ShutdownFactoryクラス

IShutdownインターフェースを実装したクラスのインスタンスを生成するファクトリクラス。Windowsの場合は上記のWinShutdownクラスのインスタンスを返している。今回はWindowsだけ実装してみる。これそもそも他のOSで動くのだろうか。

Imports System

'Shutdownファクトリークラス
Public Class ShutdownFactory
    Public Shared Function CreateShutdown() As IShutdown
        Select Case System.Environment.OSVersion.Platform
            Case System.PlatformID.Win32Windows, _
                 System.PlatformID.Win32NT, _
                 System.PlatformID.WinCE
                'Windows
                Return New WinShutdown()

            Case System.PlatformID.Unix
                Throw New NotSupportedException()

            Case System.PlatformID.MacOSX
                Throw New NotSupportedException()
            Case Else
                Throw New NotSupportedException()
        End Select
    End Function
End Class

Programクラス

メインクラス。ファクトリクラスからIShutdown実装されたインスタンスを受け取って、Shutdownメソッド呼んでいる。

Imports System
Imports System.Windows.Forms

Public Class Program
    Public Shared Sub Main(args As String())
        Dim sd As IShutdown = ShutdownFactory.CreateShutdown()
        sd.Shutdown()
    End Sub
End Class

さて、Visual Studioがあればここでメニューから「ビルド」を選択すれば良いのだが、今回は借り物のPCなのでVisual Studioをインストールするのは躊躇った。そこでSDKについてくるMSBuildをコマンドラインから使用する。

<Project DefaultTarget="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <Compile Include="*.vb" />
  </ItemGroup>
  <ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.Windows.Forms" />
    <Reference Include="System.Drawing" />
  </ItemGroup>
  <PropertyGroup>
    <AssemblyName>Pumpkin</AssemblyName>
    <OutputType>WinExe</OutputType>
  </PropertyGroup>
  <Import Project="$(MSBuildBinPath)\Microsoft.VisualBasic.targets" />
</Project>

上記のようなxmlを拡張子*.projで保存する。あとはMSBuildコマンドを実行すると、同ディレクトリの拡張子*.projのファイルを拾ってタスクを実行してくれる。

Setting environment to use Microsoft .NET Framework v2.0 SDK tools.
For a list of SDK tools, see the 'StartTools.htm' file in the bin folder.

>msbuild
Microsoft(R) Build Engine Version 2.0.50727.3053
[Microsoft .NET Framework, Version 2.0.50727.3082]
Copyright (C) Microsoft Corporation 2005. All rights reserved.

2009/09/14 23:15:43 にビルドを開始しました。
__________________________________________________
プロジェクト "Shutdown.proj" (既定のターゲット):

ターゲット PrepareForBuild:
    ディレクトリ "bin\Debug\" を作成しています。
    ディレクトリ "obj\Debug\" を作成しています。
ターゲット CoreCompile:
    c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Vbc.exe /noconfig /define:"CONFIG=\"\",PLATFORM=\"AnyCPU\"" /reference
:c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll,c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Drawing.d
ll,c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Windows.Forms.dll /debug+ /out:obj\Debug\Pumpkin.exe /target:win
exe CountDown.vb IShutdown.vb MainForm.vb Program.vb ShutdownFactory.vb WinShutdown.vb
ターゲット CopyFilesToOutputDirectory:
    "obj\Debug\Pumpkin.exe" から "bin\Debug\Pumpkin.exe" へファイルをコピーしています。
    Shutdown -> \bin\Debug\Pumpkin.exe
    "obj\Debug\Pumpkin.pdb" から "bin\Debug\Pumpkin.pdb" へファイルをコピーしています。

ビルドに成功しました。
    0 警告
    0 エラー

経過時間 00:00:01.68

成功すると、bin\debugフォルダにPumplin.exeができているはず。これを実行するとシャットダウンされる。これをタスクスケジューラに登録すれば良い。

ただ、何のお知らせも無くいきなりシャットダウンするのは心臓によろしくない。「残り何秒」のような表示の画面が欲しいところ。次回はそのあたりを実装してみよう。

コメント

このブログの人気の投稿

VirtualBoxのUbuntu 18.04でNAT + ホストオンリーアダプター

VirtualBoxのVMにインストールしたUbuntu Server 18.04で、ネットワーク割当を「NAT」+「ホストオンリーアダプター」にする場合の設定についてメモ。 VMを作るとき、ゲストOSからインターネットに繋がって、かつホスト-ゲスト間でもつながる環境にしたいとき、VMのネットワーク割当は手っ取り早いのは「ブリッジアダプター」なんだけど、会社のネットワークでは都合がわるかったりするので「NAT」+「ホストオンリーアダプター」にしている。 アダプター1は「NAT」を選択 アダプター2は「ホストオンリーアダプター」を選択 ゲストOSであるUbuntuのネットワーク設定は、NATに該当するNICはDHPCで、ホストオンリーアダプターに該当するNICは固定IPとする。 Ubuntu 18.04ではネットワーク設定はNetplanで行う(Ubuntu 17.10から変わったようだ)。編集する設定ファイルは「/etc/netplan/50-cloud-init.yaml」 network: ethernets: {NATのNIC}: addresses: [] dhcp4: true optional: true {ホストオンリーアダプターのNIC}: addresses: [{固定IPアドレス}] dhcp4: no version: 2 ポイントは、ホストオンリーアダプターに該当するNICの固定アドレスを設定するときに、 ゲートウェイを設定しない こと。 以前に QiitaのVirtualBoxでNAT + Host-Only Network環境を構築する を参考にしていたのに、今回18.04で設定する際にすっかり忘れてはまってしまった。 IPアドレスは「192.168.0.2/24」のようにサブネットマスクも指定する。 NICに指定するデバイス名は以下コマンドで表示されるlogical nameが該当する。 > lshw -class network たいていは「enp0s3」「enp0s8」となるようだ。 設定ファイルを保存したら

画像を切り取って保存する

画像処理において、画像の一部を切り出すことをトリミングというらしい。画像編集ソフトなんかにも大抵「トリミング」機能が実装されているだろう。 そんな画像を切り取る処理を.NETでやってみる。 .NETでは画像を扱う際にはBitmapクラスを使用する。BitmapクラスのCloneメソッドでサイズを指定すれば、そのサイズのBitmapオブジェクトができるので、それを保存すればよい。Cloneメソッドの引数はRectangle構造体とPixelFormat列挙体。 Rectangle構造体とは四角形の位置とサイズを表したもの。元画像の左上を(0, 0)として(X座標, Y座標, 幅, 高さ)の値でRectangle構造体を作成して渡す。PixelFormat列挙体はヘルプを見てもよくわからなかった。とりあえずは元画像の値をそのまま渡す。 例えば photo by tsukacyi のようなユッケの画像をトリミングするプログラムは以下のように書ける '元画像 Dim source As Bitmap source = New Bitmap("source.jpg") '切り取るサイズ Dim rect As Rectangle rect = New Rectangle(30, 80, 400, 320) '切り取り後の画像 Dim trimed As Bitmap trimed = source.Clone(rect, source.PixelFormat) '保存 trimed.Save("trimed.jpg") source.Dispose() trimed.Dispose() トリミング後の画像は以下の通り。

Microsoft.VisualBasic.dllを参照しない(その1)

VB.NETには、VB6に実装されていたCIntなどの型変換関数やLeft、Rightなどの文字列関数が使用できる。これらはMicrosoft.VisualBasic名前空間で定義されている。 個人的にはこれらはVB6との互換性の為に用意されたものと考えていて、あまり使用しないようにしている。名前付けのガイドラインにも沿っていないため、他の.NETなメソッドと並ぶと違和感があるというのもある。 注意:このエントリはVisual Basic 2005 Express Editionと.NET Framework2.0 SDK環境を元に書いています。 そんなわけでチーム開発の場合に、これら関数を使用しないよう、Microdoft.VisualBasic.dllを参照しないプロジェクトを作成しようと考えた。 Visual Studioのプロジェクトのプロパティから「参照」タブを開いてみると、すでに参照しているdllにMicrosoft.VisualBasic.dllが含まれていない。  下の一覧にImportされる名前空間が表示されているが、そこには含まれている。 Importされる名前空間からチェックをはずし以下のソースを書いてみる Public Class VisualBasicTest Public Shared Sub Main() Dim i As Integer = CInt("1991") System.Console.WriteLine(i) End Sub End Class だが、ビルドは通って実行もできてしまう。 逆アセンブリしてみる。 スタートメニューから「Microsoft .NET Framework SDK v2.0」→「Tools」→「MSIL 逆アセンブラ」を起動して、ビルドでできているexeを開く。すると、MANIFESTの部分を見てみると下記記述が見つかる。参照しているようだ。 .assembly extern Microsoft.VisualBasic { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) .ver 8:0:0:0 } きっとVisual Studioが