2009-07-23 12 views

Respuesta

17

Aquí hay un código que utiliza la biblioteca .Net Automatización para copiar todo el texto en el portapapeles.

Iniciar un nuevo proyecto WinForms y luego añadir las siguientes referencias:

  • WindowsBase
  • UIAutomationTypes
  • UIAutomationClient
  • System.Xaml
  • PresentationCore
  • PresentationFramework
  • System.Management

El código también explica cómo configurar un elemento de menú en Visual Studio para copiar el contenido en el portapapeles.

Editar: La UI Automation solo devuelve elementos visibles de la vista de árbol. Por lo tanto, para copiar todos los elementos, la ventana de resultados del símbolo de búsqueda se establece como primer plano, y luego se envía un {PGDN}, y se copia el siguiente lote de elementos. Este proceso se repite hasta que no se encuentren nuevos elementos. Hubiera sido preferible usar el ScrollPattern, sin embargo, lanzó un Exception al intentar establecer el desplazamiento.

Editar 2: Intenté mejorar el rendimiento de AutomationElement FindAll ejecutándose en un hilo separado. Parece ser lento en algunos casos.

Editar 3: Mejorado el rendimiento al hacer que la ventana TreeView sea muy grande. Puede copiar unos 400 elementos en aproximadamente 10 segundos.

Editar 4: Eliminar objetos que implementen IDisposable. Mejor informe de mensajes. Mejor manejo de procesos args. Ponga la ventana a su tamaño original.

enter image description here

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Management; 
using System.Runtime.InteropServices; 
using System.Text; 
using System.Threading; 
using System.Windows.Automation; 
using System.Windows.Forms; 

namespace CopyFindSymbolResults { 

// This program tries to find the 'Find Symbol Results' window in visual studio 
// and copy all the text to the clipboard. 
// 
// The Find Symbol Results window uses a TreeView control that has the class name 'LiteTreeView32' 
// In the future if this changes, then it's possible to pass in the class name as the first argument. 
// Use TOOLS -> Spy++ to determine the class name. 
// 
// After compiling this code into an Exe, add a menu item (TOOLS -> Copy Find Symbol Results) in Visual Studio by: 
// 1) TOOLS -> External Tools... 
//  (Note: in the 'Menu contents:' list, count which item the new item is, starting at base-1). 
//  Title: Copy Find Symbol Results 
//  Command: C:\<Path>\CopyFindSymbolResults.exe    (e.g. C:\Windows\ is one option) 
// 2) TOOLS -> Customize... -> Keyboard... (button) 
//  Show Commands Containing: tools.externalcommand 
//  Then select the n'th one, where n is the count from step 1). 
// 
static class Program { 

    enum Tabify { 
     No = 0, 
     Yes = 1, 
     Prompt = 2, 
    } 

    [STAThread] 
    static void Main(String[] args) { 

     String className = "LiteTreeView32"; 
     Tabify tabify = Tabify.Prompt; 

     if (args.Length > 0) { 
      String arg0 = args[0].Trim(); 
      if (arg0.Length > 0) 
       className = arg0; 

      if (args.Length > 1) { 
       int x = 0; 
       if (int.TryParse(args[1], out x)) 
        tabify = (Tabify) x; 
      } 
     } 

     DateTime startTime = DateTime.Now; 
     Data data = new Data() { className = className }; 

     Thread t = new Thread((o) => { 
      GetText((Data) o); 
     }); 
     t.IsBackground = true; 
     t.Start(data); 

     lock(data) { 
      Monitor.Wait(data); 
     } 

     if (data.p == null || data.p.MainWindowHandle == IntPtr.Zero) { 
      System.Windows.Forms.MessageBox.Show("Cannot find Microsoft Visual Studio process."); 
      return; 
     } 

     try { 

     SimpleWindow owner = new SimpleWindow { Handle = data.MainWindowHandle }; 

     if (data.appRoot == null) { 
      System.Windows.Forms.MessageBox.Show(owner, "Cannot find AutomationElement from process MainWindowHandle: " + data.MainWindowHandle); 
      return; 
     } 

     if (data.treeViewNotFound) { 
      System.Windows.Forms.MessageBox.Show(owner, "AutomationElement cannot find the tree view window with class name: " + data.className); 
      return; 
     } 

     String text = data.text; 
     if (text.Length == 0) { // otherwise Clipboard.SetText throws exception 
      System.Windows.Forms.MessageBox.Show(owner, "No text was found: " + data.p.MainWindowTitle); 
      return; 
     } 

     TimeSpan ts = DateTime.Now - startTime; 

     if (tabify == Tabify.Prompt) { 
      var dr = System.Windows.Forms.MessageBox.Show(owner, "Replace dashes and colons for easy pasting into Excel?", "Tabify", System.Windows.Forms.MessageBoxButtons.YesNo); 
      if (dr == System.Windows.Forms.DialogResult.Yes) 
       tabify = Tabify.Yes; 

      ts = TimeSpan.Zero; // prevent second prompt 
     } 

     if (tabify == Tabify.Yes) { 
      text = text.Replace(" - ", "\t"); 
      text = text.Replace(" : ", "\t"); 
     } 

     System.Windows.Forms.Clipboard.SetText(text); 

     String msg = "Data is ready on the clipboard."; 
     var icon = System.Windows.Forms.MessageBoxIcon.None; 

     if (data.lines != data.count) { 
      msg = String.Format("Only {0} of {1} rows copied.", data.lines, data.count); 
      icon = System.Windows.Forms.MessageBoxIcon.Error; 
     } 

     if (ts.TotalSeconds > 4 || data.lines != data.count) 
      System.Windows.Forms.MessageBox.Show(owner, msg, "", System.Windows.Forms.MessageBoxButtons.OK, icon); 

     } finally { 
      data.p.Dispose(); 
     } 
    } 

    private class SimpleWindow : System.Windows.Forms.IWin32Window { 
     public IntPtr Handle { get; set; } 
    } 

    private const int TVM_GETCOUNT = 0x1100 + 5; 

    [DllImport("user32.dll")] 
    static extern int SendMessage(IntPtr hWnd, int msg, int wparam, int lparam); 

    [DllImport("user32.dll", SetLastError = true)] 
    static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int Width, int Height, bool Repaint); 

    private class Data { 
     public int lines = 0; 
     public int count = 0; 
     public IntPtr MainWindowHandle = IntPtr.Zero; 
     public IntPtr TreeViewHandle = IntPtr.Zero; 
     public Process p; 
     public AutomationElement appRoot = null; 
     public String text = null; 
     public String className = null; 
     public bool treeViewNotFound = false; 
    } 

    private static void GetText(Data data) { 
     Process p = GetParentProcess(); 
     data.p = p; 

     if (p == null || p.MainWindowHandle == IntPtr.Zero) { 
      data.text = ""; 
      lock(data) { Monitor.Pulse(data); } 
      return; 
     } 

     data.MainWindowHandle = p.MainWindowHandle; 
     AutomationElement appRoot = AutomationElement.FromHandle(p.MainWindowHandle); 
     data.appRoot = appRoot; 

     if (appRoot == null) { 
      data.text = ""; 
      lock(data) { Monitor.Pulse(data); } 
      return; 
     } 

     AutomationElement treeView = appRoot.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.ClassNameProperty, data.className)); 
     if (treeView == null) { 
      data.text = ""; 
      data.treeViewNotFound = true; 
      lock(data) { Monitor.Pulse(data); } 
      return; 
     } 

     data.TreeViewHandle = new IntPtr(treeView.Current.NativeWindowHandle); 
     data.count = SendMessage(data.TreeViewHandle, TVM_GETCOUNT, 0, 0); 

     RECT rect = new RECT(); 
     GetWindowRect(data.TreeViewHandle, out rect); 

     // making the window really large makes it so less calls to FindAll are required 
     MoveWindow(data.TreeViewHandle, 0, 0, 800, 32767, false); 
     int TV_FIRST = 0x1100; 
     int TVM_SELECTITEM = (TV_FIRST + 11); 
     int TVGN_CARET = TVGN_CARET = 0x9; 

     // if a vertical scrollbar is detected, then scroll to the top sending a TVM_SELECTITEM command 
     var vbar = treeView.FindFirst(TreeScope.Subtree, new PropertyCondition(AutomationElement.NameProperty, "Vertical Scroll Bar")); 
     if (vbar != null) { 
      SendMessage(data.TreeViewHandle, TVM_SELECTITEM, TVGN_CARET, 0); // select the first item 
     } 

     StringBuilder sb = new StringBuilder(); 
     Hashtable ht = new Hashtable(); 

     int chunk = 0; 
     while (true) { 
      bool foundNew = false; 

      AutomationElementCollection treeViewItems = treeView.FindAll(TreeScope.Subtree, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.TreeItem)); 
      if (treeViewItems.Count == 0) 
       break; 

      if (ht.Count == 0) { 
       chunk = treeViewItems.Count - 1; 
      } 

      foreach (AutomationElement ele in treeViewItems) { 
       try { 
        String n = ele.Current.Name; 
        if (!ht.ContainsKey(n)) { 
         ht[n] = n; 
         foundNew = true; 
         data.lines++; 
         sb.AppendLine(n); 
        } 
       } catch {} 
      } 

      if (!foundNew || data.lines == data.count) 
       break; 

      int x = Math.Min(data.count-1, data.lines + chunk); 
      SendMessage(data.TreeViewHandle, TVM_SELECTITEM, TVGN_CARET, x); 
     } 

     data.text = sb.ToString(); 
     MoveWindow(data.TreeViewHandle, rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top, false); 
     lock(data) { Monitor.Pulse(data); } 
    } 

    // this program expects to be launched from Visual Studio 
    // alternative approach is to look for "Microsoft Visual Studio" in main window title 
    // but there could be multiple instances running. 
    private static Process GetParentProcess() { 
     // from thread: http://stackoverflow.com/questions/2531837/how-can-i-get-the-pid-of-the-parent-process-of-my-application 
     int myId = 0; 
     using (Process current = Process.GetCurrentProcess()) 
      myId = current.Id; 
     String query = String.Format("SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {0}", myId); 
     using (var search = new ManagementObjectSearcher("root\\CIMV2", query)) { 
      using (ManagementObjectCollection list = search.Get()) { 
       using (ManagementObjectCollection.ManagementObjectEnumerator results = list.GetEnumerator()) { 
        if (!results.MoveNext()) return null; 
        using (var queryObj = results.Current) { 
         uint parentId = (uint) queryObj["ParentProcessId"]; 
         return Process.GetProcessById((int) parentId); 
        } 
       } 
      } 
     } 
    } 

    [DllImport("user32.dll")] 
    private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); 

    [StructLayout(LayoutKind.Sequential)] 
    private struct RECT { 
     public int Left; 
     public int Top; 
     public int Right; 
     public int Bottom; 
    } 
} 
} 
+0

Esto es impresionante. Gracias por publicarlo No puedo esperar para probarlo. –

+0

@ Mr.Putty ¿Te funcionó? – Loathing

+0

Esto funcionó muy bien para mí. Tuve que agregar "System.Management" para VS 2013. – mojo

-1

De mi experiencia previa y algunas pruebas que acabo de hacer, no hay una función incorporada para hacer esto.

¿Por qué quieres hacer esto? ¿Por qué quieres copiar todas las referencias al portapapeles? Según entiendo, la velocidad de estas características haría que tener una copia estática de todas las referencias fuera relativamente inútil si puedes generar una copia dinámica y completa rápidamente.

Siempre puede ampliar Visual Studio para agregar esta funcionalidad, consulte this post en egghead cafe.

+3

No puedo decir que estas búsquedas se completen rápidamente para mí. Tengo una solución bastante grande con múltiples proyectos interdependientes y la búsqueda de símbolos lleva muchos minutos (y no, mi máquina no es un perro total). Además, la salida contiene varios cientos de líneas, lo que es algo engorroso. Desafortunadamente, estas búsquedas son más inteligentes que cualquier cosa que podría hacer fácilmente de forma manual y también me gustaría poder comparar el resultado de varias búsquedas similares. Tal vez estoy de mal humor hoy, pero para mí esto realmente parece ser una característica que falta. –

+0

He estado jugando con la extensibilidad en Visual Studio un poco últimamente ya pesar de los mediocres tutoriales y la documentación, puedes hacer muchas cosas interesantes para extender visual studio, y comenzar un add-on es un mago. Te animo a agregar esto, ya que no debería ser tan difícil de hacer. – user142350

1

Tenía el mismo requisito y estaba resolviendo esto mediante el uso de una herramienta de captura de pantalla llamada Hypersnap que también tiene algunas funciones básicas de OCR.

2

Lo resolví utilizando Macro Express. Tienen una prueba gratuita de 30 días que utilicé porque es algo único para mí. Escribí una macro simple para copiar todos los Find Find Results, una línea a la vez, en un documento de Notepad.

Secuencia: * Repetir (x) veces (sin embargo el símbolo de muchos resultados que ha) * Activar Buscar símbolo ventana Resultados * .5 segundos de retardo * simulan las teclas "flecha hacia abajo" * copia del portapapeles * Activar el Bloc de notas ventana * .5 segundos de retardo * Portapapeles pegar * Simular pulsaciones de tecla "ENTER" * Fin de repetición

+0

Alternativas de código abierto: [AutoHotKey] (http://www.autohotkey.com/), [Sikuli] (http://www.sikuli.org/). –

+0

@Sikuli - ¡Me encanta AutoHotKey! Bueno, yo también lo odio, pero resuelve muchos problemas. Agregaré esto a mi larga lista de cosas por hacer para automatizar con AHK, pero creo que Microsoft nos está dejando al margen. –

1

Si puede codificar el símbolo como una expresión de búsqueda global, a continuación, copiar y pegar todos los resultados de la ventana resultados de búsqueda es fácil.

por ejemplo, encontrar todas las referencias de la propiedad 'foo' que podría hacer una búsqueda global de '.foo'

+2

Sí, esto es cierto, pero creo que todas las referencias parecen ser un poco más inteligentes que yo, y a veces realmente quiero su poder. Me entristece que las ventanas normales de "Buscar resultados" tengan un comando de copia mientras que la ventana "Buscar resultados de símbolos" no. –

+0

esto no es una mala solución, especialmente si usa expresiones regulares (pero aún sería mejor si pudiera copiar todo fuera de la ventana del símbolo de búsqueda) – TooTone

-1

Hey De alguna manera se puede lograr esto de otra manera,

Justo 'FindAll' el texto seleccionado y usted será capaz de atrapar todas las líneas

0

código de Visual Studio funciona como un navegador. es posible abrir las herramientas de desarrollo y la búsqueda de esa parte del código

Menú: Ayuda> Activar Herramientas para desarrolladores

escribir las siguientes instrucciones a la consola de herramientas de desarrollo:

var elementos = document.getElementsByClassName("plain match") 
console.log(elementos.length) 
for(var i = 0; i<elementos.length;i++) console.log(elementos[i].title) 

y puedes ver los resultados de los partidos.

Ahora bien, si usted puede copiar los resultados

screen shot

0

que tenía el mismo problema. Tuve que hacer una lista de todas las ocurrencias de un cierto método y parte de su versión sobrecargada.

Para resolver mi problema usé ReSharper. (ReSharper -> Find -> Find Usages Advanced).

También tiene una muy buena función de exportación de texto tabulado.

Cuestiones relacionadas