A, B 두 프로그램이 있다.

A에서 짧은 경로를 사용하여 B를 실행하면 B에서 호출한 GetModuleFileName 함수는 짧은 경로를 반환한다.


The string returned will use the same format that was specified when the module was loaded. Therefore, the path can be a long or short file name, and can use the prefix "\\?\".


http://msdn.microsoft.com/en-us/library/windows/desktop/ms683197(v=vs.85).aspx

블로그 이미지

란마12

,

자식 컨트롤이 겹칠 때 특정 컨트롤을 맨앞으로 오게 하고 다른 컨트롤과 이벤트나 UI가 겹쳐지지 않게 하는 방법


겹쳐지는 관련 컨트롤 모두에 WS_CLIPSIBLINGS | WS_CLIPCHILDREN 스타일 추가

BringWindowToTop함수 호출

블로그 이미지

란마12

,

DllMain이 언제, 왜 호출되는지에 대하여 알아보자.

DllMain의 정의는 아래와 같다.

-------------------------- DllMain의 원형 -------------------------------

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);

----------------------------------------------------------------------

1. HINSTANCE hinstDll
   - DLL이 가상주소에 매핑되었을 때의 핸들이다. WinMain의 hInstance 와 동일한 셩격을 갖고있다.
   - IMAGE_OPTIONAL_HEADER 의 ImageBase 필드에 지정된 값이다.  보통 디폴트로 0x10000000 번지에 로드된다.

2. DWORD fdwReason
   - DllMain 이 호출한 이유를 나타낸다.
   - fdwReason 의 값으로는
     -> DLL_PROCESS_ATTACH : DLL이 프로세스의 주소 공간에 최초로 매핑됬을때

     -> DLL_PROCESS_DETACH : DLL이 프로세스의 주소 공간으로 부터 해제될 때

     -> DLL_THREAD_ATTACH : 어떤 프로세스 내에서 스레드가 생성될 때 해당 프로세스 주소공간에 매핑되어 있는 모든 DLL의 파일이미지검사하여 DLL_THREAD_ATTACH 로 호출

     -> DLL_THREAD_DETACH : 스레드 엔트리 포인트 함수로부터 리턴될때 시스템은 ExitThread를 호출한다 이때 스레드를 바로 없애지 않고 매핑되어 있는 모든 DLL의 DllMain에 DLL_THREAD_DETACH를 호출

-----------------------------------------------------------------------------------------------------------------------------------------

DllMain을 정의할 때 주의할 점이 있는데 DllMain을 호출할때 동기화를 맞추어야 한다.

예를 들어 스레드가 생성될 때 DLL_THREAD_ATTACH 를 매개 변수로 하여 DllMain이 호출된다. 이때 다른 스레드가 종료되는 상황이라면

DllMain이 동시에 두 개의 스레드에 대하여 호출되는 상황이 발생할 것앋. 이 경우 동기화 문제를 고려할 필요가 없도록 시스템이 알아서

앞에 호출된 DllMain의 실행이 끝날 때까지 DllMain의 실행을 대기시키게 된다. 이런 상황 때문에 DllMain 내에서 대기 함수(WaitForXXX 함수군들)를 호출할 때 신중을 기해야 한다.

아니 이런 상황이 발생하지 않도록 코딩해야 한다.  제프리 책 Programming Applications for Microsoft Windows 2000에 이런 상황이 발생할 수 있는 문제점에 대한 예제가 나오는데

확인해보자

------------------------------------- 데드락이 발생하는 DllMain의 예) ----------------------------------------

BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, PVOID lpvReserved)
{
 HWND hThread;
 DWOIRD dwThreadld;

 switch(fdwReason)
 {
 case DLL_PROCESS_ATTACH:
 hThred = CreateThread(NULL,0,SomeFunction,NULL,0,&dwThreadld); // 스레드하나 생성
 
  WaitForSingleObject(hThread, INFINITE);  // 스레드가 끝날때 까지 DllMain 실행을 대기 시킨다.

  CloseHandle(hThread);
 break;

 case DLL_PROCESS_DETACH:
 break;

 case DLL_THREAD_ATTACH:
 break;

 case DLL_THREAD_DETACH:
 break;

 }

}

----------------------------------------------------------------------------------------------------

위 코드를 보면 Dll 로드시에 호출되는 DllMain에서 스레드를 하나 생성하였다.

이 스레드가 종료할때까지 메인 스레드를 대기하는 코드다

이 프로그램은 데드락 상태에 빠지게 되는데  메인스레드는 SomeFuction을 실행하는 스레드를 생성키기고

WaitForSingleObject 를 통해 SomeFuction 의 실행이 종료될 때까지 대기한다.

하지만 여기서 문제가 되는것은 스레드 생성시에도 DLL_THREAD_ATTACHG를 매개변수로 하여 DllMain이 호출된다는 것이다.

그리고 앞서 언급한 것처럼 이미 DLL_PROCESS_ATTACH를 매개 변수로 하여 메인 스레드에 의해 DllMain이 호출 중이기 때문에

시스템은 메인 스레드가 실행 중인 DllMain 실행이 끝날때까지 SomFunction을 실행하는 스레드를 위한

DllMain 실행을 대기시킨다. 이 상황, 즉 메인 스레드에 의한 DllMain 실행은 SomeFunction 을 실행하는 스레드가 종료되기를 기다리고 있으며

SomeFuction 을 실행할 스레드는 SomeFunction을 실행하기 전에 DllMain을 호출하기 위하여 메인 스레드에 의한 DllMain의 호출이 끝나기를 기다리고 있다.

서로가 서로를 끝나기만 기다리고 있는 것이다. 이런 실수를 범하지 않도록 DllMain정의 시에 신중을 기해야 한다.

 


블로그 이미지

란마12

,
이 글은 Code Project에 있는 Timers Tutorial을 정리한 것입니다.

우선, 윈도우는 윈도우 CE를 제외하면 리얼타임OS가 아닌 관계로 10ms와 같은 매우 작은 시간에 대한 정확한 처리를 요구하는 건 한계가 있답니다. (쥔장도 자세한 이유는 잘... 흠...) QueryPerformanceFrequency나 QueryPerformanceCounter와 같은 경우는 특정 시간 경과 후 이벤트를 처리하는 것이 주요 쓰임이 아니라 시간과 시간사이의 시간차를 계산하는 것이 주요 쓰임인 관계로 이 글에서는 언급되어 있지 않습니다. 반대로 말하면 시간과 시간사이의 차이를 정확히 측정하려면 QueryPerformanceFrequency를 사용하여야 겠죠?

1. Standard Win32 Timer (표준 윈32 타이머)
 Win32 UI프로그래밍 처음 배우면 사용하는 WM_TIMER 메시지 관련된 API를 사용하는 것입니다. 시간 정확도가 떨어지지만 몇분에 한번과 같이 느슨하게 처리할 때 사용하면 좋습니다. 처리방식은 Win32의 윈도우 메시지 큐를 사용하며 callback 함수를 이용하는 경우는 WM_TIMER 메시지를 큐에서 끄내는 대신 함수를 콜하는 방식이라 메시지 방식과 큰 차이를 보이지 않습니다. 중요한 점은 WM_TIMER 메시지를 처리하는 동안 UI의 반응성이 나뻐질 수 있다는 것입니다.
  • SetTimer(...)
  • KillTimer(...)

2. Multimedia Timers (멀티미디어 타이머) 
 많이 사용되는 이유는 호환성 때문이며 최근에는 비권장되는 방식입니다. 표준 윈32 타이머보다 정확한 처리를 하며 자신의 쓰레드에서 동작합니다. 이 방식은 짧은 진행시간을 지정하였을 때는 메시지 큐의 보호를 받지 못하므로 조금 더 위험하다는데 자세한 이유가 나와 있지는 않습니다. (지송합니다. 쥔장이 현재 구지 쓸 이유가 없기 때문에 자세한 이유는 찾아보지 않았습니다. 원문에는 관련 블로그 포스트가 링크되어 있으므로 관심 있는 분은 한 번 찾아보기 바랍니다.)
  • timeGetDevCaps(...)
  • timeBeginPeriod(...)
  • timeSetEvent(...)
  • TimeProc(...)
  • timeKillEvent(...)
  • timeEndPeriod(...)

3. Waitable Timers
 적은 CPU 점유율을 가지면 메시지 큐를 가지지 않는 장점이 있지만 호출하는 쓰레드를 블록시키며 alertable상태로 만들어야 합니다. APC(Asynchronous Procedure Call)로 동작합니다.
  • CreateWaitableTimer(...)
  • SetWaitableTimer(...)
  • CancelWaitableTimer(...)

Alerable 관련 함수

  • SleepEx(...)
  • WaitForSingleObjectEx(...)
  • WaitForMultipleObjectsEx(...)
  • MsgWaitForMultipleObjectsEx(...)
  • SignalObjectAndWait(...)

4. Queue Timers
 현재 최고 성능의 타이머 있데 아쉽게도 윈도우2000이후 버전만을 지원합니다. 이 API는 윈도우 쓰레드풀에서 작동하며 굉장히 정확하게 작동합니다. 저자가 강력하게 추천하는 API입니다.
  • CreateTimerQueueTimer(...)
  • WaitOrTimerCallback(...)
  • DeleteTimerQueueTimer(...)


API 선택가이드
1. GUI에서 사용하며, 높은 정확도를 요구하지 않는 경우 표준 윈32 타이머가 좋은 선택입니다.
2. 높은 호환성과 높은 정확도를 원하면 멀티미디어 타이머가 좋은 선택입니다.
3. 윈도우 98/NT4.0 이후 버전만을 대상으로 하며 적은 오버헤드를 가지며 호출쓰레드가 블록되는 것이 괜찮다면 Waitable Timer가 좋은 선택입니다.
4. 윈도우 2K 이후 버전만을 대상으로 하며 적은 호버헤드, 높은 정확성, 논블록킹 타이머를 원한다면 Queue Timer가 좋은 선택입니다. 아마도 MS는 현재 이 Timer를 밀고 있는 듯 싶군요... 흠...

'library > WIN32API' 카테고리의 다른 글

자식 컨트롤 겹칠 때  (0) 2013.11.26
DllMain에서 동기화 문제 주의  (0) 2010.12.16
MBCS 프로젝트 유니코드 전환 가이드  (0) 2010.11.24
프로세스간 동기화 테스트  (0) 2010.11.24
dll LoadLibrary/FreeLibrary  (0) 2010.10.01
블로그 이미지

란마12

,

이미 만들어져 있는 프로젝트를 UNICODE로 전환한다는 것은... 어려운 작업까지는 아니지만 상당한 노가다와 시간을 필요로 하는 일임에는 틀림 없다. 약간의 요령과 정형화된 절차가 있다면 조금이나마 시간과 노력을 절약할 수 있다.

다음은 MBCS로 쓰여진 코드를 UNICODE로 변환하는 순서를 설명한다.
(여기서 말하는 UNICODE란 Generic T-TYPE이다. W-Type 아님... ^^)


1. UNICODE 매크로 선언
Visual Studio (6.0) 실행 -> Project -> C++ 탭에서 Preprocessor definitions: 항목에 _MBCS를 삭제하고 _UNICODE와 UNICODE를 추가

사용자 삽입 이미지


2. UNICODE가 불가한 부분이 있는지 검토
코드 전체를 검토하여 UNICODE로 전환할 수 없는 부분이 있는지 체크한다. 예를 들면 유니코드가 아닌 외부모듈을 사용하는 부분이라던지... 통신 프로토콜 문제로 전환이 불가한 부분, GetProcAddress 와 같이 T-TYPE을 지원하지 않는 API를 호출하는 부분 등이 되겠다. 이런 부분은 억지로 유니코드화 하는 것보다 차라리 ANSI 코드를 내버려 두고 결과 스트링을 UNICODE로 한번 더 변환해서 쓰는게 여러 모로 낫다.
다음의 API를 사용한다.
int WideCharToMultiByte( UINT CodePage, // code page
                     DWORD
dwFlags, // performance and mapping flags
                          LPCWSTR lpWideCharStr, // wide-character string          
                     int cchWideChar, // number of chars in string.
                     LPSTR lpMultiByteStr, // buffer for new string          
                     int cbMultiByte, // size of buffer              
                     LPCSTR lpDefaultChar, // default for unmappable chars
                     LPBOOL lpUsedDefaultChar // set when default char used
);
int MultiByteToWideChar( UINT CodePage, // code page
                          DWORD dwFlags, // character-type options         
                          LPCSTR lpMultiByteStr, // string to map
                          int cbMultiByte, // number of bytes in string
                          LPWSTR lpWideCharStr, // wide-character buffer
                          int cchWideChar // size of buffer
);
제대로 Generic하게 Conversion하려면, UNICODE 전환 불가 부분에 대해 다음과 같이 해줘야 한다.
(... blah~ blah~...)
#ifndef _UNICODE
        lstrcpyn(szBuffer1, szBuffer, _countof(szBuffer1));
#else
        MultiByteToWideChar(CP_ACP, FALSE, szBuffer, nLen, szBuffer1, _countof(szBuffer1)-1);
        szBuffer1[_countof(szBuffer1) - 1] = NULL;
#endif
(... blah~ blah~...)


3. Quoted String 전환
다음과 같이 Quoted String을 전환한다.
"TEXT DATA" --> _T("TEXT DATA")
'C' --> _T('C')

           한꺼번에 전환하려면 다음과 같이 한다. 
   1) Visual Studio에서 ctrl + H 를 눌러 변환 창을 띄운다.
   2) "Regular String"에 체크를 한다.
   3) Find What 에는 Quoted String을 의미하는 "\:q" 를 입력한다.
       (혹은 우측 [▶]를 클릭해 "Quoted String"을 선택하면 입력된다.)
   4) Replace With: 에는 _T(\0) 를 입력한다. (\0 는 발견된 스트링을 의미한다.)
   5) 찾아지는 Quoted String을 확인하면서 Replace 를 시작한다.
       "Replace All"을 해버리고 나서 에러나는 부분을 수정해나가는 방법도 괜찮다.
       (보통 #include 등에서 에러가 난다.)
   6) 소스코드 내의 모든 파일에 대해 1~5를 반복한다. 이 때 위에서 확인된 변환불가 부분은 제외한다.
   7) 에러없이 빌드가 되는지 확인한다.

※ #include "HeaderFile.h" 같은 부분이 #include _T("HeaderFile.h") 로 잘못 전환되지 않도록 조심. ^^;


사용자 삽입 이미지



4. Data Type과 문자열 함수를 T-Type 버젼으로 전환
Data Type과 문자열 함수를 T-Type 버젼으로 전환한다.
이때 API는 100% 대체가 가능한 API로 변환해야 하므로 주의하여 MSDN을 참고하여 진행해야 한다. 예를 들어 strncpy는 _tcsncpy 로 전환해야지 lstrcpyn 으로 전환해서는 안된다. (동작이 미묘하게 다르다. 자세한 내용은 여기 참조) 

      1) Data Type을 전환한다. 
    몇가지 예를 들면 다음과 같다.       
    다음의 항목에 대해서는 울트라에디트 등 텍스트 에디터의 Replace in Files 기능으로 한꺼번에 작업해도 된다. ("완전한 단어" 옵션에 체크하면 실수를 줄일 수 있다.)
"LPSTR" -> "LPTSTR"
"LPCSTR" -> "LPCTSTR"
"PSTR" -> "PTSTR"
"PCSTR" -> "PCTSTR"
    다음의 타입들은 VisualStudio의 Replace기능으로 하나하나 확인하며 파일별로 전환해야 한다.
"CHAR" -> "TCHAR"
"char" -> "TCHAR"

※ 소켓 통신이나 파일 I/O 등에서 CHAR Array를 버퍼로 사용하는 경우는 TCHAR로 전환해선 안된다. (이럴 땐 CHAR Array보다 BYTE array를 쓰는 게 맞다.)
※ 혹시 "unsigned char" 와 같이 쓰여진 코드를 전환하면 이상하게 되므로.. 조심. ㅡ.ㅡ

     2) 문자열 핸들링 함수들을 전환한다.   
문자열 핸들링 함수들이 무지 많아서 다 적을 수는 없고... 대표적인 것 몇가지만 적으면 다음과 같다. 다음의 API들은 Replace in Files로 한꺼번에 작업해도 된다.
"strcpy" -> "_tcscpy"
"strncpy" -> "_tcsncpy"
"strcat" -> "_tcscat"
"strncat" -> "_tcsncat"
"strcmp" -> "_tcscmp"
"strncmp" -> "_tcsncmp"
"strstr" -> "_tcsstr"
"strchr" -> "_tcsstr"
(기타 등등 strXXX 류의 함수들은 대부분 _tcsXXX 로 전환하면 된다.)

"wsprintf" -> "_stprintf"
"_snprintf" -> "_sntprintf"
"printf" -> "_tprintf"
(기타 등등.. 무지 많다.)

※ memset, memcpy 등은 문자열 함수가 아닌 메모리 함수이므로 건드리면 안된다.
※ lstrcpyn 계열의 함수들은 원래 T-Type을 지원하므로 건드릴 필요 없다.
※ 위의 함수들은 인자의 종류와 순서가 완전히 동일하므로 함수 이름만 Replace하면 유니코드로 전환이 된다. 만약 strcpy와 같이 버퍼체크를 안하는 함수를 버퍼체크 하는 버젼으로 변경하고 싶다면... 먼저 _tcscpy로 변환하여 유니코드로 컨버젼을 끝낸 다음에 _tcscpy_s (혹은 _tcsncpy) 로 다시한번 전환하는 것이 낫다.

   3) 문자열 핸들링 함수의 버퍼 사이즈에 sizeof가 사용된 곳이 없는지 체크하고, 있다면 _countof로 변환
문자열 함수에서 버퍼사이즈는 버퍼의 바이트수가 아닌 버퍼의 글자수를 지정해야 하며, memcpy 등 메모리 함수는 글자수가 아닌 바이트수를 지정해야 한다. MBCS일 때는 1글자가 1바이트이므로 sizeof가 문제가 안되지만, 유니코드일 때는 지켜주어야 한다.
VisualStudio 6.0의 경우 _countof를 지원하지 않으므로 다음과 같이 Define해서 사용하면 된다.
#ifndef _countof
#define _countof(X) sizeof(X)/sizeof(X[0])
#endif
_tcsncpy (szBuffer, lpSource, sizeof(szBuffer));  // Wrong!!
_tcsncpy (szBuffer, lpSource, _countof(szBuffer));  // OK!!

memcpy (lpBuffer, lpSource, sizeof(szBuffer)); // OK!
memset (lpBuffer, NULL, _countof(szBuffer)); // Wrong!! _countof가 아닌 sizeof 를 사용~!!



5. 기능테스트를 진행하면서 오류나는 부분을 체크하고 수정
모.. 사람마다 생각이 틀리겠지만... 코드 전환을 꼼꼼히 해봐도 어차피 컨버젼하고 나면 결함투성이고 에러를 다 잡아야 한다. 내생각엔 차라리 코드 전환을 대충(?) 해놓고, 테스트를 꼼꼼하게 하는게 낫지 않을까 싶다. ㅎㅎ

http://kuaaan.tistory.com/59

'library > WIN32API' 카테고리의 다른 글

DllMain에서 동기화 문제 주의  (0) 2010.12.16
Win32 과연 어떤 Timer함수를 사용할 것인가?  (0) 2010.12.14
프로세스간 동기화 테스트  (0) 2010.11.24
dll LoadLibrary/FreeLibrary  (0) 2010.10.01
IOCP  (0) 2010.07.08
블로그 이미지

란마12

,

뮤텍스, 이벤트, 세마포어등등으로 프로세스간 동기화가 가능하다
다음 예제는 뮤텍스를 이용하여 먼저 실행된 프로세스가 종료되기를 3초간 대기 후 결과를 메세지박스로 보여준다.

  HANDLE m_hMutex = CreateMutex(NULL, TRUE, "MTXTEST");
  if (GetLastError() == ERROR_ALREADY_EXISTS){
    DWORD dwRet = WaitForSingleObject(m_hMutex, 3000);
    //error, 뮤텍스를 최초 생성한 프로세스가 해제(신호상태로 만듦)할 수 있다.
    //BOOL bRet = ReleaseMutex(m_hMutex); 
    CloseHandle(m_hMutex);        
    if (dwRet == WAIT_FAILED){
      DWORD dwErr = GetLastError();
      MessageBox(NULL, "mtx err", "", MB_OK);
      return;
    }

    if (dwRet == WAIT_TIMEOUT){
      MessageBox(NULL, "mtx timeout", "", MB_OK);
      return;
    }

    MessageBox(NULL, "mtx terminated", "", MB_OK);
  }else{
    MessageBox(NULL, "mtx first running", "", MB_OK);
    ReleaseMutex(m_hMutex);
    CloseHandle(m_hMutex);
  }

블로그 이미지

란마12

,

같은 dll을 하나의 프로세스에서 두번 로딩하면 어떨까 하고 테스트해봤다.

- 두번째 로드된 dll핸들은 처음 로드된 dll핸들을 참조한다.(참조카운터만 1증가)
- 두번째 dll은 로드될 때 DllMain에서 ATTACH/DETACH관련 이벤트가 발생하지 않는다.
- 두 핸들은 같지만 로드된 모든 dll핸들을 FreeLibrary하기 전까지 free되지 않는다.
  (참조카운터가 0이 되면 free)
블로그 이미지

란마12

,

IOCP

library/WIN32API 2010. 7. 8. 02:25

* 이기탁님이 작성한 문서를 요약함

1. Overlapped IO

 1.1. 넌블러킹, 비동기적으로 IO처리
 1.2. 쓰레드가 Device Driver에게 IO요청 후 신경쓰지 않음
 1.3. Device Driver가 IO감시하면서 완료되면 알려 줌
  1) 이벤트 객체 멤버 이용
  2) 콜백함수
  3) 직접 조회
  4) IOCP
 1.4. IO blocking없음, FIFO로 IO처리하지 않음
 1.5. 소켓생성
      SOCKET sock=WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
 1.6. 송신
  int WSASend(SOCKET s,
        LPWSABUF lpBuffers,
              DWORD dwBufferCount,
              LPDWORD lpNumberOfBytesSent,
              DWORD dwFlags,
              LPWSAOVERLAPPED lpOverlapped,
              LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
  1) IO가 끝나기 전에 lpOverlapped가 메모리에서 사라지면 안됨.
  2) lpCompletionRoutine: 1.3.2)의 콜백함수
  3) 리턴값이 SOCKET_ERROR이면서 WSAGetLastError()==ERROR_IO_PENDING인 경우는
     IO진행중이라는 의미

2. IOCP

 2.1. 커널 오브젝트중 하나
 2.2. 재사용 가능한  쓰레드풀 유지
 2.3. CPU에 dispatch되는 쓰레드 조절
 2.4. 생성
  HANDLE CreateIoCompletionPort (
  HANDLE FileHandle,              // handle to file
  HANDLE ExistingCompletionPort,  // handle to I/O completion port
  ULONG_PTR CompletionKey,        // completion key
  DWORD NumberOfConcurrentThreads // number of threads to execute concurrently);
    두가지 일을 한다.
    1) IOCP커널 객체 생성
        hiocp = CreateCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 쓰레드숫자);
    2) Device와 IOCP객체 연결
        CreateCompletionPort(sock, hIocp, 컴플리션 키, 0 );
 2.5. 쓰레드풀 생성
 2.6. 동작에 필요한 자료구조
  2.6.1. Device List
   1) hDevice - dwCompletionKey
   2) hDevice와 관련된 IO가 완료되었을 때 dwCompletionKey를 던짐
   3) 생성: CreateIoCompletionPort가 호출될 때
   4) 제거: hDevice가 close될 때

  2.6.2. IO Completion Queue(IOCQ)
   1) dwBytesTransferred - dwCompletionKey - pOverlapped - dwError
   2) IO의 결과를 저장
   3) worker 쓰레드에서 하나하나 빼서 처리
   4) PostQueuedCompletionStatus함수를 이용해서 직접 넣을 수 있음.
      쓰레드간 큐나 쓰레드 종료 시 활용
   5) 생성: - IO 요청이 완료되었을 때 
            - PostQueuedCompletionStatus를 호출하였을 때
      제거: - Waiting Thread Queue로부터 Entry를 하나 제거할 때
              즉 IOCP Worker Thread를 하나 깨우고 이 쓰레드에서 레코드 하나를
              꺼낼 때

  2.6.3. Waiting Thread Queue(WTQ)
   1) dwThreadid
   2) IOCP Worker 쓰레드 풀
   3) GetQueuedCompletionStatus(GQCS)함수가 호출되면 Worker 쓰레드가 이 큐에
      쌓고 대기.
   4) 2.6.2의 IOCQ에 레코드가 들어오면 IOCP가 이 큐에서 쓰레드 하나를 깨우고
      GQCS함수를 리턴 시킴
      (CreateIoCompletionPort호출시 마지막 인자인 Concurrent Thread 개수만큼만)
   5) 실제로는 LIFO구조인데 Queue라는 이름이 붙었음
      LIFO구조를 이용해서 사용되는 쓰레드수를 최소화 함(CPU scheduling이 최소화됨)
   6) 제거: IOCQ가 비어있지 않고 Release Thread List(RTL)에 있는 쓰레드 수가
            Concurrent Thread수를 넘지 않을 때, IOCQ에서 레코드 entry가 하나
            제거되고, WTQ에 있던 dwThreadid가 RTL로 옮겨감.

  2.6.4. Released Thread List(RTL)
   1) 쓰레드 풀에서 꺼내온 쓰레드들
   2) 생성: - IOCP가 WTQ에서 쓰레드를 깨울 때
            - Paused Thread가 깨어날 때
   3) 제거: - 쓰레드가 다시 GetQueuedCompletionStatus 함수를 부를 때
            - 쓰레드가 스스로 Suspend(Sleep, WaitForSinglObject등으로 블록) 될 때

  2.6.5 Paused Thread List(PTL)
   1) 어떤 이유로 Suspend된 쓰레드들의 모임
   2) 생성: Released Thread 즉 돌고 있는 쓰레드가 스스로 멈출 때
      제거: Suspend된 쓰레드가 깨어날 때(dwThreadid는 RTL로 옮겨감)

  2.6.6. Concurrent Thread 수와 PTL, RTL
   1) Concurrent Thread 수: RTL에 들어갈 쓰레드 수
   2) Suspend된 쓰레드가 있다면 PTL에에 옮겨 놓고 WTQ에 대기중인 쓰레드를
      RTL로 가져와 처리.
   3) Suspend된 쓰레드가 깨어나면 RTL에 쓰레드가 Concurrent Thread 수보다
      초과될 수도 있다. 적어질 때까지 IOCP는 WTQ에서 쓰레드를 가져오지
      않는다. (효율적인 Thread Context Switching)

블로그 이미지

란마12

,

DLL안에서 thread로 윈도우를 생성할 때
CreateWindow에 인자로 쓰이는 hInstance를 GetModuleHandle(NULL)로 사용하면 안됨.
DLL이 로드될 때 생성되는 hInstance를 사용해야 함.

'library > WIN32API' 카테고리의 다른 글

MBCS 프로젝트 유니코드 전환 가이드  (0) 2010.11.24
프로세스간 동기화 테스트  (0) 2010.11.24
dll LoadLibrary/FreeLibrary  (0) 2010.10.01
IOCP  (0) 2010.07.08
실행중프로세스 목록/종속모듈 나열  (1) 2009.06.12
블로그 이미지

란마12

,
#include 

//  Forward declarations:
BOOL GetProcessList( );
BOOL ListProcessModules( DWORD dwPID );
BOOL ListProcessThreads( DWORD dwOwnerPID );
void printError( TCHAR* msg );

void main( )
{
  GetProcessList( );
}

BOOL GetProcessList( )
{
  HANDLE hProcessSnap;
  HANDLE hProcess;
  PROCESSENTRY32 pe32;
  DWORD dwPriorityClass;

  // Take a snapshot of all processes in the system.
  hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
  if( hProcessSnap == INVALID_HANDLE_VALUE )
  {
    printError( "CreateToolhelp32Snapshot (of processes)" );
    return( FALSE );
  }

  // Set the size of the structure before using it.
  pe32.dwSize = sizeof( PROCESSENTRY32 );

  // Retrieve information about the first process,
  // and exit if unsuccessful
  if( !Process32First( hProcessSnap, &pe32 ) )
  {
    printError( "Process32First" ); // Show cause of failure
    CloseHandle( hProcessSnap );    // Must clean up the
                                    //   snapshot object!
    return( FALSE );
  }

  // Now walk the snapshot of processes, and
  // display information about each process in turn
  do
  {
   //if ( lstrcmp(pe32.szExeFile, "notepad.exe") != 0 ) continue;
    printf( "\n\n"
      "=====================================================" );
    printf( "\nPROCESS NAME:  %s", pe32.szExeFile );
    printf( "\n"
      "-----------------------------------------------------" );

    // Retrieve the priority class.
    dwPriorityClass = 0;
    hProcess = OpenProcess(
      PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID );
    if( hProcess == NULL )
      printError( "OpenProcess" );
    else
    {
      dwPriorityClass = GetPriorityClass( hProcess );
      if( !dwPriorityClass )
        printError( "GetPriorityClass" );
      CloseHandle( hProcess );
    }

    printf( "\n  process ID        = 0x%08X", pe32.th32ProcessID );
    printf( "\n  thread count      = %d",   pe32.cntThreads );
    printf( "\n  parent process ID = 0x%08X",
      pe32.th32ParentProcessID );
    printf( "\n  Priority Base     = %d", pe32.pcPriClassBase );
    if( dwPriorityClass )
      printf( "\n  Priority Class    = %d", dwPriorityClass );

    // List the modules and threads associated with this process
    ListProcessModules( pe32.th32ProcessID );
    ListProcessThreads( pe32.th32ProcessID );

  } while( Process32Next( hProcessSnap, &pe32 ) );

  CloseHandle( hProcessSnap );
  return( TRUE );
}


BOOL ListProcessModules( DWORD dwPID )
{
  HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
  MODULEENTRY32 me32;

  // Take a snapshot of all modules in the specified process.
  hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID );
  if( hModuleSnap == INVALID_HANDLE_VALUE )
  {
    printError( "CreateToolhelp32Snapshot (of modules)" );
    return( FALSE );
  }

  // Set the size of the structure before using it.
  me32.dwSize = sizeof( MODULEENTRY32 );

  // Retrieve information about the first module,
  // and exit if unsuccessful
  if( !Module32First( hModuleSnap, &me32 ) )
  {
    printError( "Module32First" ); // Show cause of failure
    CloseHandle( hModuleSnap );    // Must clean up the
                                   //   snapshot object!
    return( FALSE );
  }

  // Now walk the module list of the process,
  // and display information about each module
  do
  {
    printf( "\n\n     MODULE NAME:     %s",
      me32.szModule );
    printf( "\n     executable     = %s",
      me32.szExePath );
    printf( "\n     process ID     = 0x%08X",
      me32.th32ProcessID );
    printf( "\n     ref count (g)  =     0x%04X",
      me32.GlblcntUsage );
    printf( "\n     ref count (p)  =     0x%04X",
      me32.ProccntUsage );
    printf( "\n     base address   = 0x%08X",
      (DWORD) me32.modBaseAddr );
    printf( "\n     base size      = %d",
      me32.modBaseSize );

  } while( Module32Next( hModuleSnap, &me32 ) );

  CloseHandle( hModuleSnap );
  return( TRUE );
}

BOOL ListProcessThreads( DWORD dwOwnerPID ) 
{ 
  HANDLE hThreadSnap = INVALID_HANDLE_VALUE; 
  THREADENTRY32 te32; 
 
  // Take a snapshot of all running threads  
  hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); 
  if( hThreadSnap == INVALID_HANDLE_VALUE ) 
    return( FALSE ); 
 
  // Fill in the size of the structure before using it. 
  te32.dwSize = sizeof(THREADENTRY32 ); 
 
  // Retrieve information about the first thread,
  // and exit if unsuccessful
  if( !Thread32First( hThreadSnap, &te32 ) ) 
  {
    printError( "Thread32First" ); // Show cause of failure
    CloseHandle( hThreadSnap );    // Must clean up the
                                   //   snapshot object!
    return( FALSE );
  }

  // Now walk the thread list of the system,
  // and display information about each thread
  // associated with the specified process
  do 
  { 
    if( te32.th32OwnerProcessID == dwOwnerPID )
    {
      printf( "\n\n     THREAD ID      = 0x%08X",
        te32.th32ThreadID ); 
      printf( "\n     base priority  = %d", te32.tpBasePri ); 
      printf( "\n     delta priority = %d", te32.tpDeltaPri ); 
    }
  } while( Thread32Next(hThreadSnap, &te32 ) ); 

  CloseHandle( hThreadSnap );
  return( TRUE );
}

void printError( TCHAR* msg )
{
  DWORD eNum;
  TCHAR sysMsg[256];
  TCHAR* p;

  eNum = GetLastError( );
  FormatMessage(
         FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
         NULL, eNum,
         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default lang.
         sysMsg, 256, NULL );

  // Trim the end of the line and terminate it with a null
  p = sysMsg;
  while( ( *p > 31 ) || ( *p == 9 ) )
    ++p;
  do { *p-- = 0; } while( ( p >= sysMsg ) &&
                          ( ( *p == '.' ) || ( *p < 33 ) ) );

  // Display the message
  printf( "\n  WARNING: %s failed with error %d (%s)",
    msg, eNum, sysMsg );
}
블로그 이미지

란마12

,