none
表示倍率の違うマルチディスプレイを使用時、マクロで設定したShapeの位置がずれる RRS feed

  • 質問

  • メインディスプレイ:1920×1200、表示倍率125%

    サブディスプレイ:1920×1080、表示倍率100%

    でエクセルがサブディスプレイで開いている場合、

    マクロで対象セルのTopを取得し、画像のTopに設定する処理が正しく動作しないのはエクセルのバグでしょうか。

    解決策はありますでしょうか。

    例)

    ActiveSheet.Shapes("Shape1").Top = Range("A10").Top

    メインディスプレイで実行した場合、セルA10の上辺と画像の上辺が一致する。

    サブディスプレイで実行した場合、画像の開始位置が4ピクセル程上にずれる。
    この差は行が進む程大きくなり、A100の場合、A98よりも上になる。

    2021年3月4日 7:26

すべての返信

  • WalkerShadowさん、こんにちは。

    次のURLのような記事があります。
    https://support.microsoft.com/ja-jp/topic/excel-2010-%E4%BB%A5%E9%99%8D%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%81%A7%E3%82%B7%E3%83%BC%E3%83%88%E3%81%AE%E5%80%8D%E7%8E%87%E3%81%8C-100-%E4%BB%A5%E5%A4%96%E3%81%AE%E3%81%A8%E3%81%8D-activex-%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%AB%E3%81%AE%E4%BD%8D%E7%BD%AE%E3%82%84%E3%82%B5%E3%82%A4%E3%82%BA%E3%82%92-vba-%E3%81%8B%E3%82%89%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B%E3%81%A8%E8%AA%A4%E5%B7%AE%E3%81%8C%E7%94%9F%E3%81%98%E3%82%8B-fe343673-087b-e8f8-92d4-345d64f28591

    こんな記事もありました。↓
    https://excelshogikan.com/tips/tips109.html
    http://www.excel.studio-kazu.jp/kw/20170302214231.html

    「解決策はありますでしょうか。」とのご質問については、残念がら、答えを持っておりません。
    愚考するに、表示倍率に応じて誤差も拡大しているためではないか? と考えると、異なる画面の表示倍率のままでは難しいのではないかと思います。が、どうでしょうか?

    ※【追記】
    小生のパソコン(Windows10_64bit+Excel2019MSO64bit)で試行してみました。まず、100%でShapeを表示したうえで手動で適当な位置へドラッグして移動し、同じパソコンで倍率を変えて「ActiveSheet.Shapes(1).Top = Range("A10").Top」を実行してみましたが、ずれは観察できませんでした。
    【追記の追記;ここから後ろを削除】別のパソコンでやっても、同じでした。倍率を変えても、ずれはありませんでした。
    モニターが1つだから、画面の表示倍率が大きくなっても、ずれが出ないのでしょう。【追記の追記;ここまで前方を削除】
    上記のURLの記事は、2つのモニターを接続した場合の本件には、あまり参考にならないかもしれませんね。

    メインディスプレイとサブディスプレイで横のドット数は同じですが、縦のドット数が異なるので、ディスプレイのスペックの違いが影響しているのではないかと愚考します。ただ、両方とも倍率が同じ100%の時は、ずれがないのですよね。また、両方とも倍率を125%にすると、メインディスプレイではずれが出ないけれども、サブディスプレイではずれが生じるのですよね。
    大変興味深いのですが、残念ながら小生の環境では2つの異なるスペックのディスプレイを接続して運用する環境がありませんので、試してみることができません。大変申し訳ありませんが、手も足も出ません。すみません。

    良く分からないのに厚かましい話ですが、両方のディスプレイの解像度を[スタート]>[設定]>[システム]のディスプレイの解像度の設定メニューから同じ解像度にすることができるのであれば、同じ解像度にすれば、改善するかもしれないと、思ったりしましたが、どうでしょうか。

    【追記の追記;2021/3/8】本日、再度試行したところ、ディスプレイが1つでも倍率を125%にして実行するとずれが見られましたので、追記で記載した間違った記述を削除する旨、追記の追記として記載しました。何か勘違いがあったようです。間違った内容を記載して、誠に申し訳ありません。

    2021年3月4日 7:44
  • WalkerShadowさん、こんにちは。

    マルチディスプレイ環境を作って、試行してみて、WalkerShadowさんのご指摘の通りの現象を確認しました。
    ActiveSheet.Shapes("Shape1").Top = Range("A10").Top」を実行する際に、メインディスプレイの仕様を基に描画位置を決定して描画するところ、ワークシート画面の所在するディスプレイの仕様が異なるためにずれが生じてしまうのであろう、と推測します。ワークシート画面の所在するディスプレイの仕様を取得して描画するような処理に変更できればよろしいのだろうと思いますが、小生にはそのやり方が分かりません。
    なお、マルチディスプレイをお使いの方なら先刻ご承知で言わずもがなの余計な話かとは存じますが、表示方法は「拡張」、ディスプレイ設定は「表示画面を拡張する」です。

    ※【追記】
    これは「不具合」と言えば「不具合」なのでしょうけれど、「不具合」というよりは、マルチディスプレイはしばらく前から導入された技術と理解しますので、
    1.マルチディスプレイにVBAは完璧には対応できていない。
    2.制限事項として、メインディスプレイと表示倍率の異なるサブディスプレイ上にワークシートが所在する場合に実行すると位置がずれるので、そのような使い方はしない様にすること。
    という様な説明振りになるのだろうと思います。(なお、小生はマイクロソフトの人間ではありません。)
    2021年3月5日 2:59
  • 回答ありがとうございます。

    わざわざ環境まで作って再現して頂いてありがとうございます。

    こちらのマクロについて、顧客が更に取引先へ配布する物の為、

    自分の端末で動けばよいという事ではないのが悩ましいです。

    表示倍率の異なるマルチディスプレイでの使用禁止するしかないのではと、

    現状では考えていますが、プログラム側での対策が可能かどうか、

    不可能であれば、マイクロソフトからエクセルの不具合なので対応不可能という回答が欲しい所です。

    2021年3月5日 3:16
  • ※【追記】
    その後、試行してみて、若干小生が誤解していた点もあったと判明したので、記載していた返信記事を削除しました。それはさておき、試行の結果、メインだろうとサブだろうと、倍率125%のディスプレイ上にワークブックがあるときにShape1の移動を実行すると、ずれが生じることが分かりました。100%表示のディスプレイ上にワークブックが表示されている場合は、ずれは発生しません。

    ということは、100%表示のディスプレイにワークブックを移動させてから、Shape1の移動を実行し、その後で、ワークブックを元のディスプレイに戻す、という処理を行えば、ずれを解決できるのではないかと思い、次のようなコードを作成して実行してみました。小生の環境では、狙い通りずれが無くなりました。この場合、100%表示のディスプレイがウィンドウの左側部分、125%表示のディスプレイがウィンドウの右側部分をカバーしています。この点については、間違いのないようにご注意ください。姑息な回避策かもしれませんが、とりあえずは何とかなります。お試しいただければと思います。

    Sub ブックをウィンドウ最上最左に移動してShape1を移動し元に戻す()
    '最上最左のウインドウは100%表示のディスプレイに表示されている。 Dim lastTop As Long Dim lastLeft As Long ThisWorkbook.Activate If Windows(ActiveWorkbook.Name).WindowState = xlNormal Then With Windows(ActiveWorkbook.Name) lastTop = .Top lastLeft = .Left .Top = 1 '--- 左側のディスプレイが倍率125%の場合は、100%のディスプレイの最上位置の数値にする .Left = 1 '--- 左側のディスプレイが倍率125%の場合は、100%のディスプレイの最左位置の数値にする ActiveSheet.Shapes(1).Top = ActiveSheet.Range("B100").Top .Top = lastTop .Left = lastLeft End With DoEvents Else End If End Sub
    2021年3月5日 5:12
  • 直前の返信で、「メインだろうとサブだろうと、倍率125%のディスプレイ上にワークブックがあるときにShape1の移動を実行すると、ずれが生じることが分かりました。」と記しましたが、何しろマルチディスプレイには不慣れなものですから、試行の検証が不十分だったかもしれないと思い、重ねて試行してみました。結果は、直前の返信に記した結論に変わりは有りませんでした。

    今回の試行は、ノートパソコン(以下「NPC」と記す。)とTVディスプレイの2つを使い、NPCがメインかサブか、左位置か右位置か、100%か125%か、の合計8通りで試行してみました。結果は、どの場合も、100%のディスプレイ上でShapeの移動をすればドンピシャですが、125%のディスプレイではずれる、というものでした。

    なお、125%以外の倍率については確認しておりません。ご容赦ください。以上、ご報告まで。

    2021年3月6日 16:45
  • > 不可能であれば、マイクロソフトからエクセルの不具合なので対応不可能という回答が欲しい所です。

    それは Microsoft に聞かないと結論でない話では?
    我々が見つけられていないだけで方法があるのか、Microsoft 自身は仕様と認識しているのかなど、第三者にはわかりませんから。

    エビデンス(書面)をもらう意味でも、Microsoft の有償サポートを利用する案件ですね。

    2021年3月6日 22:43
  • 本日、ディスプレイ1つだけのパソコンでshapeの位置指定をしてみたところ、ディスプレイの拡大率125%でずれが見られました。このため、3月4日の小生のご返事を訂正させていただきました。
    Windows10+Excel2019 と Windows10+Excel 2016  とで確認して同じ結果となりました。3月4日に何を確認したのだろうと、我ながら情けない気がしましたが、間違いは間違い。間違った内容をご返事して申し訳ありませんでした。

    ところで、そうなると、ディスプレイの拡大率を取得して拡大率を100%にしてから位置指定を行い、その後拡大率を元に戻す、というコードができないのだろうか、と思います。

    3月5日にお示ししたコードは、100%表示のディスプレイがあることを前提としたものですが、ディスプレイ1つでもずれてしまうとなると、当面可能な回避策としては、ディスプレイの拡大率を変更するしかないように思います。

    前後して、ここ2日ばかり、ネット上で、拡大率を取得したり設定するAPIを探しましたが、残念ながら見つけられませんでした。また、代替策としてDPI値を取得するコードも探して、2つばかり試作してみましたが、残念ながら、取得されたのは物理的なDPI値のようで、ディスプレイの拡大率を変更しても値は変わりませんでした。

    どなたか、ディスプレイの拡大率を取得したり変更するAPIや何らかの方策をご存知の方がいらっしゃいましたら、是非、教えていただければと存じます。よろしくお願いします。

    なお、いろいろ拡大率の記事の検索をしている中で、ハイスペックのディスプレイの場合、当初から125%表示に自動設定されている場合があるという様な記事がありました。そうなると、そのような場合、当然VBAのShapeの位置指定を実行するとずれが生じる、ということになってしまいます。マイクロソフトの関係者の方もこのウェブサイトをご覧になることがあるのだろうと思いますのでお願いします。マイクロソフトさんには、是非、ずれが生じない様に改善策を講じていただきたいものです。よろしくお願いします。

    2021年3月8日 9:59
  • みなさん 回答ありがとうございます。
    拡大率を設定するというAPIがあったとしても、
    一つShapeを動かすたびにガクガク画面が変わっていてはちょっとアレなので、
    使えないのではないかと考えます。

    拡大率の取得はできるならしたい所ではあります。
    GetCursorPosで取った座標を
    ActiveWindow.RangeFromPointに与えた場合、
    DPI差があると正しいセル位置が返って来ない問題もありますので。


    >本日、ディスプレイ1つだけのパソコンでshapeの位置指定をしてみたところ、ディスプレイの拡大率125%でずれが見られました。
    いろいろと試して頂きありがとうございます。
    私の所では
    125%のメインディスプレイではずれがなく、
    100%のサブディスプレイでずれる
    というの状況ですが、そちらでは異なる現象のようで奥が深そうですね。


    >それは Microsoft に聞かないと結論でない話では?
    一般のコミュニティで投稿した所、
    Microsoft エージェントの方からこちらへの誘導を受けたのですが、
    ここにはMicrosoftの方は居られないのでしょうか。
    2021年3月9日 10:10
  • ディスプレイのDPI値を取得する方法が見つかりました。出典は次のURLです。ほかにも1つか2つありましたが、こちらが要領よく出ているかと思います。
    https://excel-ubara.com/excelvba4/EXCEL_VBA_437.html

    物理DPI値を返す関数

    Function GetDpi() As Long
    Const cSql As String = "Select * From Win32_DisplayConfiguration"
      With CreateObject("WbemScripting.SWbemLocator").ConnectServer
        GetDpi = .ExecQuery(cSql).ItemIndex(0).LogPixels
      End With
    End Function

    拡大された画面の論理DPI値?を返す関数

    Public Function LogicalPixcel() As Long
    Dim hWndDesk As Long
    Dim hDCDesk As Long
      'デスクトップのウィンドウハンドルを取得
      hWndDesk = GetDesktopWindow()
      'デバイスコンテキストを取得
      hDCDesk = GetDC(hWndDesk)
      'デバイス固有情報を取得
      LogicalPixcel = GetDeviceCaps(hDCDesk, LOGPIXELSX)
      'デバイスコンテキストを解放
      Call ReleaseDC(hWndDesk, hDCDesk)
    End Function

    画面を拡大してからVBAを組み込んだエクセルブックを開いて、VBAを実行すると、拡大された画面のDPI値が取得できます。
    VBAを組み込んだエクセルブックを先に開いておいて、画面を拡大して、VBAを実行すると、拡大前の画面のDPI値しか取得できません。
    要するに、VBAを実行する時点の画面のDPI値が取得できるのではなく、ブックを開いた時の画面のDPI値が取得できる、という制限付きです。

    >(WalkerShadowさんのコメント)私の所では125%のメインディスプレイではずれがなく、100%のサブディスプレイでずれるというの状況
    確かに小生のご報告した状況とは逆ですね。今回のDPI値取得も画面を拡大してからブックを開くという手順を踏まないとだめなので、同様に何かしらの些細な手順が異なるのかもしれませんね。

    >(WalkerShadowさんのコメント)一つShapeを動かすたびにガクガク画面が変わっていてはちょっとアレなので、使えないのではないかと考えます。

    確かにお客様に使ってもらうソフトとなるとそういう判断になるのかな、と思います。なお、拡大画面でのこの処理に関するお客様のニーズが強ければ(例えば必要不可欠とか)、判断は変わってくる可能性もあるのかな、という気もしました。

    >(WalkerShadowさんのコメント)ここにはMicrosoftの方は居られないのでしょうか。
    いらっしゃらない事もないのではないかと小生も思いますが、お答えすべきテーマや内容に関してコミットメントする権限が与えられていない、と言う事ではないでしょうか。

    2021年3月9日 11:53
  • 環境が無いので試していませんが、下記のコードでは「True」と表示されるのですか?

    ActiveSheet.Shapes("Shape1").Top = Range("A10").Top

    Msgbox (ActiveSheet.Shapes("Shape1").Top = Range("A10").Top)

    もし「False」になるなら「True」になるまで補正すればよさそうな気が・・・


    2021年3月9日 12:33
  • Microsoft エージェントの方からこちらへの誘導を受けたのですが、
    ここにはMicrosoftの方は居られないのでしょうか。

    昔から「コミュニティにおけるマイクロソフト社員による発言やコメントは、マイクロソフトの正式な見解またはコメントではありません。」というスタンスと公表されていました。
    現状に変化があるかどうかは把握していませんが、実績として 公式サポートを提供する場所ではない のは確かです。

    フォーラムでご質問頂くにあたっての注意点 より:
    >MSDN/TechNetフォーラムは参加しているユーザー同士が情報交換を行うための場となっております。
    (略)
    >業務上クリティカルなトラブルや障害が発生した場合には、情報提供の迅速性や情報の正確性も必要となるかと思いますのでマイクロソフトの有償サポートをご利用頂くこともご検討ください。

    -----

    DPI スケーリングを取得して計算するという回避策を模索することも考えられますが、Microsoft 365 (Office 365) で突然修正されるようなことがあれば、その時点からマクロは不正な動作をすることになるので、環境を固定できない限り、回避実装は将来補償が難しくなるので避けたいところです。

    なので、そのコードを使わない仕様を模索できないかを考えたいところです。
    たとえば、操作手順で「ユーザーにちょっとだけ負担してもらう」ことで回避できないかなど。

    2021年3月9日 12:45
  • minmin312さん、今晩は。

    ご呈示になったコードをお試しになっていないのだろうと思いますが、ブックを開いてから拡大率125%にディスプレイを変更してご呈示のコードを実行するとFalseとなります。値を取得すると、ActiveSheet.Shapes("Shape1").Top= 118.5で、Range("A10").Top=118.8 となります。
    この小数点以下の0.3の誤差値は"A100"に変えても同じでしたが、"A100"の場合は既にご報告があるように、ずれが大きくなっています。

    「もし「False」になるなら「True」になるまで補正すればよさそうな気が・・・」とのことですが、手動で補正するなら補正できますが、その場合もMsgBox文を実行すればFalseになります。Shapeを"A100"のTopの位置に合わせて値を求めると、ActiveSheet.Shapes("Shape1").Top= 1366.85 に対して、Range("A100").Top=1336.5 となります。画面拡大によりRange("A100").Topで得られる値が画面上の位置とずれている位置の値となっている、ということです。したがって、これをどの様にVBAで補正することができるのか考えてみましたが、正しいRange("A100").Topの値が取得できない限り、補正できないのではないかと思います。補正の仕方について、何かアイデアがあれば、よろしくお願いします。

    2021年3月9日 13:11
  • WalkerShadowさん、今晩は。

    DPI値の取得に関する小生のご返事に記したコードの試行結果から、ディスプレイの拡大処理とWorkbookを開く処理の後先の順番がずれの発生に影響するのではないか、と思い至り、試行してみたところ、やはり、そうでした。このため、3月4日7:44で記した試行結果と3月8日9:59で記した試行結果と異なる結果が出たのだろうと思われます。小生の勘違いではなかったことが分かって一安心しましたが、試行する際には手順まで気を付けないといけないと再確認いたしました。

    ところで試行結果ですが、マルチディスプレイではなくて1つだけのディスプレイにおいて、(1)100%表示のディスプレイでWorkbookを開き、ディスプレイを125%に拡大してVBAを実行するとずれる、(2)125%表示のディスプレイでWorkbookを開き、ディスプレイを100%表示に戻してVBAを実行するとずれる、ようです。

    また、更に、厄介なのは、(3)100%表示のディスプレイで別のエクセルブックを開いておいて、ディスプレイを125%に拡大してVBAを組み込んであるWorkbookを開き、Shapeの位置指定のVBAを実行するとずれる、(4)125%拡大率のディスプレイで別のエクセルブックを開いておいて、ディスプレイを100%表示に戻してWorkbookを開きVBAを実行するとずれる、ようです。

    どうやら、位置指定のポイントを計算する元のデータがブックごとに取得されるわけではなくて、最初にブックを開いた時にExcelApplicationで取得されて保持されているようです。となると、ディスプレイの拡大率を100%に戻したらShapeの位置指定が正常に動作する、と言う訳でもなさそうです。

    以上の結果からすると、マルチディスプレイの1つの拡大率を100%でない拡大率にしてExcelを開いてShapeの位置指定を含むVBAを運用する、と言うのは、ご自分が使う場合であれば良いでしょうけれど、人様に使ってもらうとなると、ちょっと難しい感じがします。ブックを幾つも開くということは十分普通にあり得ることですから。もはや、Excelの画面上の表示位置が関係するVBAとディスプレイの拡大率の変更とは、相性が悪いから避けた方が良い、と観念した方が良さそうです。コードを何とかしたらどうにかなる、というレベルの話ではないような気がします。

    Azuleanさんのアドバイスを踏まえると、WalkerShadowさんの求める処理に合致しているかどうか疑問ではありますが、ディスプレイの拡大率を拡大変更しないで100%のままにしてもらい、ブックの表示を拡大表示してShapeの位置指定の処理を実行する、という方法がよい、と言うか、それしかないような気がします。ブックを開く際にディスプレイの拡大率を取得して、拡大されている場合は「すべてのエクセルファイルを閉じて、ディスプレイの拡大率を100%に変更した上で、ブックを開き直す」様にメッセージを表示して閉じる。再度開く際にはディスプレイの拡大率が100%であることを確認し、(念を入れれば、かつ、他にエクセルのブックが開かれていないことを確認した上で、)ブックの表示を拡大する。という様な手順をコードで組み込む、と言うのが小生のアイデアです。ただし、もっと良い方法があるかもしれません。

    2021年3月9日 15:07