2017-02-06 2 views
0

J'écris une application .Net WPF qui permet à d'autres programmes d'y écrire. L'une des fonctionnalités que les concepteurs veulent implémenter est un curseur personnalisé qui est utilisé dans les programmes tiers pour remplacer l'éditeur de texte "I-Beam".Bas niveau Réglage du curseur système sur personnalisé dans .Net Office Interop

Le premier programme tiers avec lequel j'ai joué est MS Word 2013, en utilisant Office Interop de .Net. J'ai une DLL C# globale dans le domaine d'application de mon programme qui contient la ressource de curseur.

Interop ne vous permet pas d'attribuer des curseurs personnalisés (bien que cela vous permette de changer de curseur). Donc, après beaucoup de creuser, j'ai essayé de déconner.

Dans l'Interop Addin, j'ai ajouté cette classe:

public static class CursorHook 

{ 


    private static IntPtr programCursor = IntPtr.Zero; 
    private static IntPtr systemCursor = LoadCursor(IntPtr.Zero, OCR_IBEAM); 

    public static void Init() 
    { 
     programCursor = Marshal.AllocHGlobal(resourceCursor.marker_cursor2.Length); 
     Marshal.Copy(resourceCursor.marker_cursor2, 0, programCursor, resourceCursor.marker_cursor2.Length); 
    } 

    public static void Start() 
    { 



     SetSystemCursor(programCursor, OCR_IBEAM); 

    } 
    public static void Stop() 
    { 
     SetSystemCursor(systemCursor, OCR_IBEAM); 


    } 

    public static void Dispose() 
    { 
     Marshal.FreeHGlobal(programCursor); 
    } 

    private static IntPtr _hookID = IntPtr.Zero; 





    private const int OCR_IBEAM = 32513; 





    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern bool SetSystemCursor(IntPtr hCursor, uint id); 
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
    private static extern IntPtr LoadCursor(IntPtr hInstance, int id); 
    [DllImport("user32.dll")] 
    private static extern IntPtr SetCursor(IntPtr hCursor); 


} 

Dans ma classe Addin, je fais ceci:

public partial class ThisAddIn 
{ 
    //Stuff 
    private void ThisAddIn_Startup(object sender, System.EventArgs e) 
    { 
     CursorHook.Init(); 
     CursorHook.Start(); 

     this.Application.WindowDeactivate += new WordInterop.ApplicationEvents4_WindowDeactivateEventHandler(Deactivated); 
     this.Application.WindowActivate += new WordInterop.ApplicationEvents4_WindowActivateEventHandler(Activated); 
     //Stuff 
    } 


    private void ThisAddIn_Shutdown(object sender, System.EventArgs e) 
    { 
     CursorHook.Stop(); 
     CursorHook.Dispose(); 
    } 

    //stuff 

    private void Activated(WordInterop.Document Doc, WordInterop.Window Wn) { CursorHook.Start(); } 
    private void Deactivated(WordInterop.Document Doc, WordInterop.Window Wn) { CursorHook.Stop(); } 

    //stuff 

} 

Cependant, le curseur ne change pas (même si le code est appelé). Qu'est-ce que je rate? Par ailleurs, je comprends mieux la façon dont une classe statique peut ne pas être la meilleure utilisation des ressources. Mais en ce moment, j'essaie juste d'amener le curseur à faire ce que je veux, je vais travailler sur l'élégance après que cela soit accompli.

Merci d'avance!

EDIT

J'ai modifié le code pour ressembler à ceci:

public static class CursorHook 

{ 


    private static IntPtr ptr_IBeam = IntPtr.Zero; 


    private static IntPtr programCursor = IntPtr.Zero; 
    private static IntPtr systemCursor = IntPtr.Zero; 
    private static Cursor ProgramCursor = null; 


    public static void Init() 
    { 


     using(MemoryStream ms = new MemoryStream(resourceCursor.marker_cursor2)) 
     { 
      ProgramCursor = new Cursor(ms); 
     } 
     programCursor = ProgramCursor.Handle; 
     systemCursor = Cursors.IBeam.CopyHandle(); 

    } 

    public static void Start() 
    { 

     SetSystemCursor(programCursor, UNS_OCR_IBEAM); 

    } 


    public static void Stop() 
    { 

      SetSystemCursor(systemCursor, UNS_OCR_IBEAM);   


    } 

    public static void Dispose() 
    { 

    } 

    private const int OCR_IBEAM = 32513; 
    private const uint UNS_OCR_IBEAM = OCR_IBEAM; 
    private static IntPtr PTR_OCR_IBEAM = new IntPtr(OCR_IBEAM); 





    [DllImport("user32.dll", CharSet = CharSet.Auto)] 
    private static extern bool SetSystemCursor(IntPtr hCursor, uint id); 




} 

Il est maintenant beaucoup plus simple tout autour, en grande partie grâce au commentaire de HansPassant ci-dessous. Cela fonctionne un peu, maintenant, aussi!

Pourtant, il y a encore deux bogues.

1) L'icône n'est pas en couleur. Je sais que c'est un problème avec les icônes personnalisées et il existe des solutions de contournement, donc je vais faire des recherches moi-même.

Ce qui m'a bloqué: 2) L'icône tournera à mon icône personnalisée sur la Parole d'ouverture, et revenir à la valeur par défaut d'un en cliquant en dehors de Word. Mais ensuite, il refusera de revenir à mon icône après cela. SetSystemCursor() retournera faux chaque fois que j'activerai MS Word. Pourquoi?

+0

Il est sûr de supposer que la fenêtre cible gère le [WM_SETCURSOR] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms648382.aspx), mettre son propre curseur à sa guise, annulant ainsi votre tentative naïve de changer la forme du curseur. – IInspectable

+0

Sauf que j'essaie de changer le curseur SYSTEM, peu importe ce que fait la fenêtre cible. – Moe45673

+1

Cela suppose que la fenêtre cible utilise le curseur SYSTEM. – IInspectable

Répondre

0

Voici le code final. Cela fonctionne mais le curseur n'est pas en couleur. Comme les curseurs de couleur sont un mal de tête connu dans Windows, c'est un problème différent. Un grand merci à Hans Passant

public static class CursorHook 

{ 




    private static IntPtr programCursor = IntPtr.Zero; 
    private static IntPtr systemCursor = IntPtr.Zero; 
    private static Cursor ProgramCursor = null; 

    private static Cursor DefaultCursor = null; 


    public static void Init() 
    { 


     using(MemoryStream ms = new MemoryStream(resourceCursor.marker_cursor2)) 
     { 
      ProgramCursor = new Cursor(ms); 
     } 
     DefaultCursor = new Cursor(Cursors.IBeam.CopyHandle()); 



    } 

    public static void Start() 
    { 
     //as the GC has likely invalidated the IntPtr, each time you have to assign it anew 
     programCursor = ProgramCursor.CopyHandle(); 
     SetSystemCursor(programCursor, UNS_OCR_IBEAM); 

    } 


    public static void Stop() 
    { 
     //as the GC has likely invalidated the IntPtr, each time you have to assign it anew 
     systemCursor = DefaultCursor.CopyHandle(); 
     SetSystemCursor(systemCursor, UNS_OCR_IBEAM);   


    } 

    public static void Dispose() 
    { 

    } 

    private const int OCR_IBEAM = 32513; 
    private const uint UNS_OCR_IBEAM = OCR_IBEAM; 
    private static IntPtr PTR_OCR_IBEAM = new IntPtr(OCR_IBEAM); 





    [DllImport("user32.dll", CharSet = CharSet.Auto)] 
    private static extern bool SetSystemCursor(IntPtr hCursor, uint id); 




}