성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
글쓰기
제목
이름
암호
전자우편
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# - (핸들을 이용하여) 모든 열린 파일을 열람</h1> <p> 아시는 것처럼, .NET Framework에서는 기본적으로 프로세스에 열려진 '핸들' 목록을 얻을 수 있는 방법이 없습니다. 사실, 저수준의 핸들이라는 개념 자체가 닷넷에서는 감춰지므로 이해가 됩니다.<br /> <br /> 대신, P/Invoke를 이용하면 C/C++에서 했던 것과 동일하게 구현을 할 수 있습니다. 마침, Codeplex에서 이와 관련된 소스 코드를 찾아냈는데요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > DetectOpenFiles.cs - CodePlex ; https://vmccontroller.svn.codeplex.com/svn/VmcController/VmcServices/DetectOpenFiles.cs </pre> <br /> 한 가지 아쉬운 점이라면, 해당 코드가 32비트에서만 동작할 뿐 64비트에서 실행하면 다음과 같은 예외를 발생시킵니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > System.OverflowException was unhandled by user code Message=<span style='color: blue; font-weight: bold'>Arithmetic operation resulted in an overflow.</span> Source=mscorlib StackTrace: at System.IntPtr.op_Explicit(IntPtr value) at WebApplication1.DetectOpenFiles.OpenFiles.<GetEnumerator>d__0.MoveNext() in d:\...\WebApplication1\DetectOpenFiles.cs:line 273 at WebApplication1.WebForm1.Page_Load(Object sender, EventArgs e) in d:\...\WebApplication1\WebForm1.aspx.cs:line 19 at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) at System.Web.UI.Control.OnLoad(EventArgs e) at System.Web.UI.Control.LoadRecursive() at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) InnerException: </pre> <br /> 다행히 64비트 포팅이 어렵지는 않습니다. 우선, SYSTEM_HANDLE_ENTRY에서 포인터 값이 기존에는 int로 표현되어 있는 것을 IntPtr로 수정해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [StructLayout(LayoutKind.Sequential)] private struct SYSTEM_HANDLE_ENTRY { public int OwnerPid; public byte ObjectType; public byte HandleFlags; public short HandleValue; <span style='color: blue; font-weight: bold'>public IntPtr ObjectPointer;</span> public int AccessMask; } </pre> <br /> 그 외, handlCount를 읽어들이는 부분에서 4byte가 아닌 8byte를 읽어야 하고,<br /> <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > long handleCount = 0; if (IntPtr.Size == 4) { handleCount = Marshal.ReadInt32(ptr); } else { handleCount = Marshal.ReadInt64(ptr); } </pre> <br /> 포인터 처리에 대해서 "(int)ptr"와 같은 표현을 "ptr.ToInt64()"와 같이 바꿔야 합니다.<br /> <br /> 기타 결정적인 차이점이, "GetFileNameFromHandle" 메서드 안에서 발생하는데요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private static bool GetFileNameFromHandle(IntPtr handle, out string fileName) { ...[생략]... NT_STATUS ret = NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length); if (ret == NT_STATUS.STATUS_BUFFER_OVERFLOW) { ...[생략]... ret = NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length); } if (ret == NT_STATUS.STATUS_SUCCESS) { fileName = <span style='color: blue; font-weight: bold'>Marshal.PtrToStringUni((IntPtr)((int)ptr + 8), (length - 9) / 2);</span> return fileName.Length != 0; } ...[생략]... } </pre> <br /> 위에서 파일 이름을 가져오는 포인터 옵셋값(+8)이 64비트에서는 바뀐다는 점입니다. 정확한 위치를 알기 위해 64비트로 해당 프로그램을 실행시킨 후 ptr.ToInt64() 값을 알아내야 합니다. 디버거의 "Watch" 창을 이용하여 알아낸 값이 "0x00000000022b0930"라고 가정하고 진행했을 때, 이 값을 메모리 윈도우(Ctrl+D,Y) 창에서 확인해 봅니다. 제 경우에는 다음과 같이 나왔습니다.<br /> <br /> <img alt='list_all_file_handle_1.png' src='/SysWebRes/bbs/list_all_file_handle_1.png' /><br /> <br /> 보시는 것처럼, 일정 바이트 후에 "\Device\HarddiskVolume2\Windows\assembly\pubpol60.dat"라는 (유니코드) 문자열이 확인되는데, 시작 바이트가 16바이트 이후임을 계산해 낼 수 있습니다. 최종적으로 이를 반영하면 다음과 같은 코드가 나옵니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > if (IntPtr.Size == 4) { fileName = Marshal.PtrToStringUni((IntPtr)((int)ptr + 8), (length - 9) / 2); } else { IntPtr namePtr = new IntPtr(<span style='color: blue; font-weight: bold'>ptr.ToInt64() + 16</span>); fileName = Marshal.PtrToStringUni(namePtr); } </pre> <br /> 옵셋값은 테스트 결과 다행히 Windows 2003부터 Windows 8까지 전혀 변함없이 사용할 수 있었습니다. 하지만, 코드를 실행하는 권한에 따라서는 범위가 달라지므로 사용 시 이를 감안해야 합니다.<br /> <br /> 첨부한 프로젝트는 위의 코드를 테스트할 수 있는 간략한 윈폼 프로젝트입니다.<br /> <br /> <img onclick='toggle_img(this)' class='imgView' alt='list_all_file_handle_2.png' src='/SysWebRes/bbs/list_all_file_handle_2.png' /><br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1444
(왼쪽의 숫자를 입력해야 합니다.)