VBAからGDI+を使う資料集
inet上の達人の方々から御教示いただいたコードを少しアレンジさせてもらっています(切り貼りとも言います)。
  1. ホーム
  2. Other
  3. getpixelcolor


画面の指定画素の色取得

マウスクリックした画素の色を取得します。他のアプリケーションも対象とします。
デュアルモニター(画面拡大モードのみ)対応です。
視認不可能なUserFormの応用で、画面全体を覆ってマウスイベントを利用しています。不透明度1とは言え、取得したRGBに僅かに影響があるかも しれませんが、実用上問題ないと考えます。


'エクセル自身を含む、表示されているアプリケーション画面等のマウス指定画素の色情報を取得
'別アプリケーションの画面をExcel上で模擬しようと思い、得意の目的と手段の逆転で作成してみた。

'標準モジュール

'☆ Module1
'カーソルを変更しようとトライしたが、エクセル上のカーソルしか変更出来ず断念。

Type POINTAPI
    x As Long
    y As Long
End Type

Public Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal uFlags As Long) As Long
Public Declare Sub DragAcceptFiles Lib "Shell32.dll" (ByVal hwnd As Long, ByVal fAccept As Long)
Public Declare Function DragQueryFile Lib "Shell32.dll" Alias "DragQueryFileA" (ByVal hDrop As Long, ByVal UINT As Long, ByVal lpStr As String, ByVal ch As Long) As Long
Public Declare Sub DragFinish Lib "Shell32.dll" (ByVal hDrop As Long)
Public Declare Function DragQueryPoint Lib "Shell32.dll" (ByVal hDrop As Long, lpPoint As POINTAPI) As Long
Public Declare Function ClientToScreen Lib "user32.dll" (ByVal hwnd As Long, lpPoint As POINTAPI) As Long
'Windowを半透明化する
Public Declare Function SetLayeredWindowAttributes Lib "user32" (ByVal hwnd&, ByVal crKey As Long, ByVal bAlpha As Long, ByVal dwFlags As Long) As Long
Public Declare Function GetDC Lib "user32.dll" (ByVal hwnd As Long) As Long
Public Declare Function ReleaseDC Lib "user32.dll" (ByVal hwnd As Long, ByVal hdc As Long) As Long
Public Declare Function GetCursorPos Lib "user32.dll" (lpPoint As POINTAPI) As Long
Public Declare Function GetPixel Lib "gdi32.dll" _
        (ByVal hdc As Long, ByVal nXPos As Long, ByVal nYPos As Long) As Long

Public Const LWA_COLORKEY = 1
Public Const LWA_ALPHA = 2              
Public Const SM_CXSCREEN As Long = 0
Public Const SM_CYSCREEN As Long = 1
Public Const GWL_STYLE = -16
Public Const GWL_EXSTYLE = -20
Public Const WS_CAPTION = &HC00000 'タイトルバーを持つウィンドウ
Public Const WS_SYSMENU = &H80000  'タイトルバー上にウィンドウメニューボックスを持つウィンドウ
Public Const WS_MINIMIZEBOX = &H20000 '最小化ボタンを持つウィンドウ
Public Const WS_MAXIMIZEBOX = &H10000  '最大化ボタンを持つウィンドウ
Public Const WS_EX_DLGMODALFRAME = &H1&    '二重の境界線を持つウィンドウ
Public Const HWND_TOPMOST = -1&   '常に手前に表示
Public Const HWND_TOP = 0         '手前に表示
Public Const SWP_FRAMECHANGED = &H20
Public Const WM_DROPFILES = &H233
Public Const GWL_WNDPROC = -4
Public Const WS_EX_TOOLWINDOW = &H80
Public Const WS_EX_LAYERED = &H80000
Public Const WM_KEYDOWN = &H100

'スクリーン座標とエクセルワークシート座標の変換
'出典:http://home.att.ne.jp/zeta/gen/excel/c04p06.htm
Public Const DPI As Long = 96
Public Const PPI As Long = 72

Public IsHooked      As Boolean
Public lpPrevWndProc As Long
Public m_hwnd         As Long

'******** 視認不能なUserformを表示し、ディスプレイ全体を覆い、クリックしたピクセルの色を取得 *******
Sub Main()
  Application.StatusBar = "カーソル位置の色を取得します。任意のキー押下で復帰します。"
  UserForm1.Show
  Application.StatusBar = False
End Sub


'☆ dualMonitorModule
'海外のサイトを参考にしましたが、既にリンク切れなので、出典は明示いたしません。
'In a module
Public Const MONITORINFOF_PRIMARY = &H1
Public Const MONITOR_DEFAULTTONEAREST = &H2
Public Const MONITOR_DEFAULTTONULL = &H0
Public Const MONITOR_DEFAULTTOPRIMARY = &H1

Public Type RECT
  Left As Long
  Top As Long
  Right As Long
  Bottom As Long
End Type

Public Type monitorInfo
  cbSize As Long
  rcMonitor As RECT
  rcWork As RECT
  dwFlags As Long
End Type

'Module1で宣言
'Public Type POINT
'  x As Long
'  y As Long
'End Type

Public Declare Function GetMonitorInfo Lib "user32.dll" Alias "GetMonitorInfoA" (ByVal hMonitor As Long, _
  ByRef lpmi As monitorInfo) As Long
Public Declare Function MonitorFromPoint Lib "user32.dll" (ByVal x As Long, ByVal y As Long, ByVal dwFlags _
  As Long) As Long
Public Declare Function MonitorFromRect Lib "user32.dll" (ByRef lprc As RECT, ByVal dwFlags As Long) As Long
Public Declare Function MonitorFromWindow Lib "user32.dll" (ByVal hwnd As Long, ByVal dwFlags As Long) As Long
Public Declare Function EnumDisplayMonitors Lib "user32.dll" (ByVal hdc As Long, ByRef lprcClip As Any, _
  ByVal lpfnEnum As Long, ByVal dwData As Long) As Long
Public Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long

Dim MIs() As monitorInfo
Dim i As Long
 
'Dual Monitorの幅合計と、大きい方の高さを求める
Public Function getMaximumSize() As RECT
  Dim i As Long
  Dim firstMonitor As monitorInfo, secondMonitor As monitorInfo
  Dim flag1 As Boolean, flag2 As Boolean
  Dim totalWidth As Long, commonHeight As Long
  
  i = 0
  ReDim MIs(1 To 1)
    
  'Monitorを列挙するコールバック関数を呼ぶ、結果は配列MIsに戻る
  EnumDisplayMonitors ByVal 0&, ByVal 0&, AddressOf MonitorEnumProc2, ByVal 0&
  
  '配列に取得した列挙結果に対して処理
  For i = LBound(MIs) To UBound(MIs)
    With MIs(i).rcMonitor
      If MIs(i).cbSize > 0 Then

        If CBool(MIs(i).dwFlags) Then
          firstMonitor = MIs(i)
          flag1 = True
        Else
          secondMonitor = MIs(i)
          flag2 = True
        End If
      End If
    End With
    If flag1 And flag2 Then Exit For
  Next i
  
  With getMaximumSize
    .Left = 0
    .Top = 0
    .Right = firstMonitor.rcMonitor.Right - firstMonitor.rcMonitor.Left + secondMonitor.rcMonitor.Right - secondMonitor.rcMonitor.Left
    If firstMonitor.rcMonitor.Bottom - firstMonitor.rcMonitor.Top >= secondMonitor.rcMonitor.Bottom - secondMonitor.rcMonitor.Top Then
      .Bottom = firstMonitor.rcMonitor.Bottom - firstMonitor.rcMonitor.Top
    Else
      .Bottom = secondMonitor.rcMonitor.Bottom - secondMonitor.rcMonitor.Top
    End If
  End With

End Function

'CallBack関数なのでディスプレイの数だけ実行される
Private Function MonitorEnumProc2(ByVal hMonitor As Long, ByVal hdcMonitor As Long, lprcMonitor As RECT, _
  ByVal dwData As Long) As Long
  
  Dim MI As monitorInfo, R As RECT

  'initialize the MONITORINFO structure
  MI.cbSize = Len(MI)
  'Get the monitor information of the specified monitor
  GetMonitorInfo hMonitor, MI
  '実行されるのは2回だけなのに、iが40迄とか加算される?
  'このプロシージャ内で.cbSizeで分岐してもうまく行かない(OKの時もあり)
    i = i + 1
    ReDim Preserve MIs(1 To i)
  'Continue enumeration,Put the result to Array
  MIs(i) = MI
  
  MonitorEnumProc2 = 1
End Function

'☆ UserForm1 Module、コントロールは何も置かない
'Thisworkbook
Dim wbk As Workbook

Private Sub UserForm_Initialize()
  'フォームの不透明度1ならほとんど判別できないが機能する。
  Const opacityRatio As Long = 1

  With Me
    .StartUpPosition = 0
    .BorderStyle = fmBorderStyleNone
    .SpecialEffect = fmSpecialEffectFlat
    'フォームに時刻を名前としてつける
    .Caption = .Caption & Timer()
  End With
  '名前を手がかりとして、ユーザーフォームのハンドルを取得
  m_hwnd = FindWindow("ThunderDFrame", Me.Caption)
  ' フォームのメニュー、最大最小化ボタン等は一切表示しない設定とする
  SetWindowLong m_hwnd, GWL_STYLE, _
              GetWindowLong(m_hwnd, GWL_STYLE) And _
                       Not (WS_SYSMENU Or WS_CAPTION Or _
                            WS_MINIMIZEBOX Or WS_MAXIMIZEBOX)
  SetWindowLong m_hwnd, GWL_EXSTYLE, _
              GetWindowLong(m_hwnd, GWL_EXSTYLE) And _
              Not WS_EX_DLGMODALFRAME
  SetWindowLong m_hwnd&, GWL_EXSTYLE, _
            GetWindowLong(m_hwnd&, GWL_EXSTYLE) Or WS_EX_LAYERED
  SetLayeredWindowAttributes m_hwnd&, 0, opacityRatio, LWA_ALPHA
  
  Set wbk = ThisWorkbook
              
End Sub

Private Sub UserForm_Activate()
  Dim myLeft As Long, myTop As Long, myWidth As Long, myHeight As Long
  Dim rightBottomCell As Range
  Dim retRECT As RECT
  
  Const myFontSize = 11
   
  '指定範囲を覆うUserFormを表示
  'ほとんど透明なので視認できない
  With Application.ActiveWindow.VisibleRange
    Set rightBottomCell = .Cells(.Rows.Count, .Columns.Count)
  End With

  'Dual Monitorも全体を覆う
  retRECT = getMaximumSize
 
  With retRECT
    myLeft = 0
    myTop = 0
    myWidth = .Right
    myHeight = .Bottom
  End With
  'フォームの表示順(Zオーダー)、サイズ指定
  SetWindowPos m_hwnd, HWND_TOP, myLeft, myTop, myWidth, myHeight, SWP_FRAMECHANGED
'  Hook
errHandle:
End Sub
 
'やめたくなったときは何かキーを押すと停止
Private Sub UserForm_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)
  Unload Me
End Sub

Private Sub UserForm_MouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal x As Single, ByVal y As Single)
  Dim hdc As Long, Color As Long
  Dim pt As POINTAPI
  Dim R As Byte, G As Byte, B As Byte
  
  Dim myCell As Range
  
  Call GetCursorPos(pt)
  hdc = GetDC(0)
  Color = GetPixel(hdc, pt.x, pt.y)
  Call ReleaseDC(0, hdc)

  R = Color And &HFF
  G = Color \ &H100 And &HFF
  B = Color \ &H10000 And &HFF
 
  'シートに着色文字と塗りつぶしセルを作成、RGBをセルに書き出す
  With wbk.Worksheets(1)
    Set myCell = .Range("A" & .Rows.Count).End(xlUp)
    If myCell.Value <> "" Then Set myCell = myCell.Offset(1, 0)
    myCell.Value = "色"
    myCell.Font.Color = Color
    myCell.Offset(0, 1).Interior.Color = Color
    myCell.Offset(0, 2).Value = R & ", " & G & ", " & B
  End With
  
End Sub