Bei Dual Monitor permanent ClipCursor auf Rechteck von Monitor 1



  • Hallo zusammen,

    So wars früher:
    ---------------

    bei unserem Quarz Tuner hatten wir bisher für die Bilderkennung eine CCIR Kamera deren Videoausgang auf einen Videomonitor und parallel auf eine Framegrabberkarte gieng. Das Live Bild war genügend aber beim GrabImage für die Positionserkennung verloren wir sehr viel Zeit über 50mSec. plus Grabzeit pro Bild .

    Neu haben wir eine Digital ProgressiveScan Autoreset USB Kamera. Das Live Bild wird durch GrabImage in einem Thread erzeugt und in einem modeless Dialog angezeigt. Dieser modeless Dialog wird durch die Quarz Tuner App automatisch auf das Rechteck von Monitor 2 positioniert. In der Quarz Tuner App wird in OnActivate ClipCursor (mMonitor1Rect) und SetCursor (CenterMonitor1Rect) aufgerufen. Damit ist der Cursor dort wo er sein soll.

    Problem:
    --------

    Wenn die Benutzer zB. den Explorer öffnen, dann ist wieder der ganze Bildschirmbereich (Monitor1 und Monitor2) erreichbar. Leider wird auch durch das schliessen zB. des Explorers nicht automatisch die Quarz Tuner App aktiviert. Wenn der Cursor aus irgendeinem Grund auf den Monitor2 gelangt ist, dann finden die Benutzer meistens den Cursor nicht mehr und rufen den Support.

    Frage:
    ------

    Gibt es eine Möglichkeit das Cursor Rechteck (ClipCursor) permanent und Systemweit auf den Monitor1 zu beschränken?

    Jeder Hack ist willkommen im Notfall sogar Löten 😃

    Herzliche Grüsse
    Walter


  • Mod

    Ich denke, dass dieses Verhalten bei Design ist und das ein ClipCursor bei einem Focus Wechsel sowieso verloren geht.

    Einzige systemweite Lösung die mir dazu einfällt ist ein Maus-Hook WH_MOUSE...


  • Mod



  • Hallo Martin,

    ich schaue mir mal den Maus-Hook an.

    Merci
    Walter



  • Mit dem MouseHook funktionierts 🙂

    Da mir die Hilfe aus dem Forum etwas Wert ist hier meine Lösung:

    Zuerst die MultiDisplay Klasse, ich weiss nicht mehr woher ich die habe eventuell mit den Angaben im MSDN selber zusammengebastelt.

    MultiDisplay.h

    #pragma once
    
    #ifndef MC_MULTIDISPLAY_H
    #define MC_MULTIDISPLAY_H
    
    //----------------------------------------------------------------------------------
    //	Lokale Deklarationen
    //----------------------------------------------------------------------------------
    struct SMonInfo
    {
      CSize mMonitorSize;
      CRect mMonitorRect;
    };
    
    typedef std::map<int, SMonInfo>	DisplayInfo;
    
    //------------------|---------------------------------------------------------------
    //  CMultiDisplay   Deklaration der Multi Display Klasse
    //----------------------------------------------------------------------------------
    class CMultiDisplay
    {
    public:
      CMultiDisplay  ();
    
      UINT   DisplayCount () const;
    
      CRect  DisplayRect  (int displayID);
      CSize  DisplaySize  (int displayID);
    
    private:
      DisplayInfo mDisplays;
    };
    
    #endif	//	MC_MULTIDISPLAY_H
    

    MultiDisplay.cpp

    //----------------------------------------------------------------------------------
    //	Includes
    //----------------------------------------------------------------------------------
    #include "StdAfx.h"
    
    //----------------------------------------------------------------------------------
    //	MC++ Includes
    //----------------------------------------------------------------------------------
    #include "UI/_MultiDisplay.h"
    
    //----------------------------------------------------------------------------------
    //	Debug Defines
    //----------------------------------------------------------------------------------
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif
    
    //----------------------------------------------------------------------------------
    //	Using Deklarationen
    //----------------------------------------------------------------------------------
    using namespace std;
    
    //----------------------------------------------------------------------------------
    //	Lokale Deklarationen
    //----------------------------------------------------------------------------------
    struct ENUM_DISP_ARG
    {
      int          mMonID;
      DisplayInfo  mInfo;
    };
    
    BOOL CALLBACK EnumDisplays (HMONITOR /*hMon*/, HDC /*dcMon*/, RECT* rect, LPARAM lParam)
    {
      ENUM_DISP_ARG* arg = reinterpret_cast<ENUM_DISP_ARG*>(lParam);
      SMonInfo monInfo;
      monInfo.mMonitorSize = CSize (rect->right - rect->left, rect->bottom - rect->top);
      monInfo.mMonitorRect = CRect (rect->left, rect->top, rect->right, rect->bottom);
      arg->mInfo[arg->mMonID] = monInfo;
      arg->mMonID++;
      return TRUE;
    }
    
    //==================|===============================================================
    //	CMultiDisplay	Definition der Multi Display Klasse
    //==================================================================================
    //----- constructor / destructor ---------------------------------------------------
    CMultiDisplay::CMultiDisplay ()
    {
      ENUM_DISP_ARG arg = { 0 };
      arg.mMonID = 1;
      EnumDisplayMonitors (0, 0, EnumDisplays, reinterpret_cast<LPARAM>(&arg));
    
      mDisplays = arg.mInfo;
    }
    
    //----- DisplayCount ---------------------------------------------------------------
    UINT CMultiDisplay::DisplayCount ()	const
    {
      return mDisplays.size ();
    }
    
    //----- DisplayRect ----------------------------------------------------------------
    CRect CMultiDisplay::DisplayRect (int displayID)
    {
      DisplayInfo::const_iterator it = mDisplays.find (displayID);
      if (it != mDisplays.end ())
        return it->second.mMonitorRect;
    
      return CRect (0, 0, 0, 0);
    }
    
    //----- DisplaySize ----------------------------------------------------------------
    CSize CMultiDisplay::DisplaySize (int displayID)
    {
      DisplayInfo::const_iterator it = mDisplays.find (displayID);
      if (it != mDisplays.end ())
        return it->second.mMonitorSize;
    
      return CSize (0, 0);
    }
    

    Dann die Applikationsklasse, hier befindet sich die MouseHook Funktion sowie das ein- und ausschalten des Hooks. Ich war ein wenig überrascht, dass ein aufrufen der SetCursorPos in der Hook Funktion wirkungslos war. Die Meldung mit PostMessage an den MainFrame aber funktioniert.

    MX-FinalTuner.h

    #pragma once
    
    #ifndef MC_MX_FINALTUNER_H
    #define MC_MX_FINALTUNER_H
    
    //----------------------------------------------------------------------------------
    //	Includes
    //----------------------------------------------------------------------------------
    #ifndef __AFXWIN_H__
    #error "\"stdafx.h\" vor dieser Datei für PCH einschließen"
    #endif
    
    #include "resource.h"       // Hauptsymbole
    
    //------------------|---------------------------------------------------------------
    // CMX_FinalTuner   Deklaration der Applikation
    //----------------------------------------------------------------------------------
    class CMX_FinalTuner : public CWinAppEx
    {
    public:
      CMX_FinalTuner();
      MC::VApplication*     Machine      () const { return mMachine; }
    
    private:
      virtual BOOL          InitInstance ();
      virtual BOOL          OnIdle       (LONG lCount);
      virtual int           ExitInstance ();
      afx_msg void          OnAppAbout   ();
      DECLARE_MESSAGE_MAP()
    
      MC::VApplication*     mMachine;
      HANDLE                mThisApp;
      MC::TPrinterControl*  mPrtCtrl;
      CMultiDisplay         mMultiDisplay;
      HHOOK                 mMouseHook;
    };
    
    extern CMX_FinalTuner gTheApp;
    
    #endif	//	MC_MX_FINALTUNER_H
    

    MX-FinalTuner.cpp

    //----------------------------------------------------------------------------------
    //	Includes
    //----------------------------------------------------------------------------------
    #include "StdAfx.h"
    #include "MX_FinalTuner.h"
    #include "MainFrm.h"
    #include "AboutDlg.h"
    
    #include "MX_FinalTunerDoc.h"
    #include "SetupView.h"
    #include "io.h"
    #include "fcntl.h"
    
    //----------------------------------------------------------------------------------
    //	Debug Defines
    //----------------------------------------------------------------------------------
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif
    
    //----------------------------------------------------------------------------------
    //	Using Deklarationen
    //----------------------------------------------------------------------------------
    using namespace MC;
    using namespace std;
    
    //----------------------------------------------------------------------------------
    //	Lokale Deklarationen
    //----------------------------------------------------------------------------------
    DECLARE_USER_MESSAGE(UWM_RESTORE_CURSOR)
    
    BEGIN_MESSAGE_MAP(CMX_FinalTuner, CWinAppEx)
    	ON_COMMAND(ID_APP_ABOUT, &CMX_FinalTuner::OnAppAbout)
    	// Dateibasierte Standarddokumentbefehle
    	ON_COMMAND(ID_FILE_NEW, &CWinAppEx::OnFileNew)
    	ON_COMMAND(ID_FILE_OPEN, &CWinAppEx::OnFileOpen)
    	// Standarddruckbefehl "Seite einrichten"
    	ON_COMMAND(ID_FILE_PRINT_SETUP, &CWinApp::OnFilePrintSetup)
    END_MESSAGE_MAP()
    
    CMX_FinalTuner gTheApp;
    
    // Diese beiden Variablen gefallen mir nicht besonders
    // Habe aber keine andere Möglichkeit gefunden
    CRect   gMainDisplayRect;
    CPoint  gOldMousePos;
    
    //----- MouseHookProc --------------------------------------------------------------
    LRESULT CALLBACK MouseHookProc (int code, WPARAM wParam, LPARAM lParam)
    {
      PMSLLHOOKSTRUCT p = reinterpret_cast<PMSLLHOOKSTRUCT> (lParam);
    
      CPoint mousePos = p->pt;
      if (gMainDisplayRect.PtInRect (mousePos))
        gOldMousePos = mousePos;
      else
      {
        CFrameWnd* mainFrame = static_cast<CFrameWnd*> (AfxGetMainWnd ());
        mainFrame->PostMessage (UWM_RESTORE_CURSOR, gOldMousePos.x, gOldMousePos.y);
      }
    
      return CallNextHookEx (NULL, code, wParam, lParam);;
    }
    

    Den cast auf CFrameWnd* kann man sich wahrscheinlich sparen.

    CMX_FinalTuner.cpp

    //==================|===============================================================
    //  CMX_FinalTuner  Definition der Applikation
    //==================================================================================
    //----- constructor / destructor ---------------------------------------------------
    CMX_FinalTuner::CMX_FinalTuner()
    {
    	InitExeptions ();
    	SetThreadName (_T("UI_Thread"));
    }
    
    //----- InitInstance ---------------------------------------------------------------
    BOOL CMX_FinalTuner::InitInstance()
    {
      // Diese Applikation darf nur einmal gestartet werden!
      mThisApp = CreateMutexW (NULL, TRUE, _T("MX_FinalTuner"));
      if (GetLastError () == ERROR_ALREADY_EXISTS)
      {
        ENSURE (mThisApp != NULL);
        CloseHandle (mThisApp);
        mThisApp = NULL;
        return FALSE;
      }
    
      SetRegistryKey(_T("Micro Crystal"));
      mMachine = CreateMachine ();
      if (mMachine == NULL)
      {
        AfxMessageBox (_T("Could not create machine!"));
        return FALSE;
      }
    
      if (ErrorFlag ())
      {
        UserError (IDS_INIT_ERROR, _T("CMX_FinalTuner::InitInstance"), eErrFatal);
        ShowError (IDS_INIT_ERROR);
        return FALSE;
      }
    
      mPrtCtrl = Object<TPrinterControl> (kPrinterControl);
      ASSERT (mPrtCtrl != NULL);
      mPrtCtrl->SetHandles (m_hDevMode, m_hDevNames);
    
      INITCOMMONCONTROLSEX InitCtrls;
      InitCtrls.dwSize = sizeof(InitCtrls);
    
      InitCtrls.dwICC = ICC_WIN95_CLASSES;
      InitCommonControlsEx(&InitCtrls);
    
      CWinApp::InitInstance();
    
      AfxEnableControlContainer();
      LoadStdProfileSettings(4);
    
      mPrtCtrl->RestorePrinterSelection ();
    
      CSingleDocTemplate* pDocTemplate;
      pDocTemplate = new CSingleDocTemplate(
                                            IDR_MAINFRAME,
                                            RUNTIME_CLASS(CMX_FinalTunerDoc),
                                            RUNTIME_CLASS(CMainFrame),
                                            RUNTIME_CLASS(CSetupView));
      if (!pDocTemplate)
        return FALSE;
    
      AddDocTemplate(pDocTemplate);
    
      CCommandLineInfo cmdInfo;
      ParseCommandLine(cmdInfo);
    
      if (!ProcessShellCommand(cmdInfo))
        return FALSE;
    
      ObjRef<TParaHandler> (kParaHandler).SetMRUPointer (m_pRecentFileList);
    
      InitShellManager ();
    
      mMachine->PostUICreate ();
    
      m_pMainWnd->ShowWindow (SW_SHOW);
      m_pMainWnd->UpdateWindow ();
    
      gMainDisplayRect = mMultiDisplay.DisplayRect (1);
      gMainDisplayRect.DeflateRect (5,5);
    
      mMouseHook = SetWindowsHookEx (WH_MOUSE_LL, MouseHookProc, NULL, NULL);
    
      return TRUE;
    }
    
    //----- ExitInstance ---------------------------------------------------------------
    int CMX_FinalTuner::ExitInstance()
    {	
      if (mPrtCtrl != NULL)
        mPrtCtrl->SavePrinterSelection ();
    
      if (mMachine != NULL)
        mMachine->Shutdown ();
    
      UnhookWindowsHookEx (mMouseHook);
    
      int res = CWinApp::ExitInstance();
    
      return res;
    }
    

    In MainFrame ist nur die Funktion OnRestoreCursor interessant. Diese setzt den Cursor auf die letzte Position vor dem verlassen des Monitor1 Rect.

    MainFrame.h

    #pragma once
    
    #ifndef MC_MX_FINALTUNER_MAINFRAME_H
    #define MC_MX_FINALTUNER_MAINFRAME_H
    
    //----------------------------------------------------------------------------------
    //	Includes
    //----------------------------------------------------------------------------------
    #include "MX_FinalTunerView.h"
    
    //----------------------------------------------------------------------------------
    //	Lokale Deklarationen
    //----------------------------------------------------------------------------------
    
    class CHardwareDlg;
    
    //------------------|---------------------------------------------------------------
    //	CMainFrame  Deklaration des MainFrame
    //----------------------------------------------------------------------------------
    class CMainFrame : public CFrameWnd
    {
    
    public:
      virtual ~CMainFrame ();
    
    protected:
      CMainFrame ();
    
      DECLARE_DYNCREATE (CMainFrame)
    
      afx_msg LRESULT OnRestoreCursor (WPARAM, LPARAM);
    
      DECLARE_MESSAGE_MAP()
    
    };
    
    #endif	//	MC_MX_FINALTUNER_MAINFRAME_H
    

    MainFrame.cpp

    //----------------------------------------------------------------------------------
    //	Includes
    //----------------------------------------------------------------------------------
    #include "StdAfx.h"
    #include "MainFrm.h"
    #include "resource.h"
    
    //----------------------------------------------------------------------------------
    //	MC++ Includes
    //----------------------------------------------------------------------------------
    #include "MX_FinalTuner.h"
    
    //----------------------------------------------------------------------------------
    //	Debug Defines
    //----------------------------------------------------------------------------------
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif
    
    //----------------------------------------------------------------------------------
    //	Using Deklarationen
    //----------------------------------------------------------------------------------
    using namespace std;
    using namespace MC;
    
    //----------------------------------------------------------------------------------
    //	Lokale Deklarationen
    //----------------------------------------------------------------------------------
    LPCTSTR	kRegShowLiveImage = _T("Software\\Micro Crystal\\MX_FinalTuner\\LiveImage");
    
    DECLARE_USER_MESSAGE(UWM_RESTORE_CURSOR)
    
    IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    	ON_REGISTERED_MESSAGE(UWM_RESTORE_CURSOR, OnRestoreCursor)
    END_MESSAGE_MAP()
    
    //==================|===============================================================
    //  CMainFrame      Definition des MainFrame
    //==================================================================================
    //----- constructor / destructor ---------------------------------------------------
    CMainFrame::CMainFrame()
    : mActiveView     (NULL),
      mSetupView      (NULL),
      mAutomatView    (NULL),
      mHardwareStatus (NULL),
      mProgressBox    (NULL),
      mKeyPad         (NULL),
      mMiniKeyboard   (NULL),
      mHardwareDlg    (NULL)
    {}
    
    CMainFrame::~CMainFrame()
    {}
    
    //----- OnRestoreCursor ------------------------------------------------------------
    LRESULT CMainFrame::OnRestoreCursor (WPARAM x, LPARAM y)
    {
      SetCursorPos (x, y);
      SetFocus ();
      return TRUE;
    }
    
    #endif //_DEBUG
    

    Nochmals Danke an Martin. Hat spass gemacht das zu implementieren und gab ne ganze Menge Überstunden 😉

    Walter


Log in to reply