2012-07-18 5 views
0

Visual C#에서는 여러 텍스트 상자 (한 번에 하나씩)에서 텍스트를 가져 와서 메모장에 붙여 넣으려고합니다. 클립 보드에 복사하고 alt-tabbing 한 다음 메모장에 붙여 넣으면 다른 텍스트 상자가 다시 나타납니다. 이 코드는이 아이디어를 나타냅니다C# 복사하여 여러 번 작동하지 않습니다.

subBox1.SelectAll(); 
subBox1.Copy(); 
SendKeys.Send("%{TAB}"); // alt+tab 
SendKeys.Send("^v");  // paste 
SendKeys.Send("{TAB}");  // tab 

subBox2.SelectAll(); 
subBox2.Copy(); 
SendKeys.Send("^v"); 
SendKeys.Send("{TAB}"); 

subBox3.SelectAll(); 
subBox3.Copy(); 
SendKeys.Send("^v"); 
SendKeys.Send("{TAB}"); 

을 당신이 볼 수 있듯이, 세 개의 텍스트 상자에서이 복사 및 붙여 넣기 (subBox1, 2,라는 3). 그러나 어떤 이유로 텍스트 상자의 마지막 내용 만 복사됩니다. 세 번째 상자를 주석 처리하면이 문제가 발생합니다.이 경우 두 번째 텍스트 상자의 내용 만 복사됩니다. 나는 SelectAll()과 Copy()를 사용해 보았는데, 클립 보드 클래스뿐만 아니라 여기에서도 보았다. 둘 다 똑같은 문제가 있습니다.

예를 들어 텍스트 상자의 내용이 각각 "asdf", "qwer"및 "zxcv"이면 "zxcv"가 세 번 표시됩니다.

왜 이런 일이 발생하는지 알고 싶습니다. 나는 약 1 시간 동안 이것에 붙어 있었고, 무슨 일이 벌어지고 있는지 전혀 모른다.

감사합니다.

답변

2

SendKeys는 다른 응용 프로그램이 보내는 키를 처리 할 때까지 기다리지 않으므로 메모장에서 키 누르기가 처리 될 때까지 프로그램에서 다른 텍스트의 맨 위에 이미 subBox3의 텍스트를 복사했습니다.

대신 SendWait을 사용해야합니다. 좀 더 정확한 결과를 위해 sendMessage 첨부를 사용하는 거라고

[DllImport("user32.dll", SetLastError = true)] 
static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 

[DllImport("user32.dll")] 
[return: MarshalAs(UnmanagedType.Bool)] 
static extern bool SetForegroundWindow(IntPtr hWnd); 

// ... 

SetForegroundWindow(FindWindow(null, "Untitled - Notepad")); 
+0

우, 감사합니다. 나는 그 기회를 줄거야! – MattM

+0

흠, 이제 alt-tab은 가지 않습니다. 나는 alt-tab을 그냥 "보내기"로 유지하려고 시도했다. 그것이 왜 그렇게 작동하지 않는지 어떤 생각? – MattM

+0

alt-tab에 대해 SendKeys를 사용하는지 잘 모르겠습니다. 다른 앱으로 전환 할 수있는 기본 제공 방법이 없습니다. FindWindow + SetForegroundWindow 네이티브 호출을 사용합니다. – Blorgbeard

0

:

뿐만 아니라, 대신 Alt + Tab 키를 보내는, 다음과 같이 사용할 수 있습니다. SendMessage를 사용하려면 먼저 메모장의 텍스트 영역에 대한 유효한 창 핸들이 필요합니다. 이 방법은 다양한 방법으로 수행 할 수 있지만 단순한 하위 조회 기능을 사용하는 것을 선호합니다. 자식 창 조회에 지금

using System; 
using System.Diagnostics; 
using System.Runtime.InteropServices; 
using System.Collections.Generic; 
using System.Text; 

//pinvoke 
[DllImport("user32.dll", CharSet = CharSet.Auto)] 
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); 

[DllImport("user32.dll")] 
[return:MarshalAs(UnmanagedType.Bool)] 
private static extern bool GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); 

[DllImport("user32.dll")] 
private static extern int GetWindowTextLength(IntPtr hWnd); 

[DllImport("user32.dll")] 
[return:MarshalAs(UnmanagedType.Bool)] 
private static extern bool EnumChildWindows(IntPtr hParent, delChildWndProc callback, IntPtr lpParam); 
//delegate callback for EnumChildWindows: 
[return:MarshalAs(UnmanagedType.Bool)] 
private delegate bool delChildWndProc(IntPtr hWnd, IntPtr lParam); 

:

는 다음과 같은 네임 스페이스 수입의 PInvoke 선언이 필요합니다. 기본적으로 FindWindowEx와 비슷하게 작동하지만, 필자는 직접 작성하고 싶다.

private static List<IntPtr> FindAllWindows(IntPtr hParent, string className, int maxdepth = 0) 
{ 
    var lookup = new WindowLookup(className, maxdepth); 
    var gcAlloc = GCHandle.Alloc(lookup); 
    try 
    { 
     LookupChildProc(hParent, GCHandle.ToIntPtr(gcAlloc)); 
    } 
    finally 
    { 
     if (gcAlloc.IsAllocated) 
      gcAlloc.Free(); 
    } 
    return lookup.MatchedChildren; 
} 

private static bool LookupChildProc(IntPtr hChild, IntPtr lParam) 
{ 
    var handle = GCHandle.FromIntPtr(lParam); 
    WindowLookup lookup = null; 
    if (handle.IsAllocated && (lookup = handle.Target as WindowLookup) != null) 
    { 
     if (lookup.Depth < lookup.MaxDepth || lookup.MaxDepth == 0) 
     { 
      lookup.Depth++; 
      var builder = new StringBuilder(256); 
      if (GetClassName(hChild, builder, builder.Capacity) && builder.ToString().ToLower() == lookup.LookupName.ToLower()) 
       lookup.MatchedChildren.Add(hChild); 
      EnumChildWindows(hChild, LookupChildProc, lParam); 
     } 
    } 
    return true; 
} 

당신도 이러한 기능의 구현에 대해 걱정할 필요가 없습니다 : 다음과 같은 기능이 모든 일을 다음

private class WindowLookup 
{ 
    public string LookupName { get; private set; } 
    public List<IntPtr> MatchedChildren { get; private set; } 
    public int Depth { get; set; } 
    public int MaxDepth { get; set; } 

    public WindowLookup(string lookup, int maxdepth) 
    { 
     this.MatchedChildren = new List<IntPtr>(); 
     this.LookupName = lookup; 
     this.MaxDepth = maxdepth; 
     if (this.MaxDepth > 0) 
      this.MaxDepth++; //account for the depth past the parent control. 
     this.Depth = 0; 
    } 
} 

그리고 :이 호출 사이의 정보를 설명하기 위해 다음과 같은 래퍼 클래스를 사용하여 많이, 그들은 그대로 일할 것입니다. 중요한 점은 이러한 함수를 사용하면 notepadEdit 창 (사용자가 입력하는 텍스트 영역)에 대한 핸들을 매우 쉽게 찾을 수 있다는 것입니다. 내가 코드를 왼쪽 이제

var notepads = Process.GetProcessesByName("notepad"); 
if (notepads.Length > 0) 
{ 
    foreach(var notepad in notepads) //iterate through all the running notepad processes. Of course, you can filter this by processId or whatever. 
    { 
     foreach(var edit in FindAllWindows(notepad.MainWindowHandle, "Edit")) 
     { 
      //next part of the code will go here, read on. 
     } 

    } 
} 

는 시간에 실행하는 각 메모장 프로세스의 "편집"창을 통해 루프의 중간에 있었다. 이제는 유효한 윈도우 핸들을 가지고 있으므로 SendMessage를 사용하여 보낼 수 있습니다. 특히, 텍스트 추가.(내가 코멘트를 넣어 경우)

지금
private static void AppendWindowText(IntPtr hWnd, string text) 
{ 
    if (hWnd != IntPtr.Zero) 
    { 
     //for your reference, 0x0E (WM_GETTEXTLENGTH), 0xB1 (EM_SETSEL), 0xC2 (EM_REPLACESEL) 
     int len = SendMessage(hWnd, 0x000E, IntPtr.Zero, IntPtr.Zero).ToInt32(); 
     var unmanaged = Marshal.StringToHGlobalAuto(text); 
     SendMessage(hWnd, 0x00B1, new IntPtr(len), new IntPtr(len)); 
     SendMessage(hWnd, 0x00C2, IntPtr.Zero, unmanaged); 
     Marshal.FreeHGlobal(unmanaged); 
    } 
} 

우리는 우리의 AppendWindowText 기능을 가지고, 위의 중첩 루프에서에 함수 호출을 추가 할 수 있습니다 : 나는 원격 제어에 텍스트를 추가 처리하기 위해 다음과 같은 기능을 썼다 :

AppendWindowText(edit, "Some text here"); 

그리고 거기에 있습니다. 이것은 약간의 말투적 인 반응이지만, 결국이 방법은 SendKeys를 사용하고 창에 집중하는 것보다 훨씬 더 안정적입니다. 자신의 응용 프로그램에 집중할 필요가 없습니다.

질문이 있으시면 언제든지 말씀해 주시면 최대한 대답하겠습니다.

건배,
J

편집 : 일부 참조 :

SendMessage function (MSDN)
EnumChildWindows function (MSDN)
Appending text using SendMessage

+0

시간 내 주셔서 감사합니다. 불행히도 이것은 약간 압도적입니다. 이것은 Visual C# 및 Windows API 유형 항목을 사용하는 첫 번째 날입니다. 나는 방금 발이 젖어있는 아주 간단한 프로젝트라고 생각한 것을 시작했다. 그러나 ... 창 초점 설정과 관련된 모든 문제로 인해 나는 지금 당장 머리를 쓰고있는 것 같아요. – MattM

+0

C++에서 Win32 폼 프로그래밍을 다룬 적이 없다면, SendMessage는 꽤 소수 일 수 있습니다. 취할 수있는 하나의 대안은'AppActivate'를 사용하여 포커스 윈도우를 설정 한 다음 붙여 넣기를하는 것입니다. 원한다면 위의 코드를 사용하여 "AppendTextToNotepad (text)"라고 부를 수있는 일회용 함수를 작성하여 여가 시간에 다른 코드를 배울 수 있지만 여전히 작동하고 안정적인 응용 프로그램을 사용할 수 있습니다. –

+0

구현하기 쉽다면 멋질 것입니다! 나는이 시점에서 내가 아직 완전히 이해하지 못하는 코드를 어디서 붙일 지 확신하지 못한다고 생각한다. 하하. 어디로 가는지 알려주세요. 다시 한번 감사드립니다. – MattM

관련 문제