2011-08-31T00:24:28の更新内容

programming/tips/activate_window_by_hotkey/index.wiki.txt

current previous
1,15 1,9
 
${smdncms:title,ホットキーを使ってフォームをアクティブにする}
${smdncms:title,ホットキーを使ってフォームをアクティブにする}
 
${smdncms:keywords,C#,VB.NET,RegisterHotKey,WndProc,WM_HOTKEY}
${smdncms:keywords,C#,VB.NET,RegisterHotKey,WndProc,WM_HOTKEY}
~
${smdncms:tags,plat/win,api/win32,lang/vb,lang/c#}
${smdncms:tags,plat/win,api/win32,lang/vb}
~
${smdncms:document_versions,codelang=cs,codelang=vb}
API関数のRegisterHotKeyおよびUnregisterHotKeyを使用してウィンドウハンドルに関連づけられたホットキーを設定・解除する事ができる。 ホットキーを指定する際は、そのキーと修飾キー(ALT, CTRL, SHIFT)及び、ホットキーのIDを指定する必要がある。 ここでは、ホットキーのIDをキーと修飾キーの値から決定している。
 

        

        
~
API関数のRegisterHotKeyおよびUnregisterHotKeyを使用することで、ウィンドウハンドルに関連づけられたホットキーを設定・解除する事ができる。 ホットキーを指定する際は、キーコードと修飾キー(ALT, CTRL, SHIFT)及び、ホットキーのIDを指定する必要がある。 ホットキーのIDは、0x0000~0xBFFFの範囲でアプリケーションに一意な値を設定する必要がある。
また、実際にホットキーが押されたときには、WM_HOTKEYメッセージが送られてくるので、これに対してフォームをアクティブにする記述をすれば、ホットキーが押された時点でフォームをアクティブにすることができる。 なお、ホットキーを識別するためには、lParamの値が指定したホットキーと等しいかを比較する必要がある。
+

          
+
登録したホットキーが実際に押されたときにはWM_HOTKEYメッセージが送られてくるので、これに対してフォームをアクティブにする記述をすれば、ホットキーが押された時点でフォームをアクティブにすることができる。 複数のホットキーから目的のものを識別するためには、lParamの値を比較する必要がある。
+

          
+
なお、RegisterHotKeyで指定するキーコード・修飾キーの値は、&msdn(netfx,type,System.Windows.Forms.dll,System.Windows.Forms.Keys){Keys列挙体};の値を使うことができるが、修飾キーとして使えるWindowsロゴキーに該当するメンバがないので、個別に定数などを用意する必要がある。 さらに、システムや他のアプリケーションで既に使用されている場合は、そのホットキーを登録をすることはできない(RegisterHotKeyの呼び出しに失敗する)。
+

          
+
以下の例では、Ctrl+Spaceでフォームをアクティブにし、Win+Enter通知音(SystemSounds.Beep)を鳴らすようにホットキーの割り当てを行っている。
 

        

        
 
-関連するページ
-関連するページ
 
--[[programming/tips/disable_alt_f4]]
--[[programming/tips/disable_alt_f4]]
20,210 14,100
 

        

        
 
#googleadunit
#googleadunit
 

        

        
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.ComponentModel;
+
using System.Media;
+
using System.Runtime.InteropServices;
+
using System.Windows.Forms;
+

          
+
/// <summary>ホットキーの登録・解除を行うためのクラス</summary>
+
class HotKey {
+
  [DllImport("user32", SetLastError = true)]
+
  private static extern int RegisterHotKey(IntPtr hWnd,
+
                                           int id,
+
                                           int fsModifier,
+
                                           int vk);
+

          
+
  [DllImport("user32", SetLastError = true)]
+
  private static extern int UnregisterHotKey(IntPtr hWnd,
+
                                             int id);
+

          
+
  public HotKey(IntPtr hWnd, int id, Keys key)
+
  {
+
    this.hWnd = hWnd;
+
    this.id = id;
+

          
+
    // Keys列挙体の値をWin32仮想キーコードと修飾キーに分離
+
    int keycode = (int)(key & Keys.KeyCode);
+
    int modifiers = (int)(key & Keys.Modifiers) >> 16;
+

          
+
    this.lParam = new IntPtr(modifiers | keycode << 16);
+

          
+
    if (RegisterHotKey(hWnd, id, modifiers, keycode) == 0)
+
      // ホットキーの登録に失敗
+
      throw new Win32Exception(Marshal.GetLastWin32Error());
+
  }
+

          
+
  public void Unregister()
+
  {
+
    if (hWnd == IntPtr.Zero)
+
      return;
+

          
+
    if (UnregisterHotKey(hWnd, id) == 0)
+
      // ホットキーの解除に失敗
+
      throw new Win32Exception(Marshal.GetLastWin32Error());
+

          
+
    hWnd = IntPtr.Zero;
+
  }
+

          
+
  public IntPtr LParam {
+
    get { return lParam; }
+
  }
+

          
+
  private IntPtr hWnd; // ホットキーの入力メッセージを受信するウィンドウのhWnd
+
  private readonly int id; // ホットキーのID(0x0000〜0xBFFF)
+
  private readonly IntPtr lParam; // WndProcメソッドで押下されたホットキーを識別するためのlParam値
+
}
+

          
+
public class Form1 : Form {
+
  public static void Main()
+
  {
+
    Application.Run(new Form1());
+
  }
+

          
+
  private HotKey hotkeyActivate;
+
  private HotKey hotkeyNotify;
+

          
+
  protected override void OnLoad(EventArgs e)
+
  {
+
    const Keys modifierWinKey = (Keys)0x00080000; // Windowsロゴキー
+

          
+
    // Ctrl+SpaceをID 0のホットキーとして登録
+
    hotkeyActivate = new HotKey(this.Handle, 0, Keys.Control | Keys.Space);
+

          
+
    // Win+EnterをID 1のホットキーとして登録
+
    hotkeyNotify = new HotKey(this.Handle, 1, modifierWinKey | Keys.Enter);
+
  }
+

          
+
  protected override void OnClosing(CancelEventArgs e)
+
  {
+
    // 登録しているホットキーを解除
+
    hotkeyActivate.Unregister();
+
    hotkeyNotify.Unregister();
+
  }
+

          
+
  // ホットキーの入力メッセージを処理する
+
  protected override void WndProc(ref Message m)
+
  {
+
    const int WM_HOTKEY = 0x312;
+

          
+
    if (m.Msg == WM_HOTKEY && m.LParam == hotkeyActivate.LParam) {
+
      // フォームをアクティブにする
+
      Activate();
+
    }
+
    else if (m.Msg == WM_HOTKEY && m.LParam == hotkeyNotify.LParam) {
+
      // 通知音を鳴らす
+
      SystemSounds.Beep.Play();
+
    }
+
    else {
+
      base.WndProc(ref m);
+
    }
+
  }
+
}
+
}}
+
#tabpage(VB)
 
#code(vb){{
#code(vb){{
+
Imports System
+
Imports System.ComponentModel
+
Imports System.Media
 
Imports System.Runtime.InteropServices
Imports System.Runtime.InteropServices
+
Imports System.Windows.Forms
 

        

        
~
''' <summary>ホットキーの登録・解除を行うためのクラス</summary>
Public Class Form1
~
Class HotKey
    Inherits System.Windows.Forms.Form
~
  <DllImport("user32", SetLastError := True)> _

          
~
  Private Shared Function RegisterHotKey(ByVal hWnd As IntPtr, _
    ' ホットキーを登録する
~
                                         ByVal id As Integer, _
    <DllImport("user32", EntryPoint:="RegisterHotKey")> _
~
                                         ByVal fsModifier As Integer, _
    Private Shared Function RegisterHotKey( _
~
                                         ByVal vk As Integer) As Integer
        ByVal hWnd As IntPtr, _
~
  End Function
        ByVal id As Integer, _
~

          
        ByVal fsModifier As Integer, _
~
  <DllImport("user32", SetLastError := True)> _
        ByVal vk As Integer) _
~
  Private Shared Function UnregisterHotKey(ByVal hWnd As IntPtr, _
    As Integer
~
                                           ByVal id As Integer) As Integer
    End Function
~
  End Function

          
~

          
    ' ホットキーを解除する
~
  Public Sub New(ByVal hWnd As IntPtr, ByVal id As Integer, ByVal key As Keys)
    <DllImport("user32", EntryPoint:="UnregisterHotKey")> _
~
    Me.hWnd = hWnd
    Private Shared Function UnregisterHotKey( _
~
    Me.id = id
        ByVal hWnd As IntPtr, _
~

          
        ByVal id As Integer) _
~
    ' Keys列挙体の値をWin32仮想キーコードと修飾キーに分離
    As Integer
~
    Dim keycode As Integer = CInt(key And Keys.KeyCode)
    End Function
~
    Dim modifiers As Integer = CInt(key And Keys.Modifiers) >> 16

          
~

          
    ' ホットキーの修飾キー
~
    Me._lParam = New IntPtr(modifiers Or keycode << 16)
    Private Const MOD_ALT As Byte = &H1
~

          
    Private Const MOD_CONTROL As Byte = &H2
~
    If RegisterHotKey(hWnd, id, modifiers, keycode) = 0 Then
    Private Const MOD_SHIFT As Byte = &H4
~
      ' ホットキーの登録に失敗

          
~
      Throw New Win32Exception(Marshal.GetLastWin32Error())
    <Flags()> _
~
    End If
    Private Enum KeyModifiers As Integer
~
  End Sub

          
~

          
        None = 0
~
  Public Sub Unregister()
        Alt = MOD_ALT
~
    If hWnd = IntPtr.Zero Then Return
        Control = MOD_CONTROL
~

          
        Shift = MOD_SHIFT
~
    If UnregisterHotKey(hWnd, id) = 0 Then

          
~
      ' ホットキーの解除に失敗
    End Enum
~
      Throw New Win32Exception(Marshal.GetLastWin32Error())

          
~
    End If

          
~

          

          
~
    hWnd = IntPtr.Zero
    Dim id As Integer ' ホットキーのID
~
  End Sub
    Dim lParam As IntPtr ' WndProc()メソッドでホットキーを識別するためのlParam値
~

          

          
~
  Public ReadOnly Property LParam As IntPtr
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
~
    Get

          
~
      Return _lParam
        ' Alt + Ctrl + Spaceをホットキーに指定する
~
    End Get
        RegisterHotKey(Me.Handle, Keys.Space, KeyModifiers.Alt Or KeyModifiers.Control, id, lParam)
~
  End Property

          
~

          
    End Sub
~
  Private hWnd As IntPtr ' ホットキーの入力メッセージを受信するウィンドウのhWnd

          
~
  Private ReadOnly id As Integer ' ホットキーのID(0x0000〜0xBFFF)
    Private Sub Form1_Closing(ByVal sender As Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
~
  Private ReadOnly _lParam As IntPtr ' WndProcメソッドで押下されたホットキーを識別するためのlParam値

          
~
End Class
        ' 指定したホットキーを解除する
-
        UnregisterHotKey(Me.Handle, id)
-

          
-
    End Sub
-

          
-
    ' ホットキーの設定を行う
-
    Private Function RegisterHotKey(ByVal hWnd As IntPtr, ByVal key As Keys, ByVal modifier As KeyModifiers, ByRef id As Integer, ByRef lParam As IntPtr) As Boolean
-

          
-
        ' これらの値は戻り値として返される
-
        id = CInt(key) Or CInt(modifier) * &H100
-
        lParam = New IntPtr(CInt(modifier) Or CInt(key) * &H10000)
-

          
-
        ' ホットキーの指定 
-
        Dim result As Integer
-

          
-
        result = RegisterHotKey(hWnd, id, CInt(modifier), CInt(key))
-

          
-
        Return (result <> 0)
-

          
-
    End Function
-

          
-
    ' ホットキーの入力メッセージを取得する
-
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
-

          
-
        Const WM_HOTKEY As Integer = &H312
-

          
-
        If m.Msg = WM_HOTKEY AndAlso m.LParam.Equals(lParam) Then
-

          
-
            ' フォームをアクティブにする
-
            Me.Activate()
-

          
-
        Else
-

          
-
            ' 基底クラスでのメッセージ処理
-
            MyBase.WndProc(m)
-

          
-
        End If
-

          
-
    End Sub
 

        

        
~
Public Class Form1
#End Region
+
  Inherits Form
 

        

        
+
  Public Shared Sub Main()
+
    Application.Run(New Form1())
+
  End Sub
+

          
+
  Private hotkeyActivate As HotKey
+
  Private hotkeyNotify As HotKey
+

          
+
  Protected Overrides Sub OnLoad(ByVal e As EventArgs)
+
    Const modifierWinKey As Keys = CType(&h00080000, Keys) ' Windowsロゴキー
+

          
+
    ' Ctrl+SpaceをID 0のホットキーとして登録
+
    hotkeyActivate = New HotKey(Me.Handle, 0, Keys.Control Or Keys.Space)
+

          
+
    ' Win+EnterをID 1のホットキーとして登録
+
    hotkeyNotify = New HotKey(Me.Handle, 1, modifierWinKey Or Keys.Enter)
+
  End Sub
+

          
+
  Protected Overrides Sub OnClosing(ByVal e As CancelEventArgs)
+
    ' 登録しているホットキーを解除
+
    hotkeyActivate.Unregister()
+
    hotkeyNotify.Unregister()
+
  End Sub
+

          
+
  ' ホットキーの入力メッセージを処理する
+
  Protected Overrides Sub WndProc(ByRef m As Message)
+
    Const WM_HOTKEY As Integer = &H312
+

          
+
    If m.Msg = WM_HOTKEY AndAlso m.LParam = hotkeyActivate.LParam Then
+
      ' フォームをアクティブにする
+
      Me.Activate()
+
    Else If m.Msg = WM_HOTKEY AndAlso m.LParam = hotkeyNotify.LParam Then
+
      ' 通知音を鳴らす
+
      SystemSounds.Beep.Play()
+
    Else
+
      MyBase.WndProc(m)
+
    End If
+
  End Sub
 
End Class
End Class
 
}}
}}
+
#tabpage-end
+

          

programming/tips/create_region_from_bitmap/index.wiki.txt

current previous
1,220 1,84
~
${smdncms:title,画像から不定形リージョンを作成する}
${smdncms:title,画像から複雑な形状のリージョンを作成する}
~
${smdncms:keywords,リージョン,Region,不定形,画像から,Bitmapから}
${smdncms:keywords,リージョン,Region,画像から,Bitmapから}
~
${smdncms:tags,api/.net,lang/cs,lang/vb}
${smdncms:tags,api/.net,lang/vb}
~
${smdncms:document_versions,codelang=cs,codelang=vb}
FormクラスのTransparencyKeyプロパティを使い、その色を用いたBitmapをFormクラスのBackgroundImageプロパティに指定すれば、複雑な形状をしたウィンドウを作ることができますが、これだとWindows 2000やWindows XPなど、半透明ウィンドウをサポートしたWindows上でしか透過しなくなってしまいます。 そこで、画像からリージョンを作成し、それをFormクラスのRegionプロパティに割り当てることで他のWindowsでも動作するようにします。
-
#googleadunit
-

          
-
あらかじめ適当な色を透過色と定めておいた適当な画像を用意しておき、Formの読み込み時に画像を読み込んでリージョンを作成、表示するということで、複雑な形状のウィンドウを作成できます。
-

          
-
#code(vb){{
-
''' <summary>
-
''' イメージからリージョンを作成する。
-
''' </summary>
-
Private Function CreateRegionFromImage(ByVal sourceImage As Bitmap, ByVal transparencyColor As Color) As Region
-

          
-
  ' 画像からリージョンを作成する
-
  Dim imageBound As New Rectangle(0, 0, sourceImage.Width, sourceImage.Height)
-

          
-
  Dim imageRegion As New Region
-

          
-
  ' リージョンを空領域にする
-
  imageRegion.MakeEmpty()
-

          
-
  ' イメージを走査する
-
  For y As Integer = imageBound.Y To imageBound.Height - 1
-

          
-
    Dim opaqueBound As Rectangle = New Rectangle(0, y, 0, 1)
-
    Dim isTransparentLast As Boolean = False
-
    Dim isTransparentCurr As Boolean = False
-

          
-
    For x As Integer = imageBound.X To imageBound.Width - 1
-

          
-
      ' 色を取得
-
      Dim pixelColor As Color = sourceImage.GetPixel(x, y)
-

          
-
      ' 各コンポーネントが透過色と等しい場合、透過させる
-
      isTransparentCurr = (transparencyColor.R = pixelColor.R) And (transparencyColor.G = pixelColor.G) And (transparencyColor.B = pixelColor.B)
-

          
-
      ' 現在の点を透過させる場合
-
      If isTransparentCurr Then
-

          
-
        ' 直前の点を透過させるか
-
        If isTransparentLast Then
 

        

        
~
FormクラスのTransparencyKeyプロパティを使い、その色を透過色として用いたBitmapをFormクラスのBackgroundImageプロパティに指定すれば、カラーキーを使った複雑な形状のウィンドウを作ることができる。 一方、不定形のリージョン(&msdn(netfx,type,System.Drawing.dll,System.Drawing.Region){Region};)を作成し、FormクラスのRegionプロパティに指定することでも、リージョンに合わせた形状のウィンドウを作ることができる。
          ' 透明領域のまま
 

        

        
~
ただし、&msdn(netfx,type,System.Drawing.dll,System.Drawing.Region){Regionクラス};や&msdn(netfx,type,System.Drawing.dll,System.Drawing.Bitmap){Bitmapクラス};には画像からリージョンを作成するメンバが用意されていないため、自前で実装する必要がある。
        Else
 

        

        
~
以下のコードでは、αチャンネルを含む画像(透過PNG)を読み込み、α値が25%未満の部分を透明とみなしてリージョンを作成している。 また作成したリージョンをFormに割り当て、画像の半透明部分に合わせた形状のウィンドウとなるようにしている。 この例ではαチャンネルをもとにリージョンを作成しているが、カラーキーを使うように実装する事も可能。
          ' 透明領域になった 
-
          If 0 < opaqueBound.Width Then imageRegion.Union(opaqueBound)
 

        

        
~
#googleadunit
        End If
-

          
-
      Else
-

          
-
        ' 直前の点を透過させるか
-
        If isTransparentLast Then
-

          
-
          ' 不透明領域になった
-
          opaqueBound.X = x
-
          opaqueBound.Width = 1;
-

          
-
        Else
-

          
-
          ' 不透明領域のまま
-
          opaqueBound.Width = x - opaqueBound.X + 1
 

        

        
+
*実装
+
#tabpage(C#)
+
#code(cs){{
+
using System;
+
using System.Drawing;
+
using System.Drawing.Imaging;
+
using System.Windows.Forms;
+

          
+
public class Form1 : Form {
+
  public static void Main()
+
  {
+
    Application.Run(new Form1());
+
  }
+

          
+
  protected override void OnLoad(EventArgs e)
+
  {
+
    var image = new Bitmap("region.png");
+

          
+
    // ウィンドウを枠線なしにする
+
    this.FormBorderStyle = FormBorderStyle.None;
+

          
+
    // 背景に読み込んだイメージを割り当てる
+
    this.BackgroundImage = image;
+

          
+
    // 読み込んだイメージから作成したリージョンを割り当てる
+
    this.Region = CreateRegionFromImage(image);
+
  }
+

          
+
  // 画像のαチャンネルを元にリージョンを作成する
+
  private static Region CreateRegionFromImage(Bitmap image)
+
  {
+
    var region = new Region();
+

          
+
    // リージョンを一旦空領域にする
+
    region.MakeEmpty();
+

          
+
    BitmapData locked = null;
+

          
+
    // 画像の各ピクセルを読み取り、α値が25%(0x40)未満の部分を透明とみなしてリージョンとする
+
    try {
+
      locked = image.LockBits(new Rectangle(0, 0, image.Width, image.Height),
+
                              ImageLockMode.ReadOnly,
+
                              PixelFormat.Format32bppArgb);
+

          
+
      unsafe {
+
        for (var y = 0; y < image.Height; y++) {
+
          var opaque = new Rectangle(0, y, 0, 1); // 連続する不透明ピクセル列の領域
+
          var transparentPrev = false;
+
          var transparentCurr = false;
+
          var pixel = (byte*)locked.Scan0 + y * locked.Stride;
+

          
+
          for (var x = 0; x < image.Width; x++) {
+
            // このピクセルは透明かどうか
+
            transparentCurr = pixel[3] < 0x40;
+
            //transparentCurr = image.GetPixel(x, y).A < 0x40;
+

          
+
            if (transparentCurr) {
+
              if (transparentPrev) {
+
                // 透明領域のまま
+
              }
+
              else {
+
                // このピクセルで透明領域に変わった
+
                if (0 < opaque.Width)
+
                  // これまでの不透明ピクセル列の領域をリージョンに結合
+
                  region.Union(opaque);
+
              }
+
            }
+
            else {
+
              if (transparentPrev) {
+
                // このピクセルで不透明領域に変わった
+
                // 不透明ピクセル列の開始点を初期化
+
                opaque.X = x;
+
                opaque.Width = 1;
+
              }
+
              else {
+
                // 不透明領域のまま
+
                // 不透明ピクセル列の幅を更新
+
                opaque.Width = x - opaque.X + 1;
+
              }
+
            }
+

          
+
            // 次のピクセルへ進む
+
            pixel += 4;
+

          
+
            // 直前のピクセルの透明/不透明の値を保存
+
            transparentPrev = transparentCurr;
+
          }
+

          
+
          // 最後のピクセルが不透明領域だった場合
+
          if (!transparentPrev && !transparentCurr)
+
            // これまでの不透明ピクセル列の領域をリージョンに結合
+
            region.Union(opaque);
+
        }
+
      }
+

          
+
      return region;
+
    }
+
    finally {
+
      if (locked != null)
+
        image.UnlockBits(locked);
+
    }
+
  }
+
}
+
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Drawing
+
Imports System.Drawing.Imaging
+
Imports System.Runtime.InteropServices
+
Imports System.Windows.Forms
+

          
+
Public Class Form1
+
  Inherits Form
+

          
+
  Public Shared Sub Main()
+
    Application.Run(New Form1())
+
  End Sub
+

          
+
  Protected Overrides Sub OnLoad(ByVal e As EventArgs)
+
    Dim image As New Bitmap("region.png")
+

          
+
    ' ウィンドウを枠線なしにする
+
    Me.FormBorderStyle = FormBorderStyle.None
+

          
+
    ' 背景に読み込んだイメージを割り当てる
+
    Me.BackgroundImage = image
+

          
+
    ' 読み込んだイメージから作成したリージョンを割り当てる
+
    Me.Region = CreateRegionFromImage(image)
+
  End Sub
+

          
+
  ' 画像のαチャンネルを元にリージョンを作成する
+
  Private Shared Function CreateRegionFromImage(ByVal image As Bitmap) As Region
+
    Dim region As New Region()
+

          
+
    ' リージョンを一旦空領域にする
+
    region.MakeEmpty()
+

          
+
    Dim locked As BitmapData = Nothing
+

          
+
    ' 画像の各ピクセルを読み取り、α値が25%(0x40)未満の部分を透明とみなしてリージョンとする
+
    Try
+
      locked = image.LockBits(New Rectangle(0, 0, image.Width, image.Height), _
+
                              ImageLockMode.ReadOnly, _
+
                              PixelFormat.Format32bppArgb)
+

          
+
      For y As Integer = 0 To image.Height - 1
+
        Dim opaque As New Rectangle(0, y, 0, 1) ' 連続する不透明ピクセル列の領域
+
        Dim transparentPrev As Boolean = False
+
        Dim transparentCurr As Boolean = false
+

          
+
        For x As Integer = 0 To image.Width - 1
+
          ' このピクセルは透明かどうか
+
          transparentCurr = Marshal.ReadByte(locked.Scan0, y * locked.Stride + x * 4 + 3) < &h40
+
          'transparentCurr = image.GetPixel(x, y).A < &h40
+

          
+
          If transparentCurr Then
+
            If transparentPrev Then
+
              ' 透明領域のまま
+
            Else
+
              ' このピクセルで透明領域に変わった
+
              ' これまでの不透明ピクセル列の領域をリージョンに結合
+
              If 0 < opaque.Width Then region.Union(opaque)
+
            End If
+
          Else
+
            If transparentPrev Then
+
              ' このピクセルで不透明領域に変わった
+
              ' 不透明ピクセル列の開始点を初期化
+
              opaque.X = x
+
              opaque.Width = 1
+
            Else
+
              ' 不透明領域のまま
+
              ' 不透明ピクセル列の幅を更新
+
              opaque.Width = x - opaque.X + 1
+
            End If
+
          End If
+

          
+
          ' 直前のピクセルの透明/不透明の値を保存
+
          transparentPrev = transparentCurr
+

          
+
          ' 次のピクセルへ進む
+
        Next
+

          
+
        ' 最後のピクセルが不透明領域だった場合
+
        If Not transparentPrev AndAlso Not transparentCurr Then
+
          ' これまでの不透明ピクセル列の領域をリージョンに結合
+
          region.Union(opaque)
 
        End If
        End If
+
      Next
 

        

        
~
      Return region
      End If
~
    Finally

          
~
      If locked IsNot Nothing Then image.UnlockBits(locked)
      ' 現在の状態を保存
~
    End Try
      isTransparentLast = isTransparentCurr
~
  End Function

          
~
End Class
    Next x
~
}}

          
~
#tabpage-end
    ' 最後の点が不透明領域だった場合
-
    If Not isTransparentLast AndAlso Not isTransparentCurr Then imageRegion.Union(opaqueBound)
 

        

        
~
*実行例
  Next y
+
使用した画像。
+
#image(region.png,使用した画像)
 

        

        
~
上記の画像を使用した実行結果。
  Return imageRegion
+
#image(region-result.png,実行結果)
 

        

        
-
End Function
-
}}

programming/tips/check_ngchar/index.wiki.txt

current previous
1,143 1,84
 
${smdncms:title,ダメ文字を含むかどうかチェックする}
${smdncms:title,ダメ文字を含むかどうかチェックする}
 
${smdncms:keywords,ダメ文字,ソ系,ポ系}
${smdncms:keywords,ダメ文字,ソ系,ポ系}
~
${smdncms:tags,api/.net,lang/c#,lang/vb}
${smdncms:tags,lang/c#}
~
${smdncms:document_versions,codelang=cs,codelang=vb}
文字列を一文字ずつチェックして、いわゆる「ダメ文字」が含まれていないかどうか調べる。 文字列をバイト表現に変換し、2バイト目以降に0x5c(バックスラッシュ記号)を含む「ソ系」ダメ文字、または0x7c(パイプ記号)を含む「ポ系」ダメ文字かどうか判断する。
+

          
+
文字列を一文字ずつチェックして、いわゆる「ダメ文字」が含まれていないかどうか調べる。 文字列中の文字を1文字ずつバイト表現に変換し、変換した2バイト目以降をチェックして、0x5c(バックスラッシュ記号)なら「ソ系」ダメ文字、0x7c(パイプ記号)なら「ポ系」ダメ文字と判断する。
 

        

        
 
-関連するページ
-関連するページ
 
--[[programming/tips/shiftjis_eucjp]]
--[[programming/tips/shiftjis_eucjp]]
+
--[[programming/netfx/conversion/0_basetype]]
+
--[[programming/netfx/string/2_comparison]]
+
-参考
+
--[[Shift_JIS - Wikipedia:http://ja.wikipedia.org/wiki/Shift_JIS]]
 

        

        
~
#googleadunit
#code(cs){{
-
static bool ContainsNGByte(string text, Encoding encoding)
-
{
-
  for (int index = 0; index < text.Length; index++)
-
  {
-
    string chr = text.Substring(index, 1);
-

          
-
    byte[] bytes = encoding.GetBytes(chr);
-

          
-
    for (int i = 1; i < bytes.Length; i++)
-
    {
-
      // 「ソ系」ダメ文字(バックスラッシュ0x5cを含む)かどうか
-
      if (bytes[i] == 0x5c)
-
      {
-
        Console.WriteLine("{0}は「ソ系」ダメ文字です", chr);
 

        

        
~
#tabpage(C#)
        return true;
~
#code(cs){{
      }
+
using System;
+
using System.Text;
 

        

        
~
class Sample {
      // 「ポ系」ダメ文字(パイプ0x7cを含む)かどうか
~
  public static void Main()
      if (bytes[i] == 0x7c)
~
  {
      {
~
    // Shift-JISに変換した場合にダメ文字が含まれるかチェックする
        Console.WriteLine("{0}は「ポ系」ダメ文字です", chr);
+
    var shiftjis = Encoding.GetEncoding(932);
 

        

        
~
    foreach (var str in new[] {
        return true;
~
      // テストする文字列
      }
+
      "ソフトウェア",
+
      "シンポジウム",
+
      "六十年",
+
      "竹取物語",
+
      "ダメ文字"
+
    }) {
+
      Console.Write("{0} → ", str);
+

          
+
      // 文字列中のダメ文字のインデックスを取得する
+
      var posNGChar = IndexOfShellSpecialChar(str, shiftjis);
+

          
+
      if (posNGChar == -1)
+
        Console.WriteLine("ダメ文字は含まれていません");
+
      else
+
        Console.WriteLine("'{0}'はダメ文字です", str[posNGChar]);
 
    }
    }
 
  }
  }
 

        

        
~
  private static int IndexOfShellSpecialChar(string text, Encoding encoding)
  return false;
-
}
-
}}
-

          
-
#code(cs,上記メソッドの使用例){{
-
static void Main(string[] args)
-
{
-
  foreach (string str in new string[] { "ソフトウェア", "シンポジウム", "六十年", "竹取物語", "ダメ文字" })
 
  {
  {
~
    var chars = text.ToCharArray(); // 文字列をchar配列に変換
    Console.WriteLine("チェック対象: {0}", str);
~
    var buffer = new byte[4]; // 任意の1文字のバイト表現を格納するバッファ

          
~
    var encoder = encoding.GetEncoder();
    // Shift-JISの文字列としてチェックする
~

          
    if (ContainsNGByte(str, Encoding.GetEncoding(932)))
~
    encoder.Reset();
    {
~

          
      Console.WriteLine("  ダメ文字が含まれています");
~
    // 1文字ずつチェックする
    }
~
    for (var index = 0; index < chars.Length; index++) {
    else
~
      // 1文字をエンコードしてバイト列を取得する
    {
~
      var len = encoder.GetBytes(chars, index, 1, buffer, 0, false);
      Console.WriteLine("  ダメ文字は含まれていません");
+

          
+
      // エンコードした文字の2バイト目以降をチェックする
+
      for (var i = 1; i < len; i++) {
+
        if (buffer[i] == 0x5c)
+
          // バックスラッシュ 0x5cの場合 (「ソ系」ダメ文字)
+
          return index;
+
        else if (buffer[i] == 0x7c)
+
          // パイプ 0x7cの場合 (「ポ系」ダメ文字)
+
          return index;
+
      }
 
    }
    }
 

        

        
~
    return -1; // ダメ文字は含まれていない
    Console.WriteLine();
 
  }
  }
 
}
}
 
}}
}}
+
#tabpage(VB)
+
#code(vb){{
+
Imports System
+
Imports System.Text
+

          
+
Class Sample
+
  Public Shared Sub Main()
+
    ' Shift-JISに変換した場合にダメ文字が含まれるかチェックする
+
    Dim shiftjis As Encoding = Encoding.GetEncoding(932)
+

          
+
    For Each str As String In New String() { _
+
      "ソフトウェア", _
+
      "シンポジウム", _
+
      "六十年", _
+
      "竹取物語", _
+
      "ダメ文字" _
+
    }
+
      Console.Write("{0} → ", str)
 

        

        
~
      ' 文字列中のダメ文字のインデックスを取得する
#prompt(実行結果){{
~
      Dim posNGChar As Integer = IndexOfShellSpecialChar(str, shiftjis)
チェック対象: ソフトウェア
-
ソは「ソ系」ダメ文字です
-
  ダメ文字が含まれています
-

          
-
チェック対象: シンポジウム
-
ポは「ポ系」ダメ文字です
-
  ダメ文字が含まれています
-

          
-
チェック対象: 六十年
-
十は「ソ系」ダメ文字です
-
  ダメ文字が含まれています
-

          
-
チェック対象: 竹取物語
-
竹は「ポ系」ダメ文字です
-
  ダメ文字が含まれています
 

        

        
~
      If posNGChar = -1 Then
チェック対象: ダメ文字
~
        Console.WriteLine("ダメ文字は含まれていません")
  ダメ文字は含まれていません
+
      Else
+
        Console.WriteLine("'{0}'はダメ文字です", str(posNGChar))
+
      End If
+
    Next
+
  End Sub
+

          
+
  Private Shared Function IndexOfShellSpecialChar(ByVal text As String, ByVal encoding As Encoding) As Integer
+
    Dim chars As Char() = text.ToCharArray() ' 文字列をchar配列に変換
+
    Dim buffer(3) As Byte ' 任意の1文字のバイト表現を格納するバッファ
+
    Dim encoder As Encoder = encoding.GetEncoder()
+

          
+
    encoder.Reset()
+

          
+
    ' 1文字ずつチェックする
+
    For index As Integer = 0 To chars.Length - 1
+
      ' 1文字をエンコードしてバイト列を取得する
+
      Dim len As Integer = encoder.GetBytes(chars, index, 1, buffer, 0, False)
+

          
+
      ' エンコードした文字の2バイト目以降をチェックする
+
      For i As Integer = 1 To len - 1
+
        If buffer(i) = &h5C Then
+
          ' バックスラッシュ &h5Cの場合 (「ソ系」ダメ文字)
+
          Return index
+
        Else If buffer(i) = &h7C Then
+
          ' パイプ 0x7cの場合 (「ポ系」ダメ文字)
+
          Return index
+
        End If
+
      Next
+
    Next
+

          
+
    Return -1 ' ダメ文字は含まれていない
+
  End Function
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
ソフトウェア → 'ソ'はダメ文字です
+
シンポジウム → 'ポ'はダメ文字です
+
六十年 → '十'はダメ文字です
+
竹取物語 → '竹'はダメ文字です
+
ダメ文字 → ダメ文字は含まれていません
 
}}
}}