성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Get Started with Milvus Vector DB i...
[정성태] cyberark/PipeViewer - A tool that...
[정성태] WinForms in a 64-Bit world – our st...
[정성태] 예제에서 SELECT_SQL도 내부적으로는 SqlCommand/...
[victor] SELECT_LINQ SELECT_SQL 같은 쿼리인...
[victor] 답변 갑사합니다. 예외(Exception)가 났습니다. ...
[정성태] 일단, 위의 방식대로 하면 예외(Exception) 없이 잘 동...
[정성태] Windows 10 (버전 1809)에 이런 기능이 ^^ 추가되...
[정성태] pde windbg extension ; https://lea...
[정성태] // GetEnumerator extensions for Ran...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C#/Windows - Clipboard(Ctrl+C, Ctrl+V)가 동작하지 않는다면?</h1> <p> ctrl+c, ctrl+v가 어느 순간부터 동작하지 않았을 때, 다음과 같이 명령행에서 clip을 사용해 보면 "ERROR: Access is denied"인 경우를 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> <span style='color: blue; font-weight: bold'>dir | clip</span> ERROR: Access is denied. </pre> <br /> 저 오류 메시지는 권한이 부족해서 발생하는 것이 아니고 clipboard 자원이 현재 다른 프로세스에 의해 잠겨 있기 때문일 수 있습니다. 실제로 한번 재현을 해볼까요? ^^<br /> <br /> C#으로 다음과 같이 간단하게 프로그램을 만들고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [DllImport("user32.dll", SetLastError = true)] static extern bool CloseClipboard(); [DllImport("user32.dll", SetLastError = true)] static extern bool OpenClipboard(IntPtr hWndNewOwner); bool _opened = false; void Button1_Click(object sender, EventArgs e) { <span style='color: blue; font-weight: bold'>_opened = OpenClipboard(this.Handle);</span> this.Text = _opened.ToString(); } void Button2_Click(object sender, EventArgs e) { if (_opened == true) { <span style='color: blue; font-weight: bold'>CloseClipboard();</span> } } </pre> <br /> Button1만 눌러 Clipboard 자원을 Open 한 다음, 명령행에서 clip을 실행하면 "ERROR: Access is denied." 오류가 그대로 재현되는 것을 볼 수 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그래서, ctrl+c/ctrl+v가 동작하지 않는 경우 Clipboard 자원을 Open만 하고 Close를 못 시킨 응용 프로그램을 종료하면 다시 정상적으로 동작하게 됩니다. 문제는? 도대체 어떤 프로그램이 소유하고 있는지 알 수 없으므로 현재 실행 중인 프로세스들을 임의로 모두 종료하는 식으로 대응해야 합니다.<br /> <br /> 명색이 프로그래머인데 ^^ 그러면 너무 없어 보이므로 간단하게 코드로,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > How to check which application has the clipboard hold? ; <a target='tab' href='https://superuser.com/questions/770476/how-to-check-which-application-has-the-clipboard-hold'>https://superuser.com/questions/770476/how-to-check-which-application-has-the-clipboard-hold</a> </pre> <br /> 작성해 명확하게 알아내는 것도 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [DllImport("user32.dll")] static extern IntPtr GetOpenClipboardWindow(); [DllImport("user32.dll", SetLastError = true)] static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); ...[생략]... { IntPtr pClipboardOwner = GetOpenClipboardWindow(); if (pClipboardOwner == IntPtr.Zero) { return; } GetWindowThreadProcessId(pClipboardOwner, out uint dwProcessId); Process owner = Process.GetProcessById((int)dwProcessId); string name = owner.ProcessName; string mainModule = owner.MainModule.FileName; } </pre> <br /> 일단 GetWindowThreadProcessId Win32 API까지의 호출은 문제없는데, 이후의 Process 타입을 통해 정보를 알아오는 경우 호출 측과 대상 프로세스의 플랫폼 차이(x86/x64)가 발생하면 오류가 뜹니다. 이런 경우 해당 코드만 실행해 그 결과를 반환하는 간단한 EXE를 x86과 x64로 각각 빌드해 실행하는 것도 가능하겠지만, 그보다는 간단하게 WMI 쿼리를 사용하는 것도 나쁘지 않습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > { // ...[생략]... GetWindowThreadProcessId(pClipboardOwner, out uint dwProcessId); textBox1.Text = $"{dwProcessId}, " + GetProcessInfo(dwProcessId); } string GetProcessInfo(uint processId) { string arg = $"process where \"ProcessID={processId}\" get ExecutablePath"; return GetOutput("wmic", arg); } private static string GetOutput(string executable, string arguments) { ProcessStartInfo psi = new ProcessStartInfo(); // wmic를 사용하지 않고 System.Management.dll을 이용해 결과를 반환하는 것도 가능 psi.FileName = executable; psi.Arguments = arguments; psi.RedirectStandardOutput = true; psi.UseShellExecute = false; psi.CreateNoWindow = true; Process proc = Process.Start(psi); string txt = proc.StandardOutput.ReadToEnd(); return txt; } </pre> <br /> <hr style='width: 50%' /><br /> <br /> 참고로, OpenClipboard에 윈도우 핸들 값이 아닌 null을 주는 것도 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > OpenClipboard(IntPtr.Zero); </pre> <br /> 위와 같은 방식으로 클립보드를 잠근 경우에는 다른 프로세스에서라도 CloseClipboard를 호출하면 풀리게 됩니다. 물론, 윈도우 핸들을 넘긴 경우에는 무조건 그 윈도우를 소유한 프로세스에서 CloseClipboard를 호출해야 합니다. (또는 종료하든가.)<br /> <br /> <a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1493&boardid=331301885'>첨부 파일은 이 글의 소스 코드와 실행 파일(WindowsFormsApp1.exe)</a>을 담고 있습니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1633
(왼쪽의 숫자를 입력해야 합니다.)