Visual Basic 中学校 掲示板 投稿内容
タグのない投稿を抽出 統計 RSS

Visual Basic 中学校 > 投稿一覧 >

Windows解像度の回転について 解決済み

タグの編集...

投稿者 モル   (社会人)   投稿日時 2016/9/12 11:19:21
お久しぶりです、モルです。
早速ですが「Windowsを使用しているパソコンで、解像度(モニタ?)の向きをVB2010プログラムから行いたい」という質問です。

経緯として業務で使用しているパソコンが縦置のモニタでWindowsの向きで90度回転しております。
しかしパソコンの電源を付けたまま、モニタの電源を切って暫く(1日~)してから付けると、解像度も回転向きも初期値(1024/768 回転0度)に戻ってしまうのをどうにかできないか?と思った次第です。

これはモニタやパソコンにより初期値に戻らないタイプもあるのですが、今回のような時にプログラムから回転向きと解像度を指定出来れば汎用性が効くかな?と思い質問させて頂きました。


WindowsCEであれば
https://msdn.microsoft.com/ja-jp/library/microsoft.windowsce.forms.screenorientation(v=vs.90).aspx
のように「ScreenOrientation」を使ったり、APIのChangeDisplaySettingsを利用してDEVMODE構造体のdmDisplayOrientationを使えば出来るのでは?という事が書いてありましたが、VB2010で使えるのかキーワードを元にサンプルなど探してみましたが行えませんでした。

解決への方法をご教授願えればと思っております。
よろしくお願い申し上げます。

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2016/9/12 11:57:59
取得するだけならば、
  SystemInformation.ScreenOrientation
  Screen.PrimaryScreen.Bounds
で取得できます。設定は出来ませんが。

マルチモニタの場合、Screen.AllScreens というのがありますが、
解像度は取れますが向きについては取得できません。



設定まで必要なら、EnumDisplaySettings(Ex) API で、
DEVMODE 構造体を列挙し、それぞれの
  dmDisplayOrientation
  dmPelsHeight
  dmPelsWidth
を取得した上で、これらを新しい値に書き変えてから
ChangeDisplaySettings(Ex) API に渡してみては如何でしょう。
(試してはいませんが)

投稿者 モル   (社会人)   投稿日時 2016/9/13 10:39:28
魔界の仮面弁士様、ありがとうございます。
色々検索してVB6の環境なら以下のサイトのサンプルを参考に解像度の取得/設定は行えました。
http://www.entiapage.com/souko/module/sample_display_vb.html

但し「dmDisplayOrientation」をDEVMODE構造体に追加して、設定時に「dmDisplayOrientation」に90°指定(値1)を行って実行してみましたが、解像度が指定したものに変わるだけで回転までは行いませんでした。

上記参考サイトのサンプルを元にVB2010に直すにも、まずはこのサンプル(VB6)で行えるかどうかではありますが、どのように「dmDisplayOrientation」を正しく与えれば宜しいでしょうか?

以下に上記URLのサンプルプログラムに「dmDisplayOrientation」を追加したものを記載しますので、ご教授願えればと思います。
よろしくお願い申し上げます。

投稿者 モル   (社会人)   投稿日時 2016/9/13 10:43:36
Displayクラス
Private OldMode As DEVMODE
Private ModeList(1000) As DEVMODE
Private DisplayModeNumber As Integer
'ディスプレイの幅を取得 
'パラメータ    n As Integer 
'戻り値        n番目の設定の幅 
Public Function GetWidth(Optional n As Integer = -1) As Integer
    If n >= 0 And n < GetDisplayModeNumber Then
        GetWidth = ModeList(n).dmPelsWidth
    ElseIf n = -1 Then
        Dim DesktopDC As Long
        Dim ret As Long
        DesktopDC = GetDC(0)
        GetWidth = GetDeviceCaps(DesktopDC, HORZRES)
        ret = ReleaseDC(0, DesktopDC)
    End If
End Function
'ディスプレイの高さを取得 
'パラメータ    n As Integer 
'戻り値        n番目の設定の高さ 
Public Function GetHeight(Optional n As Integer = -1) As Integer
    If n >= 0 And n < GetDisplayModeNumber Then
        GetHeight = ModeList(n).dmPelsHeight
    ElseIf n = -1 Then
        Dim DesktopDC As Long
        Dim ret As Long
        DesktopDC = GetDC(0)
        GetHeight = GetDeviceCaps(DesktopDC, VERTRES)
        ret = ReleaseDC(0, DesktopDC)
    End If
End Function
'ディスプレイの色深度を取得 
'パラメータ    n As Integer 
'戻り値        n番目の設定の色深度 
Public Function GetBit(Optional n As Integer = -1) As Integer
    If n >= 0 And n < GetDisplayModeNumber Then
        GetBit = ModeList(n).dmBitsPerPel
    ElseIf n = -1 Then
        Dim DesktopDC As Long
        Dim ret As Long
        DesktopDC = GetDC(0)
        GetBit = GetDeviceCaps(DesktopDC, BITSPIXEL)
        ret = ReleaseDC(0, DesktopDC)
    End If
End Function
'変更できるディスプレイモードを列挙 
'パラメータ    なし 
'戻り値        変更できるディスプレイモードの数 
Public Function GetDisplayMode() As Integer
    DisplayModeNumber = 0
    Do
        If (EnumDisplaySettings(vbNullString, DisplayModeNumber, ModeList(DisplayModeNumber))) = 0 Then Exit Do
        If CheckNowSetting(ModeList(DisplayModeNumber)) Then
            OldMode = ModeList(DisplayModeNumber)
        End If
        DisplayModeNumber = DisplayModeNumber + 1
    Loop
    GetDisplayMode = DisplayModeNumber
End Function
'ディスプレイモードを変更 
'パラメータ    n As Integer , w As Integer , h As Integer , b As Integer 
'n番目の設定に変更する。 
'戻り値        成功すればDISP_CHANGE_SUCCESSFULが帰る 
Public Function ChangeDisplayMode(Optional n As Integer = -1, Optional w As IntegerOptional h As IntegerOptional b As IntegerAs Long
    If n >= 0 And n < GetDisplayModeNumber Then
        
        'ここで強制的に90°回転を指定してみる 
        ModeList(n).dmDisplayOrientation = 1 '0:0° 1:90° 2:180°4:270° 
        
        If ChangeDisplaySettings(ModeList(n), CDS_TEST) = DISP_CHANGE_SUCCESSFUL Then
            ChangeDisplayMode = ChangeDisplaySettings(ModeList(n), 0)
        Else
            ChangeDisplayMode = 0
        End If
    ElseIf w = 0 And h = 0 And b = 0 Then
        If CheckNowSetting(OldMode) Then
            ChangeDisplayMode = 0
        Else
            If ChangeDisplaySettings(OldMode, CDS_TEST) = DISP_CHANGE_SUCCESSFUL Then
                ChangeDisplayMode = ChangeDisplaySettings(OldMode, 0)
            Else
                ChangeDisplayMode = 0
            End If
        End If
    Else
        Dim i As Integer
        For i = 0 To GetDisplayModeNumber
            If GetWidth(i) = w And GetHeight(i) = h And GetBit(i) = b Then
                ChangeDisplayMode = ChangeDisplayMode(i)
                Exit Function
            End If
        Next i
        ChangeDisplayMode = 0
    End If
End Function
'変更できるディスプレイモードの数を列挙 
'パラメータ    なし 
'戻り値        変更できるディスプレイモードの数 
Public Function GetDisplayModeNumber() As Integer
    GetDisplayModeNumber = DisplayModeNumber
End Function
'内部でのみ使用 
Private Function CheckNowSetting(d As DEVMODE) As Boolean
    CheckNowSetting = (d.dmBitsPerPel = GetBit()) And _
                      (d.dmPelsWidth = GetWidth()) And _
                      (d.dmPelsHeight = GetHeight())
End Function
'初期化処理 
Private Sub Class_Initialize()
    GetDisplayMode
End Sub
'破棄されるときに呼び出される 
Private Sub Class_Terminate()
    ChangeDisplayMode
End Sub



投稿者 (削除されました)   ()   投稿日時 2016/9/13 10:44:47
(削除されました)

投稿者 (削除されました)   ()   投稿日時 2016/9/13 10:45:38
(削除されました)

投稿者 モル   (社会人)   投稿日時 2016/9/13 10:50:02
Module1
'定数宣言 
Public Const HORZRES = 8            '  Horizontal width in pixels 
Public Const VERTRES = 10           '  Vertical width in pixels 
Public Const BITSPIXEL = 12         '  Number of bits per pixel 
Public Const CDS_TEST = &H2
Public Const DISP_CHANGE_SUCCESSFUL = 0
Public Const CCHDEVICENAME = 32
Public Const CCHFORMNAME = 32
'型宣言 
Public Type DEVMODE
        dmDeviceName As String * CCHDEVICENAME
        dmSpecVersion As Integer
        dmDriverVersion As Integer
        dmSize As Integer
        dmDriverExtra As Integer
        dmFields As Long
        dmOrientation As Integer
        dmPaperSize As Integer
        dmPaperLength As Integer
        dmPaperWidth As Integer
        dmScale As Integer
        dmCopies As Integer
        dmDefaultSource As Integer
        dmPrintQuality As Integer
        dmColor As Integer
        dmDuplex As Integer
        dmYResolution As Integer
        dmTTOption As Integer
        dmCollate As Integer
        dmFormName As String * CCHFORMNAME
        dmUnusedPadding As Integer
        dmBitsPerPel As Long
        dmPelsWidth As Long
        dmPelsHeight As Long
        dmDisplayFlags As Long
        dmDisplayFrequency As Long
        dmDisplayOrientation As Integer
End Type
'関数宣言 
Public Declare Function EnumDisplaySettings Lib "user32" Alias "EnumDisplaySettingsA" (ByVal lpszDeviceName As StringByVal iModeNum As Long, lpDevMode As DEVMODE) As Long
Public Declare Function ChangeDisplaySettings Lib "user32" Alias "ChangeDisplaySettingsA" (lpDevMode As DEVMODE, ByVal dwFlags As LongAs Long
Public Declare Function GetDC Lib "user32" (ByVal hwnd As LongAs Long
Public Declare Function GetDeviceCaps Lib "gdi32" (ByVal hdc As LongByVal nIndex As LongAs Long
Public Declare Function ReleaseDC Lib "user32" (ByVal hwnd As LongByVal hdc As LongAs Long 



投稿者 モル   (社会人)   投稿日時 2016/9/13 10:51:52
FormにListBoxとLabelとCommandButtonを1つずつ使用します。
ModuleとClassも1つずつ使用します。

Dim m_Display As Display

Private Sub Command1_Click()
    If List1.ListIndex > 0 Then
        m_Display.ChangeDisplayMode List1.ListIndex
    End If
End Sub

Private Sub Form_Load()
    
    Dim i As Integer
    Set m_Display = New Display
    For i = 0 To m_Display.GetDisplayModeNumber - 1
        List1.AddItem Format$(m_Display.GetWidth(i), "# x ") & _
                      Format$(m_Display.GetHeight(i), "#") & _
                      Format$(m_Display.GetBit(i), " # bit")
    Next i
    Label1.Caption = Format$(m_Display.GetWidth, "現在:# x ") & _
                      Format$(m_Display.GetHeight, "#") & _
                      Format$(m_Display.GetBit, " # bit")
End Sub



投稿者 (削除されました)   ()   投稿日時 2016/9/13 12:06:47
(削除されました)

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2016/9/13 12:11:01
> 但し「dmDisplayOrientation」をDEVMODE構造体に追加して、
構造体の各メンバーは、「位置」と「データサイズ」が
重要なのですから、勝手に最後に追加したりしては駄目ですよ。


今の実装は、DEVMODE 構造体の先頭を 0 バイト目とすると
 40~43 dmFields
 -------------------------
 44~45 dmOrientation
 46~47 dmPaperSize
 48~49 dmPaperLength
 50~51 dmPaperWidth
 52~53 dmScale
 54~55 dmCopies
 56~57 dmDefaultSource
 58~59 dmPrintQuality
 -------------------------
 60~61 dmColor
のような配置になっていますよね。
実はこれ、プリンター制御の際に使う場合の宣言です。

44~59バイト目のエリアは union すなわち『共用体』となっており、
画面制御の場合には、ここが
 40~43 dmFields
 -------------------------
 44~47 dmPosition.x
 48~51 dmPosition.y
 52~55 dmDisplayOrientation
 56~59 dmDisplayFixedOutput
 -------------------------
 60~61 dmColor
という定義に変わります。

本来の定義は下記で確認してみてください。
https://msdn.microsoft.com/ja-jp/library/windows/desktop/dd183565.aspx


ちなみに .NET で『共用体』を宣言するには、「構造体」への属性指定で
StructLayout(LayoutKind.Explicit) と FieldOffset を使うことができます。

しかし、VB6/VBA の場合は、『共用体』を宣言できないので、
「ユーザー定義型」の宣言を、dmDisplayOrientation 側の定義に
書き変える形で対応することになるでしょう。


…手を抜くのであれば、DEVMODE 宣言を書きかえることをせずに
dmCopies を 0 にして、dmScale を 0~3 に変化させることでも
dmDisplayOrientation を変更しているのと同じ結果を得られるのですが、
それだと分かりにくいコードになるので、やはり DEVMODE の定義を
見直すのが望ましいでしょうね。


> どのように「dmDisplayOrientation」を正しく与えれば宜しいでしょうか?

±90度回転させた場合は、dmPelsHeight と dmPelsWidth を交換してください。
180度回転した場合は、縦横サイズはそのままで OK です。


> List1.AddItem Format$(m_Display.GetWidth(i), "# x ") & _
サンプルでは # 書式になっていますが、本来は 0 書式を使うべきですね。

投稿者 魔界の仮面弁士   (社会人)   投稿日時 2016/9/13 12:59:11
提示頂いたサンプルを読んでみたのですが、現在の実装だと、
同じ解像度・色数で、周波数(dmDisplayFrequency)だけが異なる
画面設定が用意されていた場合に、それらを区別できないという問題があります。
dmDisplayFrequency の判定コードも用意しておくことをお奨めします。


ついでに、DEVMODE の dmFields が指し示す値も書いておきます。
(dmFields は、DEVMODE 構造体のどのメンバーを利用しているかを示すフラグです)

これらはビットフラグになりますので、.NET から使う場合は、
列挙型宣言に <Flags()> 属性を付与しておいてください。

Public Enum DevModeFields
 DM_ORIENTATION        = &H00000001  'dmOrientation 
 DM_PAPERSIZE          = &H00000002  'dmPaperSize 
 DM_PAPERLENGTH        = &H00000004  'dmPaperLength 
 DM_PAPERWIDTH         = &H00000008  'dmPaperWidth 
 DM_SCALE              = &H00000010  'dmScale 
 DM_POSITION           = &H00000020  'dmPosition ((WINVER >= &H0500)) 
 DM_NUP                = &H00000040  'dmNup ((WINVER >= &H0500)) 
 DM_DISPLAYORIENTATION = &H00000080  'dmDisplayOrientation ((WINVER >= &H0501)) 
 DM_COPIES             = &H00000100  'dmCopies 
 DM_DEFAULTSOURCE      = &H00000200  'dmDefaultSource 
 DM_PRINTERQUALITY     = &H00000400  'dmPrinterQuality 
 DM_COLOR              = &H00000800  'dmColor 
 DM_DUPLEX             = &H00001000  'dmDuplex 
 DM_YRESOLUTION        = &H00002000  'dmYResolution 
 DM_TTOPTION           = &H00004000  'dmTTOption 
 DM_COLLATE            = &H00008000  'dmCollate 
 DM_FORMNAME           = &H00010000  'dmFormName 
 DM_LOGPIXELS          = &H00020000  'dmLogPixels 
 DM_BITSPERPEL         = &H00040000  'dmBitsPerPel 
 DM_PELSWIDTH          = &H00080000  'dmPelsWidth 
 DM_PELSHEIGHT         = &H00100000  'dmPelsHeight 
 DM_DISPLAYFLAGS       = &H00200000  'dmDisplayFlags 
 DM_DISPLAYFREQUENCY   = &H00400000  'dmDisplayFrequency 
 DM_ICMMETHOD          = &H00800000  'dmICMMethod ((WINVER >= &H0400)) 
 DM_ICMINTENT          = &H01000000  'dmICMintent ((WINVER >= &H0400)) 
 DM_MEDIATYPE          = &H02000000  'dmMediaType ((WINVER >= &H0400)) 
 DM_DITHERTYPE         = &H04000000  'dmDitherType ((WINVER >= &H0400)) 
 DM_PANNINGWIDTH       = &H08000000  'dmPanningWidth ((WINVER >= &H0400)) 
 DM_PANNINGHEIGHT      = &H10000000  'dmPanningHeight ((WINVER >= &H0400)) 
 DM_DISPLAYFIXEDOUTPUT = &H20000000  'dmDisplayFixedOutput ((WINVER >= &H0501)) 
 DM_ICCMODEL           = &H40000000  'dmICCModel 
End Enum


投稿者 モル   (社会人)   投稿日時 2016/9/13 13:57:26
魔界の仮面弁士様、ありがとうございます。

>構造体の各メンバーは、「位置」と「データサイズ」が
重要なのですから、勝手に最後に追加したりしては駄目ですよ。
サンプルがそのまま望む形に近い動作をした為、何がどう働いているのかの確認など後回しにして結果だけ急いでしまいました。ご指摘ありがとうございます。

>dmCopies を 0 にして、dmScale を 0~3
確かにこれで回転動作について確認出来ました。
しかし、ご指摘いただいたようにDEVMODEを教えて頂いたサイトを元に勉強+見直してみたいと思います。

dmDisplayFrequencyも勉強不足でしたので、これから確認して判定コードをどのように扱うのかやってみたいと思います。


現時点で解決とまではいっておりませんが、魔界の仮面弁士様からご指摘いただいた部分を勉強して対応すれば実現可能な感じが致しますので、一度解決扱いにさせていただき、ご教授頂いた所で躓いたりした場合に改めて質問させて頂きたく存じます。

魔界の仮面弁士様、ありがとうございました。