WinDbg 사용법

KB/tutorial 2006.08.27 09:16
오전 11:13 2001-09-26
조경민 bro@shinbiro.com
WinDbg 사용법
--------------------------------------------------------------------
요약.
두대를 시리얼로 연결
타겟은 디버그 될 대상 : boot.ini 수정,리붓,드라이버 설치,UserApp실행
소스는 dbg_x86_3.0.20.0.exe 설치후 WinDbg로 디버그를 봄


시리얼 케이블로 두대의 컴퓨터를 COM1에 연결한다.

타겟 컴퓨터에
c:\\boot.ini에서 해당 OS의 옵션 뒤로 붙인다.
/debugport=COMX /baudrate=BaudRate 옵션을 더붙여서 다음처럼 만든다.
multi(0)disk(0)rdisk(0)partition(3)\WINDOWS="Microsoft Windows XP Professional" /fastdetect /debugport=COMX /baudrate=BaudRate
를 기입한 컴퓨터를 끄고 (타겟컴퓨터)

다른 소스컴퓨터에서 WinDbg의 File/Kernel Debug를 한후
COM : 1
baudRate : 19200
을 한다.



Opened \\.\com1

Microsoft (R) Windows Kernel Debugger  Version 3.0.0020.0
Copyright (c) Microsoft Corporation. All rights reserved.

Waiting to reconnect...
Connected to Windows XP 2526 x86 compatible target, ptr64 FALSE
Kernel Debugger connection established.
Loaded dbghelp extension DLL
Loaded ext extension DLL
Loaded exts extension DLL
Loaded kext extension DLL
Loaded kdexts extension DLL
Symbol search path is: *** Invalid *** : Verify _NT_SYMBOL_PATH setting
Executable search path is:
*********************************************************************
* Symbols can not be loaded because symbol path is not initialized. *
*                                                                   *
* The Symbol Path can be set by:                                    *
*   using the _NT_SYMBOL_PATH environment variable.                 *
*   using the -y <symbol_path> argument when starting the debugger. *
*   using .sympath and .sympath+                                    *
*********************************************************************
PsLoadedModuleList not initialized yet.  Delay kernel load.
Windows XP Kernel Version 2526 UP Free x86 compatible
Built by: 2526.xpclient.010724-1758
Kernel base = 0x804d2000 PsLoadedModuleList = 0x80547b28
System Uptime: not available
여기까지 나오면 연결 성공
타겟 컴퓨터 하드웨어 COM1은 충돌난거처럼 나오는게 정상
타겟 컴퓨터에 디바이스를 추가하면 ( 장치관리자에서 충돌나서 !가 나오면
리소스에서 IRQ값을 바꿔본다 )

DdkDrv20.sys DriverEntry: loading
DdkDrv20.sys AddDevice
DdkDrv20.sys, DdkDispatchPnp minor function(unknown_pnp_irp)
DdkDrv20.sys, DdkDispatchPnp minor function(IRP_MN_FILTER_RESOURCE_REQUIREMENTS)
DdkDrv20.sys, DdkDispatchPnp minor function(IRP_MN_START_DEVICE)
DdkDrv20.sys StartDevice
DdkDrv20.sys, DdkDispatchPnp minor function(IRP_MN_QUERY_CAPABILITIES)
DdkDrv20.sys, DdkDispatchPnp minor function(IRP_MN_QUERY_PNP_DEVICE_STATE)
DdkDrv20.sys, DdkDispatchPnp minor function(IRP_MN_QUERY_DEVICE_RELATIONS)

라고 뜬다.

이제 타겟 컴퓨터에서 DdkDrv20용 UserApp를 실행한후 File/Create를 하게 되면
소스컴퓨터의 WinDbg에
DdkDrv20.sys IRP_MJ_CREATE
라고 뜬다. 즉, 타겟 컴퓨터에 일어나는 드라이버내 DgbPrint가 소스 컴퓨터에
찍히는 것이다.

'KB > tutorial' 카테고리의 다른 글

WinDbg 사용법  (0) 2006.08.27
DCOM 로컬서버 하기  (0) 2005.08.04
Implementation FAT File System  (0) 2004.11.04
How to i386 32bit OS Kernel Compile in VC6  (0) 2004.11.04
DDK in VC with SoftICE  (1) 2004.11.04
COM 형식 라이브러리 사용하기  (0) 2004.11.04
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST


오후 2:17 2005-08-04
DCOM 로컬서버하기 조경민 bro@shinbiro.com
=========================================================================

DCOM은?
----------------------------------------------------------
DCOM은 Distributed COM으로 다른 컴퓨터에 있는 COM 기능을 원격에서 호출할 수 있다.
DCOM을 구현하기 위해서 COM+ DLL을 작성하여 이를 MTS에 올려 미들웨어식으로
DCOM 패키지를 내보낼 수 있는데 이는 COM+과 MTS의 도움으로 DCOM 기능을 구현한 것인데
이는 "COM+ 컴포넌트 만들기" 문서를 참고한다.
http://neri.cafe24.com/menu/bbs/view.php?id=kb&page=1&sn1=&divpage=1&sn=off&ss=on&sc=on&keyword=MTS&select_arrange=headnum&desc=asc&no=176

만일 ATLCOM을 잘 모른다면 다음 문서를 참고한다.
ATL 조금 하기
http://neri.cafe24.com/menu/bbs/view.php?id=kb&page=1&sn1=&divpage=1&category=5&sn=off&ss=on&sc=on&keyword=COM&select_arrange=headnum&desc=asc&no=79

DCOM 로컬 서버
----------------------------------------------------------
지금 하려는 것은 로컬 서버 자신이 DCOM 서버가 되는 방법이다.
1. 먼저 VC를 실행 한 후 ATL COM 프로젝트로 실행하되(이름은 TestDCOM), Service를 선택한다.
2. ClassView에서 오른클릭 New ATL Object한 후 Simple Object을 만든다.
3. Project/Setting에서 Post-Build step에 다음을 추가한다.
   .\debug\TestDCOM.exe -regserver
   start nmake TestDCOMps.mk
   regsvr32 TestDCOMps.dll
   만일 로컬서버를 서비스로 동작시키려면 -regserver 대신 -service를 넣고 관리도구/서비스 에서
   해당 TestDCOM 서비스를 동작 시키면된다. TestDCOMpsmk는 자동생성된 TestDCOM의 Proxy com dll
   프로젝트다. proxy com dll이 없어도 어느정도 동작은되나 배열인자 전달이 제대로 안되는 경우가
   있으므로 proxy dll을 제공하는것이 좋다.
4. TestDCOM.cpp에 있는 소스를 수정한다.

void CServiceModule::Run()
{
    _Module.dwThreadID = GetCurrentThreadId();

//    HRESULT hr = CoInitialize(NULL);
//  If you are running on NT 4.0 or higher you can use the following call
//  instead to make the EXE free threaded.
//  This means that calls come in on a random RPC thread
        HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

    _ASSERTE(SUCCEEDED(hr));


    // This provides a NULL DACL which will allow access to everyone.
//    CSecurityDescriptor sd;
//    sd.InitializeFromThreadToken();
//    hr = CoInitializeSecurity(sd, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);

        // This provides a NULL DACL which will allow access to everyone.
    hr = CoInitializeSecurity(0, -1, 0, 0, RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_ANONYMOUS, 0, 0, 0 );
    _ASSERTE(SUCCEEDED(hr));

    hr = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, REGCLS_MULTIPLEUSE);
    _ASSERTE(SUCCEEDED(hr));

    LogEvent(_T("Service started"));
    if (m_bService)
        SetServiceStatus(SERVICE_RUNNING);

    MSG msg;
    while (GetMessage(&msg, 0, 0, 0))
        DispatchMessage(&msg);

    _Module.RevokeClassObjects();

    CoUninitialize();
}

5. rgs파일을 수정한다.
   DCOM 서버에 접속하기 위해서는 Activation Security에 어긋나선 안되는데 이를 위해 LaunchPermission
   이 알맞아야 한다. CoCreateInstanceEx 시 접속할 컴퓨터의 로그인 계정(아이디암호)를 넣는 등 여러가지
   방법이 있으나 간편하게 Interactive user로 접속하는 MSDN 샘플 'DCOMTEST.exe' 에서 제시된 레지스트리
   파일을 이용하여 수정하였다. (codeproject의 anonymous DCOM에서 소개된 것으로는 안되었다.)
   TestDCOM.rgs를 수정한다.

HKCR
{
        NoRemove AppID
        {
                {4C4C2EB8-D5A6-49AD-9233-7B3E28E2B126} = s 'TSSCORE'
                {
                        val LaunchPermission = b '01000480300000004c000000000000001400000002001c00010000000000140001000000010100000000000100000000010500000000000515000000a05f841f5e2e6b49ce120303f4010000010500000000000515000000a05f841f5e2e6b49ce120303f4010000'
                        val RunAs = s 'Interactive User'
                }
                'TSSCORE.EXE'
                {
                        val AppID = s {4C4C2EB8-D5A6-49AD-9233-7B3E28E2B126}
                }
        }
}


이제 로컬서버 DCOM은 끝났다.
모두 빌드 후 TestDCOM.exe를 더블 클릭 또는 Debug 모드로 실행한다.


DCOM 클라이언트
--------------------------------------------------------------------
DCOM 클라이언트를 작성한다.
1. MFC 기반 다이얼로그나 Win32 프로젝트를 하나 생성한다. TestDCOMClient라고 지었다고하자.
2. DCOM 연결을 위해서 stdafx.h 같은 공통 헤더파일에 아래를 추가한다.
   경로는 상대경로 절대경로다되므로 알맞게 맞춘다. tlb를 복사해와도 된다.

#import "TestDCOM.tlb" no_namespace
#define _WIN32_DCOM                                // DCOM USE!
#include <objbase.h>
#include <comdef.h>

3. 다음 코드를 추가한다.

IUnknownPtr        CreateDCOMInstance(const CLSID& clsid, LPCTSTR pszIP)
{
        _bstr_t bsIP(pszIP);
        MULTI_QI mqi = { &IID_IUnknown, 0, 0 };

    COAUTHINFO cai = {  RPC_C_AUTHN_NONE,
                        RPC_C_AUTHZ_NONE,
                        0,
                        RPC_C_AUTHN_LEVEL_NONE,
                        RPC_C_IMP_LEVEL_IMPERSONATE,
                        0,
                        EOAC_NONE};

    COSERVERINFO csi = { 0, bsIP, &cai, 0 };

    HRESULT hr = CoCreateInstanceEx(clsid, NULL, CLSCTX_REMOTE_SERVER, &csi, 1, &mqi);

    if (FAILED(hr))
    {

        TCHAR szError[1024];
        FormatMessage(  FORMAT_MESSAGE_FROM_SYSTEM,
                        NULL,
                        hr,
                        MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
                        szError,
                        1024,
                        NULL);
        return FALSE;
    }

    return (IUnknownPtr)mqi.pItf;
}

다음은 쓰는 부분이다. 접속하려는 DCOM 서버의 ip나 www.yahoo.com같은 도메인이름,
컴퓨터 이름 등을 넣어주면 된다.

// 먼저 COM 라이브러리를 초기화하고, 인증없이 익명 연결한다.
CoInitialize(NULL);
CoInitializeSecurity(0, -1, 0, 0, RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_ANONYMOUS, 0, 0, 0 );

ITestPtr spTest;
spTest = CreateDCOMInstance(__uuidof(TestDCOM), "localhost" );
spTest->RemoteCall();

// 다썼으면 프로그램 끝내기 전에 COM 라이브러리 해제
CoUninitialize();

실행하기
-----------------------------------------------------------------------------
DCOM 서버에서 proxy dll인 TestDCOMps.dll을 regsvr32로 등록한 후,
DCOM 로컬서버를 실행한다.
DCOM 클라이언트에서는 proxy dll을 복사해와서 regsvr32로 등록한 후,
클라이언트를 실행한다.

배열인자 사용
-----------------------------------------------------------------------------
DCOM 서버의 메소드 중에 아래와 같은 것이 있다고 한다면

[id(1), helpstring("method Test")] HRESULT Test( [out] int* pnLen, [out,size_is(,*pnLen)] unsigned char** ppData );

size_is는 이 메소드가 호출될 당시에 배열의 길이가 결정된다는 것으로 *pnLen으로 결정되는
것인데 DCOM 서버에 proxy dll이 등록되지 않으면 제대로 동작하지 않는다.
DCOM서버의 Test 구현부다.

STDMETHODIMP TestDCOM::Test(int *pnLen, unsigned char** ppData)
{
        // TODO: Add your implementation code here
        char buf[5] = "abcd";
        *pnLen = 5;
        *ppData = (unsigned char*)CoTaskMemAlloc( *pnLen );

        strcpy( (char*)*ppData, buf );

        return S_OK;
}

DCOM을 통해 오가는 메모리는 CoTaskMemAlloc/CoTaskMemfree를 사용하길 권한다.
DCOM서버에서 Alloc하고 이를 클라이언트로 리턴했다면 클라이언트는 Free를 해야할 책임이 있다.
아래는 클라이언트 호출부이다. 만일 proxy dll을 DCOM 서버에서 등록해 놓은 상태가 아니면
abcd가 전달안되고 a만 전달되게 된다.

int nLen;
unsigned char* pData;
spTest->Test( &nLen, &pData );
CoTaskMemFree(pData);


DCOM 안될때
--------------------------------------------------------------------------------

RPC Server is Unavailable에 관하여
----------------------------------
- RPC Server is Unavailable 방화벽으로 막힌 경우가 많다.
- netstat -noa 후 135 포트 (RPC)가 리슨되고 있는지 확인한다.
- MSDN에서 DCOMTEST.exe 을 찾아서 확인해 보라.
- MSDN에서 Windows Resource kit 인 rpcping.exe를 사용할 수 있다.
http://groups-beta.google.com/group/microsoft.public.win32.programmer.wmi/browse_thread/thread/56473e1d1801e010/1f747c12e595d612?lnk=st&q=re:+DCOMTEST+800706ba&rnum=2#1f747c12e595d612

=> 본인은 방화벽 문제였다.


Access Denied에 관해서
----------------------
위의 CoInitializeSecurity(0, -1, 0, 0, RPC_C_AUTHN_LEVEL_NONE, RPC_C_IMP_LEVEL_ANONYMOUS, 0, 0, 0 );
코드를 서버와 클라이언트에 모두 넣어주면 (SP2시 SP2 보안설정까지) 다른 도메인, 다른 유저라도 가능하다.
유저가 Administrator 권한이 아니고 User 권한이라도 동작이 된다.

그래도 안되면 아래의 상황을 보자.

BUG: "Access Denied" Error Message in Response to DCOM Activation for ATL-Built COM Servers
8자 이하의 실행 파일명이어야 한다.

http://www.codeguru.com/activex/COMSecurity1.html
http://www.codeguru.com/activex/COMSecurity2.html
DCOM의 보안은 두가지 주제로 볼 수 있다. Activation Security과 Access Security이다.


Activation Security

Activation Security은 일단 DCOM 서버를 활성화시킬 수 있는가에 대한 얘기이다.
활성화하기 위해서는 DCOM 서버를 실행해야하므로 Launch Permission이 있어야 한다.
이말은 bro로 로그인된 DCOM 클라이언트 호스트에서 다른 컴퓨터인 DCOM 서버에 접근하려면
그 다른 컴퓨터에도 bro 계정이 있어야 한다는 말인데,
이를 해결하기 위해서
- anonymous (unauthenticated) permission을 설정
- COSERVERINFO구초체안의 COAUTHINFO 구조체 안의 COAUTHIDENTITY에 DCOM서버컴퓨터에 로그인 가능한
사용자 아이디와 암호를 넣어야 한다. 그리고 CoCreateInstanceEx or CoGetClassObject 를 호출하게 된다.
이 때 COAUTHINFO 구조체의 dwImpersonationLevel는 RPC_C_IMP_LEVEL_IMPERSONATE과 같거나 높은 레벨이어야 한다.

=> DCOMTEST.exe에서 제공된 레지스트리 값으로 해결했다.


XP SP2에 관하여
http://service1.symantec.com/Support/faxprod.nsf/docid/2004071911425704
------------------------------------
아래는 DCOM 클라이언트/ 서버 모두 해주어야 하는 설정이다.
XP SP2는 자체 방화벽으로 RPC server is unavailable 에러를 일으킬 수 있다.
방화벽을 끄거나, 제어판/윈도우즈 방화벽의 탭중 예외에 RPC인 135 포트를 추가하거나 프로그램에서
자신의 Local DCOM Server EXE를 선택한다.

그러나 SP2에서는 Anonymous DCOM Access를 Access Denied하기 때문에 다음과 같은 절차로 허용하게 만든다.

커맨드창을 열고 다음 명령어를 입력한다.
netsh
firewall
set service REMOTEADMIN ENABLE SUBNET 또는
set service REMOTEADMIN ENABLE ALL
show service
exit

다음으로 제어판/관리도구로 간 후 구성 요소 서비스를 연 후
트리 노드 루트/컴퓨터/내컴퓨터에서 오른클릭 속성으로 간 후 COM 보안에서
각각 제한값 편집 버튼을 눌러 사용자 'ANOYMOUS LOGIN' 추가 후 ANOYMOUS LOGIN을 모두 허용하게 만든다.

아래는 마지막으로 DCOM 서버가 할 이다.
구성 요소 서비스의 트리  루트 노드/컴퓨터/내컴퓨터/DCOM 구성에서 자신의 DCOM 서버를 선택한 후
오른클릭 속성으로 등록정보를 연후 보안탭을 연후 시작 및 활성화 권한을 사용자 지정으로 한후 편집 버튼
을 누른 후 ANONYMOUS LOGON을 추가후 모두 허용으로 한다.

'KB > tutorial' 카테고리의 다른 글

WinDbg 사용법  (0) 2006.08.27
DCOM 로컬서버 하기  (0) 2005.08.04
Implementation FAT File System  (0) 2004.11.04
How to i386 32bit OS Kernel Compile in VC6  (0) 2004.11.04
DDK in VC with SoftICE  (1) 2004.11.04
COM 형식 라이브러리 사용하기  (0) 2004.11.04
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST




오후 2:59 2004-05-31


조경민 bro(bro@shinbiro.com)


Implementation FAT File System


*본 문서는 편집 중입니당.. 설명에 도움이 되는
그림 추가 및 문체를 수정하는 중입니다. *


 


[Implementation FAT File System]


 


먼저 참조했던 문서 및 소스는


 http://neri.cafe24.com/menu/bbs/zboard.php?id=project&page=1&sn1=&divpage=1&category=5&sn=off&ss=on&sc=on&select_arrange=headnum&desc=asc&no=23


에서 찾아볼 수 있다.


 


이중에서 이 문서에서 필요한 FileSystem이란 무엇인가 만 발췌해보겠습니다.


 


파일시스템
조사


2004-05-21
조경민 bro (bro@shinbiro.com)


================================================================


 


file
system ; 파일 시스템


 


1.
컴퓨터에서, 파일 시스템은 파일에 이름을 붙이고, 저장이나 검색을 위해 논리적으로
그것들이 어디에 위치시켜야 하는지 등을 나타내는 방법이다. 도스, 윈도우, OS/2,
매킨토시 및 유닉스 기반의 운영체계들은 모두, 파일들이 어딘가에 계층적인 구조로
위치하는 파일 시스템을 가지고 있다. 파일은 계층구조 내의 바른 위치인 디렉토리
(윈도우95 이후에는 폴더) 또는 서브디렉토리 내에 놓여진다. 파일 시스템들은 파일의
이름을 붙이는 규칙을 가지고 있다. 이러한 규칙에는 파일이름의 길이제한, 어떤
글자들이 사용될 수 있는지 등이 포함되며, 몇몇 시스템에서는 파일이름 확장자의
길이조차 제한을 두고 있다. 파일 시스템은 또한 디렉토리 구조를 통하여 파일까지
가는 경로를 설정하는 형식을 포함한다.


 


2.
때로 이 용어는 1.번에서 정의된 파일시스템을 지원하는 운영체계 또는 부가적인
프로그램의 일부를 지칭한다. 이러한 부가 프로그램의 예로는 NFS (Network File
System)나 AFS (Andrew file system) 등이 있다.


 


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


파일시스템의
구성


 


filesystem
- 일정한 file i/o 함수로 특정 기억 장치 미디어( 하드디스크,플로피 디스크, cd-rom,
플래쉬 메모리, RAM메모리, ROM 메모리, 가상파일 등)



구축되는 시스템으로 파티션/볼륨/디렉토리/파일을 접근/수정/생성/삭제 등의 관리를
하는 OS의 일부분. 조사해 본 결과 OS구현시 해당 OS에 맞는 filesystem을 만들어서
명명짓거나, 새로운 기술을 적용하여 여러 OS에 포팅할 수 있는 filesystem을 만들거나,
기억장치의 특성에 맞춘 파일시스템이 있을 수 있다.


출처
: 조경민 bro (bro@shinbiro.com)


 


예:
SMX rtos의 파일시스템 smxFile ™ Feature이다.


Features


FAT
12/16/32


DOS/Win9x
compatible


Extensive
drivers:  // 지원하는 저장매체이다.


   Floppy


   IDE
Hard Disk


   Compact
Flash


   DiskOnChip®


    PCMCIA-ATA


   Ultra
DMA


   ATAPI


   LS-120


   Zip®


    SCSI


   RAM
disk


   ROM
disk


   BIOS
devices


20
MByte/sec sustained transfer rate ( IDE Ultra-DMA )


Up
to 2 terrabyte disks


55
KB typical code footprint


20
KB typical data footprint


Multitasking
support


Contiguous
file support


Source
code included


Fully
integrated with SMX


출처:
http://www.smxinfo.com/rtos/fileio/smxfile.htm#note1


 


파티션
- 하드디스크경우 물리적 디스크 하나를 그대로 파티션 하나가 될 수 있으며 하드디스크의
공간을 나누어 파티션으로 관리 할 수 있다. 즉 디스크의 저장공간 단위 이다.


출처
: http://www.thinki21.com/os/win2000/05volume.htm


 


볼륨
- 파티션과 반대로 물리적 하드디스크 두개이상 또는 파티션 두개이상을 하나의 볼륨으로
묶을수 있다. 하나의 디스크가 하나의 파티션이되고 주파티션은 볼륨이 될 수 있다.


winnt서버에서는
볼륨 셋 [Volume Set] - Spanned Volume, 스트라이프 셋 [Stripe Set] - Striped
Volume, 미러 셋 [Mirror Set] - Mirrored Volume, 디스크 듀플렉싱 [Disk Duplexing],
패러티 사용 스트라이프 셋 [Stripe Set With Parity] - RAID 5 Volume 등으로 볼륨
종류를 나누고 있다.


 


디렉토리
- 여러개의 파일을 담아 놓는 논리적 공간 디렉토리는 속해있는 파일들의 위치 정보를
갖고 있다.


 


파일
- 파일시스템에서 데이타를 저장하는 물리적 단위


출처
: 조경민 bro (bro@shinbiro.com)


 


가장 추천하고 싶은 FAT 문서는 아래와 같습니다.


 


참조
문서:


 MS의
WhitePager:


 http://www.nondot.org/sabre/os/files/FileSystems/FatFormat.pdf


 


 GNU
Parted FAT file system documentation:


 http://www.polarhome.com:793/manual/parted-1.6.3/FAT


 


 Understanding
FAT32 Filesystems:


 http://www.pjrc.com/tech/8051/ide/fat32.html


 


 FAT
Guide:


 http://home.freeuk.com/foxy2k/disk/disk3.htm


 


[파일 시스템을 접근하는 방법]


 


FAT의 종류


FAT12 : 보통 20MB 이하의 디스크 또는 플로피에 적용


FAT16 : 보통 20MB 이상 512MB이하의 디스크에 적용 2GB까지 가능


FAT32 : 보통 512MB 이상이며 테라바이트까지 지원 가능하다.


 


먼저 FAT 파일시스템을 만들기 위해서는 기존 FAT 파일 시스템에 접근해서 읽어보는
것이 좋을것이다.


 


세가지 방법이 있습니다.


 


- 자신의 하드 HDD를 읽어본다. (용량상 FAT16/FAT32 테스트 가능하다)


 


- 자신의 플로피 FDD를 읽어본다. (용량상 FAT12만 테스트 가능하다)


 


- 하드의 해당 파티션 하나 또는 플로피 FDD 하나를 이미지 떠서 바이너리 파일로
만든후 해당 파일을 Ram 메모리에 올려 메모리에서 읽어보는 것이다. (이미지를 뜨거나
가상 파일 시스템을 만드는 툴을 이용하면 FAT12/16/32가능)


 


저의 경우는 개발 환경상 3번째 방법이 간편하여 선택하였습니다.


 


먼저 자신의 하드 HDD를 읽어보는 방법은 먼저 MBR이라는 것을 이해해야합니다.


 이 정보에 대한 문서는 아래의 'MBR/ 파티션 부트섹터 읽기' 를 참조하시기
바랍니다.


 


 


오전 4:42 2004-05-29


조경민 bro(bro@shinbiro.com)


MBR/ 파티션 부트섹터 읽기


==============================================


win nt를 소스 예를 들었으며 win9x는 codeproject( http://www.codeproject.com)


에서 DiskSector 라는 클래스를 가져다 쓰면 된다.


테스트되는 내 pc 는 하나의 물리 하드디스크를 3개의 파티션으로 나누어서


C: NTFS


D: NTFS


E: FAT32


로 한 상태에서 예를 보인다.


 


하드디스크의 물리적인 이름인 PHYSICALDRIVE0으로


열면된다. 두번째 hdd는 PHYSICALDRIVE1 이다.


 


        HANDLE
hDrive;


        TCHAR
szDrv[]=_T("\\\\.\\PHYSICALDRIVE0");


        DWORD
dwRead;


 


        hDrive
= CreateFile(szDrv, GENERIC_READ | GENERIC_WRITE,


                   FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);


 


        //
 how many bytes you want to read


        DWORD
dwSize = 512;


 


        //
Set pointer to begin of file


        SetFilePointer(hDrive,
NULL, NULL, FILE_BEGIN);


 


        //
read the first 512 bytes


        //
Becarefull not to ovrewrite this because it will make you system un-bootable.
 You will lose everything.


        ReadFile(hDrive,
data , len , &dwRead, NULL);


        CloseHandle(
hDrive );


 


이렇게 열면 물리적 하드디스크의 첫번째 섹터에 있는 MBR을 얻게된다.


MBR에는 파티션 정보등의 유용한 정보가 들어 있다. MBR 섹터는 아래와 같은 레이아웃을
갖습니다.


 



 


다음은 MBR 512 bytes 1 sector 내용이다.


 


0012F654
 4E 32 E4 8A 56 00 CD 13 EB E4  N2?V...陰


0012F65E
 8A 56 00 60 BB AA 55 B4 41 CD  둞.`빽U퀮.


0012F668
 13 72 36 81 FB 55 AA 75 30 F6  .r6괎U챬0.


0012F672
 C1 01 74 2B 61 60 6A 00 6A 00  ..t+a`j.j.


0012F67C
 FF 76 0A FF 76 08 6A 00 68 00  .v..v.j.h.


0012F686
 7C 6A 01 6A 10 B4 42 8B F4 CD  |j.j.퀯뗵.


0012F690
 13 61 61 73 0E 4F 74 0B 32 E4  .aas.Ot.2.


0012F69A
 8A 56 00 CD 13 EB D6 61 F9 C3  둞...絨a荷


0012F6A4
 49 6E 76 61 6C 69 64 20 70 61  Invalid pa


0012F6AE
 72 74 69 74 69 6F 6E 20 74 61  rtition ta


0012F6B8
 62 6C 65 00 45 72 72 6F 72 20  ble.Error


0012F6C2
 6C 6F 61 64 69 6E 67 20 6F 70  loading op


0012F6CC
 65 72 61 74 69 6E 67 20 73 79  erating sy


0012F6D6
 73 74 65 6D 00 4D 69 73 73 69  stem.Missi


0012F6E0
 6E 67 20 6F 70 65 72 61 74 69  ng operati


0012F6EA
 6E 67 20 73 79 73 74 65 6D 00  ng system.


0012F6F4
 00 00 00 00 00 00 00 00 00 00  ..........


0012F6FE
 00 00 00 00 00 00 00 00 00 00  ..........


0012F708
 00 00 00 00 00 00 00 00 00 00  ..........


0012F712
 00 00 00 00 00 00 00 00 00 00  ..........


0012F71C
 00 00 00 00 00 00 00 00 00 00  ..........


0012F726
 00 00 00 00 00 00 00 2C 44 63  .......,Dc


0012F730
 FC 8F FC 8F 00 00 80 01 01 00  ??......


0012F73A
 07 FE FF FF 3F 00 00 00 37 16  ....?...7.


0012F744
 71 02 00 00 C1 FF 0F FE FF FF  q......... 파티션0부터 3까지


0012F74E
 76 16 71 02 8A 8F DF 06 00 00  v.q.뒒.... 4개의 파티션 정보


0012F758
 00 00 00 00 00 00 00 00 00 00  .......... 16바이트 4개있음


0012F762
 00 00 00 00 00 00 00 00 00 00  ..........


0012F76C
 00 00 00 00 00 00 00 00 00 00  ..........


0012F776
 55 AA 74 FE 12 00 B4 F7 12 00  U챫...덖..


              ~~~~
<- 여기까지가 512


 


그중 첫번째 파티션0의 16바이트는 0x0012F736 으로 (http://www.pjrc.com/tech/8051/ide/fat32.html
참조)


 


 이 곳의 8번째부터 4칸이 첫번째 파티션이 시작되는 LBA값이다. (LBA는
하드디스크에서 특정 섹터를 찾는 방식이며 그냥 섹터값 번지로 생각하면 된다. 기존
CHS 실린더 헤더 섹터 번지값보다 쉬운 표현이다.)


3F 00 00 00 이 첫번째 파티션의 LBA Begin인데 intel은 리틀인디언이므로 00
00 00 3F 로 0x3F*512 바이트 번재가 실제 파티션의 첫 섹터(부트섹터) 가 된다.


 


SetFilePointer(hDrive, 0x3F*512, NULL, FILE_BEGIN); 이렇게 해서 읽어보면
첫번째 파티션의 부트섹터가 나온다. NTFS 로 시작하는게 보일것이다 (vc 디버거의
메모리창임) 륬TFS 로 나왔는데 한글로 변환되어 보여서 그렇다..


 


0012F578
 EB 52 90 4E 54 46 53 20 20 20  ?륬TFS   


0012F582
 20 00 02 08 00 00 00 00 00 00   .........


0012F58C
 00 F8 00 00 3F 00 FF 00 3F 00  ....?...?.


0012F596
 00 00 00 00 00 00 80 00 80 00  ..........


0012F5A0
 36 16 71 02 00 00 00 00 04 00  6.q.......


0012F5AA
 00 00 00 00 00 00 63 11 27 00  ......c.'.


0012F5B4
 00 00 00 00 F6 00 00 00 01 00  ..........


0012F5BE
 00 00 EC 10 21 14 1F 21 14 08  ....!..!..


0012F5C8
 00 00 00 00 FA 33 C0 8E D0 BC  .....3핉劇


0012F5D2
 00 7C FB B8 C0 07 8E D8 E8 16  .|醯..롔..


0012F5DC
 00 B8 00 0D 8E C0 33 DB C6 06  ....렳3培.


0012F5E6
 0E 00 10 E8 53 00 68 00 0D 68  ...?.h..h


0012F5F0
 6A 02 CB 8A 16 24 00 B4 08 CD  j.?.$....


0012F5FA
 13 73 05 B9 FF FF 8A F1 66 0F  .s....딋f.


0012F604
 B6 C6 40 66 0F B6 D1 80 E2 3F  뗬@f.뚜..?


0012F60E
 F7 E2 86 CD C0 ED 06 41 66 0F  透녝잿.Af.


0012F618
 B7 C9 66 F7 E1 66 A3 20 00 C3  령f投f. ..


0012F622
 B4 41 BB AA 55 8A 16 24 00 CD  퀮빽U..$..


0012F62C
 13 72 0F 81 FB 55 AA 75 09 F6  .r.괎U챬        .


0012F636
 C1 01 74 04 FE 06 14 00 C3 66  ..t.....홨


0012F640
 60 1E 06 66 A1 10 00 66 03 06  `..f...f..


0012F64A
 1C 00 66 3B 06 20 00 0F 82 3A  ..f;. ...:


0012F654
 00 1E 66 6A 00 66 50 06 53 66  ..fj.fP.Sf


0012F65E
 68 10 00 01 00 80 3E 14 00 00  h.....>...


0012F668
 0F 85 0C 00 E8 B3 FF 80 3E 14  ....蘊..>.


0012F672
 00 00 0F 84 61 00 B4 42 8A 16  ...꼆.퀯..


0012F67C
 24 00 16 1F 8B F4 CD 13 66 58  $...뗵..fX


0012F686
 5B 07 66 58 66 58 1F EB 2D 66  [.fXfX..-f


0012F690
 33 D2 66 0F B7 0E 18 00 66 F7  3?.....f.


0012F69A
 F1 FE C2 8A CA 66 8B D0 66 C1  只혡?뗊f.


0012F6A4
 EA 10 F7 36 1A 00 86 D6 8A 16  ...6..녧..


0012F6AE
 24 00 8A E8 C0 E4 06 0A CC B8  $.딂잤..見


0012F6B8
 01 02 CD 13 0F 82 19 00 8C C0  ........뙽


0012F6C2
 05 20 00 8E C0 66 FF 06 10 00  . .렳f....


0012F6CC
 FF 0E 0E 00 0F 85 6F FF 07 1F  .....꿵...


0012F6D6
 66 61 C3 A0 F8 01 E8 09 00 A0  fa횪...        ..


0012F6E0
 FB 01 E8 03 00 FB EB FE B4 01  .....魂

'KB > tutorial' 카테고리의 다른 글

WinDbg 사용법  (0) 2006.08.27
DCOM 로컬서버 하기  (0) 2005.08.04
Implementation FAT File System  (0) 2004.11.04
How to i386 32bit OS Kernel Compile in VC6  (0) 2004.11.04
DDK in VC with SoftICE  (1) 2004.11.04
COM 형식 라이브러리 사용하기  (0) 2004.11.04
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST




[작성한 이유]


 


 OS를 만들어보고 싶은 마음에 무작정 뛰어 들었는데 대부분 환경은 리눅스상이
gcc였습니다. 하지만 저는 windows 환경하의 vc 개발을 주로 해왔으므로 무척
불편(?)했습니다. Djgpp 라는 dos용 gcc 포팅 버전과 윈도우용 cygwin 패키지를
사용하면 역시 동일하게 gcc를 윈도우에서 컴파일 할 수 있습니다 그래도 저는 vc의
에디팅 환경이 맘에 들어서 디버깅까지는 힘들겠지만 (어차피 커널 디버깅하려면
스스로 자신의 커널 디버거를 만들어야 하는 거 같습니다. ) 에디팅과 컴파일
그리고 링커를 vc6를 사용하고 싶었고 그래서 이를 사용할 수 있는 방법을 소개하려
합니다.


 


 


[참고하면 좋은 사이트들]


 


 http://www.osx86.org/


 -
어려운 영문 OS개발방법을 한글화 해주는 고마운 사이트


 http://www.zap.pe.kr/


 -
Chobits os의 소스를 얻을 수 있습니다.


 http://www.execpc.com/~geezer/johnfine/index.htm


 -
무척 좋은 Protected mode에 대한 설명과 소스를 담고 있는 사이트


http://www.nondot.org/sabre/os/articles


 -
OS에 대한 전반적인 자료가 있습니다.


http://www.osdever.net/


 -
Bona Fide OS 개발 튜토리얼 사이트


http://mega-tokyo.com/osfaq2/


 -
OS FAQ


http://www.osdev.org/


 -
소스가 공개된 여러 os가 있습니다.


http://www.bellona2.com


 -
혼자서 GUI OS를 만드시고 책도 출판하신 분의 사이트


http://ksyspro.org


 -
한국 임베디드 개발자 모임


 


[Protected i386 OS를 위한 잛은 설명]


 


 Intel은 다른 cpu 벤더보다 역사가 오래되어서 4bit microprocessor인 4004에서
출발해서 8bit cpu 8008, 8080, 16비트 8086, 80186, 80286, 32비트 80386, 80486,
80586 (또는 P5 그러나 숫자는 저작권 보호를 받지 못한다길래 이후에 Pentium으로
바뀌었습니다 ). 이렇게 긴 역사를 갖고 있는 보편적인 cpu 8080 또는 8086 통틀어
intel x86 cpu에서 돌던 프로그램도 586에서도 수행되도록 하위호환을 갖게 됩니다.


 이 하위호환 때문에 아무리 최신 컴퓨터도 처음 부팅시에는 빠른 x86 처럼
수행되도록 설계되었습니다. 부트섹터에서 이 빠른 x86에서 최신의 기능을 사용하는
i386+ 환경으로 만들기 위해서 적어도 다음과 같은 일을 하게 됩니다.


 


 ORG 0x7C00 - PC가 리셋되면 부팅될 디바이스의 첫번째 섹터(512바이트)를
읽어 메모리 0x7C00에 올려 놓은 후 이 코드로 점프하게 됩니다. 따라서 부트섹터
코드는 그 메모리에서 시작한다고 지정해야 할것입니다.


 


 A20 enable - 예전에는 실제 메모리크기가 크지 않아 1M 이하만 접근하도록
설계되었는데 이젠 메모리가 1M이상 되는 것이 많아 1M 이상을 실제 접근 하기 위해서
해주어야 합니다.


 


 Protected mode - 처음 부팅할땐 x86 Real Mode라는 세그먼트:오프셋
지정 방식으로 수행되지만 80286이상에서는 보호모드가 지원되었고 (최대16M가능)
80386에서는 4G까지 접근 가능하도록 되었습니다.


 


 커널로드 - 부트섹터 코드는 C로된 바이너리 커널을 여러 섹터에서
읽어서 특정 메모리 번지에 로드한 후 그 시작 함수 (엔트리 포인트 함수)로 점프하게
됩니다.


 


[준비물]


 


 VC6,
- 커널의 컴파일 및 링킹을 담당합니다.


 VMWare
- 우리는 디스켓도 사용하지 않고 바로 컴파일해서 이미지로 뜬 후 VMWare로
로드할 것입니다. ( no 플로피 부팅디스켓, no 리붓, no test machine )


 Nasm
- Netwide Asm으로 at&t 계열 및 intel 계열 둘다 지원하고 target format도
많이 지원하는 어셈블러입니다. 설치과정없이 그냥 nasm.exe 만 있으면 됩니다.


 


 


[소스 파일들]


 


 bootsect.asm
- 간단한 보호모드(세그먼테이션방식) 진입 및 커널 로드 부트로더


 main.c
- VC로 컴파일될 작은 커널


  Makeboot.exe
- 부트섹터와 커널이미지를 merge하는 간단한 makeboot.c 컴파일해서 자신만의
부트 이미지 뜨는 프로그램 만들면된다.


 


[소스]


bootsect.asm

 


[BITS
16]       ; We need 16-bit intructions for Real
mode


 


[ORG
0x7C00]    ; The BIOS loads the boot sector into memory location
0x7C00


 


reset_drive:


        mov
ah, 0               ;
RESET-command


        int
13h                 ;
Call interrupt 13h


        or
ah, ah               ;
Check for error code


        jnz
reset_drive         ; Try again if ah
!= 0


 


        ;vc에서
/base 10000 로 헀기 때문에 여기서부터 메모리를 로드


        ;
해야 한다. ES:BX 로 1000:0000h 이기 때문에 아래처럼 한다.


        mov
ax, 1000h           ; es:bx
<-- 1000:0000h


        mov
es, ax


        mov
bx, 0h          ; Destination address
= 0000:1000


 


        mov
ah, 02h             ;
READ SECTOR-command


        ;
두번째 섹터부터 3칸섹터가 커널이미지 섹터다.


        mov
al, 3h             ;
Number of sectors to read = 1


        mov
ch, 0               ;
Cylinder = 0


        ;
현재 첫번째섹터512 이며 두번째 섹터부터 3개 섹터가 커널이미지다


        mov
cl, 02h             ;
Sector = 2


        mov
dh, 0               ;
Head = 0


        int
13h                 ;
Call interrupt 13h


        or
ah, ah               ;
Check for error code


        jnz
reset_drive         ; Try again if ah
!= 0


 


A20Address:


    ;
Set A20 Address line here


 


    CLI


    CALL
enableA20


    STI


    JMP
Continue


enableA20:


        call
enableA20o1


        jnz
short enableA20done


        mov
al,0d1h


        out
64h,al


        call
enableA20o1


        jnz
short enableA20done


        mov
al,0dfh


        out
60h,al


enableA20o1:


        mov
ecx,20000h


enableA20o1l:


        jmp
short $+2


        in
al,64h


        test
al,2


        loopnz
enableA20o1l


enableA20done:


        ret


Continue:   


 


        cli
                    ;
Disable interrupts, we want to be alone


 


        xor
ax, ax


        mov
ds, ax              ;
Set DS-register to 0 - used by lgdt


 


        lgdt
[gdt_desc]         ; Load the GDT descriptor


 


        mov
eax, cr0            ;
Copy the contents of CR0 into EAX


        or
eax, 1               ;
Set bit 0


        mov
cr0, eax            ;
Copy the contents of EAX into CR0


 


        jmp
08h:clear_pipe      ; Jump to code segment, offset
clear_pipe


 


[BITS
32]                       ;
We now need 32-bit instructions


clear_pipe:


        mov
ax, 10h             ;
Save data segment identifyer


        mov
ds, ax              ;
Move a valid data segment into the data segment


register


        mov
ss, ax              ;
Move a valid data segment into the stack segment


register


        mov
esp, 090000h        ; Move the stack pointer
to 090000h


 


        ;
10200h로 점프 200h는 vc로 링킹하면 기본 PE헤더(200h)를 붙여서 더했다.


        ;
10200h 즉 1000:0200h 로 점프


        ;jmp
1000:0200h


        jmp
10200h


        


gdt:
                   ;
Address for the GDT


 


gdt_null:
              ;
Null Segment


        dd
0


        dd
0


 


gdt_code:
              ;
Code segment, read/execute, nonconforming


        dw
0FFFFh


        dw
0


        db
0


        db
10011010b


        db
11001111b


        db
0


 


gdt_data:
              ;
Data segment, read/write, expand down


        dw
0FFFFh


        dw
0


        db
0


        db
10010010b


        db
11001111b


        db
0


 


gdt_end:
               ;
Used to calculate the size of the GDT


gdt_desc:
                      ;
The GDT descriptor


        dw
gdt_end - gdt - 1    ; Limit (size)


        dd
gdt                  ;
Address of the GDT


 


times
510-($-$$) db 0           ;
Fill up the file with zeros


 


        dw
0AA55h                ;
Boot sector identifyer


 




아래처럼 어셈블 하여 오브젝 부트로더 바이너리를 얻어내야
합니다.


nasm -f bin bootsect.asm -o bootsect.bin

 


 


다음은 부트로더와 커널 이미지를 합쳐서 하나의 부팅이미지로 만들어주는 makeboot.c
입니다. 따로 프로젝트를 만들어서 간단히 컴파일해서 makeboot.exe를 만들도록
합니다.


 


#include


 


int
main(int argnr, char *args[])


{


  FILE
*output, *input;


  int
i, bytes_read, sectors_read, bytes_from_file;


  char
buffer[512];


 


  if
(argnr < 4) {


    printf("Invalid
number of parameters.\n\n");


    printf("USAGE:
%s [output] [input 1] [input 2] ... [input n]\n", args[0]);


    printf("Example:
%s a.img bootsect.bin kernel.bin");


    exit(0);


  }


 


  output
= fopen(args[1], "r");


 


//
덮어 쓰기 할꺼냐고 물어보는 코든데 그냥 덮어쓰자. --


  if
(output != NULL) {


    buffer[0]
= 0;


  }


 


  fclose(output);


  output
= fopen(args[1], "wb");


 


  for
(i = 2; i < argnr; i++) {


    input
= fopen(args[i], "rb");


 


    if
(input == NULL) {


      printf("Missing
input file %s. Aborting operation...", args[i]);


      fclose(output);


      exit(1);


    }


 


    bytes_read
= 512;


    bytes_from_file
= 0;


    sectors_read
= 0;


 


    while(bytes_read
== 512 && !feof(input)) {


      bytes_read
= fread(buffer, 1, 512, input);


 


      if
(bytes_read == 0)


        break;


 


      if
(bytes_read != 512)


        memset(buffer+bytes_read,
0, 512-bytes_read);


 


      sectors_read++;


      fwrite(buffer,
1, 512, output);


      bytes_from_file
+= bytes_read;


    }


 


    printf("%d
sectors, %d bytes read from file %s...\n", sectors_read, bytes_from_file,


args[i]);


 


    fclose(input);


  }


 


  fclose(output);


  return
0;


}


 


다음은 커널 소스입니다.


//
bro (bro@shinbiro.com)2004-02-17 - initial ( vc dev envronment setting )


//
starting function


 


void
start()


{   


    char*
hello = "Hello OS!";


    unsigned
char* vidmem = (unsigned char*)0xB8000;


 


    while(
*hello != '\0' )        


    {


        *vidmem++
= *hello++;


        *vidmem++
= 7;        // 문자 기본 속성


    }   


    


    //
커널이 끝나면 안된다. 계속 돌아야한다.   


    for(;;);


}


 


 


[Visual C++ 컴파일 환경 구성하기]


 


위의 커널 소스를 컴파일 하기 위해서 VC 컴파일 환경을 구성해보도록 합니다.


먼저 Win32 Console Application으로 간단히 프로젝트를 생성합니다.


 



 



 


디버깅은 못하니 그냥 Release 모드로 선택한 후 C++ 세팅을 아래처럼 합니다.


 


/nologo
/G4 /Zp1 /ML /W3 /vmg /vd0 /GX /Od /Gf /FAc /Fa"Release/" /Fo"Release/"
/Fd"Release/" /FD /c


 


Link탭의 옵션은 아래처럼 합니다.


/nologo
/base:"0x10000" /entry:"start" /subsystem:console /incremental:no


/pdb:"Release/testos.pdb"
/map:"Release/testos.map" /machine:I386 /nodefaultlib /out:"testos.bin"
/DRIVER /align:512 /FIXED


 


또한 컴파일 작업에서 바로 부트섹터도 컴파일하고 컴파일 끝나면 바로 부트 이미지도
만들도록 Pre-Link와 Post-Build를 작성합니다.


 



 



 


 


컴파일을 해봅니다.


 


Deleting
intermediate files and output files for project 'testos - Win32 Release'.


--------------------Configuration:
testos - Win32 Release--------------------


Compiling...


main.c


Linking...


LINK
: warning LNK4096: /BASE value "10000" is invalid for Windows 95;
image may


not
run


1
sectors, 512 bytes read from file bootsect.bin...


3
sectors, 1536 bytes read from file testos.bin...


 


testos.bin
- 0 error(s), 1 warning(s)


 


 


[VMWare 테스트 환경 구성하기]


 


컴파일을 마치고 나면 아래와 같은 탐색기 모습이 될 것입니다. 마지막으로 셍성된
testos.img 가 우리의 커널 이미지가 됩니다.


 



 


Partcopy.exe 툴을 사용하여 부팅 디스켓에 놓을 수도 있지만 번거롭습니다. 따라서
VMWare에서 직접 이를 디스켓 이미지로 로드하도록 합니다.


 



 


무조건 그냥 기본으로 Next 합니다.


제일 중요한 것은 이렇게 만든 가상 testos 의 플로피 디스켓 부팅 설정입니다.


 



 


Use Floppy Image에 해당 img 패스 맞추도록 합니다.


 


 


이제 실행해봅니다.


 



 



 


 

'KB > tutorial' 카테고리의 다른 글

DCOM 로컬서버 하기  (0) 2005.08.04
Implementation FAT File System  (0) 2004.11.04
How to i386 32bit OS Kernel Compile in VC6  (0) 2004.11.04
DDK in VC with SoftICE  (1) 2004.11.04
COM 형식 라이브러리 사용하기  (0) 2004.11.04
[winsock] 윈속 소켓 정리  (0) 2004.03.19
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST



[시작하면서]


 


이 문서는 2001년에 작성된 것을 기반으로 다시 정리한 것임을 밝힙니다. 현재
더 좋은 솔루션이 있다면 공유하여주시면 감사하겠습니다.


 


다음은 설명에 필요한 환경입니다.


Windows
2000 pro, SoftICE 4.05, VC++ 6.0, Windows DDK 2000 oct


 


DDK xp 버전도 동일했으므로 요즘 나오는 것도 될것으로 생각합니다.


 


[알게된 과정]


 


처음에는 Windows Driver Model이라는 책에서 제공하는 VC app wizard를 사용하여
약간 수정하여 VC Kernel Mode NTDDK 환경을 만들었습니다.


다음과 같은 방법이었습니다.


 


WDM책의 부록 씨디 안의 wdmwiz.awx 파일을 아래 폴더에 놓고 VC를 실행시킵니다.


C:\Program
Files\Microsoft Visual Studio\Common\MSDev98\Bin\IDE


 


그러면 프로젝트 생성시 뜨는 App Wizard에 WDM 드라이버 위자드가 뜨는데 이를
선택한 후 처음 위자드 페이지에서 선택된것을 모두 빼고, Empty Driver를 선택 후,
Finish를 선택합니다.


그런 후 Project/Setting에 가신 후  C++ 섹션에서 아래 부분에 "$(DDKPATH)\inc\ddk\wdm"
을 "$(DDKPATH)\inc\ddk" 으로 수정한후


Link섹션에 wdm.lib를 hal.lib ntoskrnl.lib 로 수정한 후 컴파일 하면 sys파일이
만들어지게 됩니다.


 


이렇게 사용하다보니 하다보니 WDM 위자드 없이 적절히 C++과 Linking 옵션을
주어서 ddk 컴파일 환경을 만들 수 있다는 것을 알게 되었습니다.


 


 


[DDK in VC]


 


Win32 Empty Project로 프로젝트를 생성합니다.


Project / Setting을 선택 한 후 아래와 같이 C++과 Link 옵션을 수정해 주면
됩니다.



 


 


 Debug Setting


 


C++ Option


/nologo
/Gz /MLd /W3 /Zi /Od /Gy /I "$(DDKPATH)\inc" /I


"$(DDKPATH)\inc\ddk"
/FI"$(DDKPATH)\inc\warning.h" /D "WIN32" /D


"_DEBUG"
/D "_WINDOWS" /D "_MBCS" /D _X86_=1 /D i386=1 /D


"STD_CALL"
/D CONDITION_HANDLING=1 /D NT_UP=1 /D NT_INST=0 /D


WIN32=100 /D _NT1X_=100 /D
WINNT=1 /D _WIN32_WINNT=0x0500 /D


_WIN32_IE=0x0400 /D WIN32_LEAN_AND_MEAN=1
/D DBG=1 /D DEVL=1


/D FPO=0 /D "NDEBUG" /D _DLL=1 /D "DRIVER"
/D "_IDWBUILD" /D


"RDRDBG" /D "SRVDBG" /FR"Debug/"
/Fp"Debug/FileSM.pch" /YX


/Fo"Debug/" /Fd"Debug/"
/FD /Zel -cbstring /QIfdiv- /QI0f /GF /QIf /c


 


 


Link Option


hal.lib
ntoskrnl.lib libcmt.lib /nologo /base:"0x10000" /version:4.0


/entry:"DriverEntry@8"
/subsystem:windows /incremental:yes


/pdb:"Debug/TestDev.pdb" /debug
/debugtype:both /machine:I386


/nodefaultlib /out:"debug\TestDev.sys"



/libpath:"$(DDKPATH)\libchk\i386" -MERGE:_PAGE=PAGE


-MERGE:_TEXT=.text
-MERGE:.rdata=.text -SECTION:INIT,d -OPT:REF


-FORCE:MULTIPLE -RELEASE -FULLBUILD



-IGNORE:4001,4037,4039,4065,4070,4078,4087,4089,4096


-osversion:4.00 -optidata
-driver -align:0x20 -subsystem:native,4.00


-debug:notmapped,FULL


 


 


Release Setting


 


C++ Option


/nologo
/Gz /ML /W3 /O1 /I "$(DDKPATH)\inc" /I


"$(DDKPATH)\inc\ddk"
/FI"$(DDKPATH)\inc\warning.h" /D "WIN32" /D


"NDEBUG"
/D "_WINDOWS" /D "_MBCS" /D _X86_=1 /D i386=1 /D


"STD_CALL"
/D CONDITION_HANDLING=1 /D NT_UP=1 /D NT_INST=0 /D


WIN32=100 /D _NT1X_=100 /D
WINNT=1 /D _WIN32_WINNT=0x0500 /D


_WIN32_IE=0x0400 /D WIN32_LEAN_AND_MEAN=1
/D DBG=1 /D DEVL=1


/D FPO=0 /D _DLL=1 /D "DRIVER" /D "_IDWBUILD"
/FR"Release/"


/Fp"Release/FSMon.pch" /YX /Fo"Release/"
/Fd"Release/" /FD /Zel


-cbstring /QIfdiv- /QI0f /GF /Oxs /c


 


Link Option


hal.lib
ntoskrnl.lib /nologo /base:"0x10000" /version:4.0


/entry:"DriverEntry@8"
/subsystem:windows /pdb:none /machine:I386


/nodefaultlib /out:"release\FSMon.sys"
/libpath:"$(DDKPATH)\libfre\i386"


-MERGE:_PAGE=PAGE -MERGE:_TEXT=.text
-MERGE:.rdata=.text


-SECTION:INIT,d -OPT:REF -FORCE:MULTIPLE -RELEASE -FULLBUILD



-IGNORE:4001,4037,4039,4065,4070,4078,4087,4089,4096


-osversion:4.00 -optidata
-driver -align:0x20 -subsystem:native,4.00


-debug:notmapped,minimal


 


 


[VC with SoftICE]


 


VC에서 컴파일한 디바이스 드라이버를 로드 한 후 이를 디버깅 하기 위해서는
일반적으로 WinDBG를 사용하여 두개의 pc를 시리얼로 연결 후, 디버깅 pc의 boot.ini에
debug 옵션을 주어 디버그 할 수 있습니다. 어떤 이는 VMWare에 디버깅 guest os를
두어 가상 시리얼 연결로 디버깅 하기도 합니다. 저자 역시 시리얼로 연결하는
것을 유로 세미나를 들으면서 해보았지만 열악한 환경에서는 무척 매력적이지 안았습니다. 그러는
중에 SoftICE는 싱글머신 소스레벨 디버깅이 된다는 반가운 소식을 들었고, 성공적으로
프로젝트를 완수 할 수 있었습니다.


 


소프트 아이스에서 디버깅 하기 위해서 브레이크 포인트를 걸어야 하는데 소스에
직접 걸거나, 소프트 아이스에서 걸수가 있습니다. 만일 소스에서 걸길 원한다면


 


__asm
int 0x03;


 


을 넣으면 됩니다. 또는


 


DbgBreakPoint();


를 해도 됩니다. 이렇게 브레이크 포인트를 담은 드라이버를 실행 하면 해당 코드에서
소프트 아이스 디버깅 콘솔이 뜨게 됩니다.


 


(참고로 www.sysinternals.com
가시면 커널 모드 디버그 문자열도 보여주는 DebugView 유틸이 있습니다. 이를
이용하면 소스 레벨 디버깅은 불가능하지만 간단하게 디버그 문자열 출력을 볼 수
있습니다. )


 


이제 소프트 아이스를 사용하여 디버깅을 해 보겠습니다.


 


아래와 같이 소프트 아이스를 실행합니다.



 


소프트 아이스가 실행되었다면 Symbol Loader를 실행 한 후 Open으로 디버깅할
디바이스 드라이버 ( sys )를 엽니다.



 


위 그림처럼 툴바의 버튼이 눌렸는지 확인해 보아야 합니다.특히 Package With
Source 버튼은 눌려 있어야 소스 레벨 디버깅이 가능하게 됩니다. 되었다면
왼쪽의 세번째 버튼을 Translate 버튼으로 변환 후 왼쪽 두번째 버튼으로 변환후
생성된 디버깅용 심볼 파일 NMS를 로드하도록 합니다.


 



위와 같은 상황이 되었다면 디버깅 할 수 있는 준비가 끝난 것입니다.


 


만일 시스템에 의해서 이 후 드라이버가 로드되어 실행되고, 소스상의 브레이크
포인트가 실행될때 자동으로 소프트 아이스 디버그 콘솔창이 뜨게 될 것입니다.


 


저 같은 경우 Ctrl+D를 눌러 디버그 콘솔창을 부른 후, file * 명령을 주어 현재
소프트 아이스 디버깅에 올라온 파일이 있는지 확인 한 다음 u '파일명'을 한 후
u DriverEntry 를 해서 DriverEntry 소스 코드가 보이게 되면 그때 브레이크 포인트를
주어 작업을 하였습니다.


 


아래는 간단한 소프트 아이스 명령 입니다.


 


F10
: 한줄 실행한다.


F11
: 함수안으로 Step into 한다.


F3
or SRC : 소스 코드를 보여준다.


G
: 다음 브레이크 포인트까지 코드를 실행한다.


    (소스코드창에서
마우스 클릭으로 브이크 포인트를 걸 수 있다. )    


Watch
data : data라는 변수를 watch한다.    


WL
: 현재 로컬 지역 변수를 모두 보여준다.    


Ctrl+D
: 언제든지 소프트아이스 팝업 콘솔창을 열고 닫을 수 있다.


X
: 소프트 아이스 콘솔 창을 나온다.


 


 


정리


Visual C++의 에디팅과 컴파일 링킹 세팅을 하여 Kernel Mode 드라이버를
작성하는 방법을 알아 보았고, 소프트 아이스를 사용하여 싱글머신 소스레벨 디버깅
하는 방법을 알아 보았습니다.


http://www.debuglab.com 에 가시면 시리얼
케이블 연결이나 VMWare를 사용한 원격 디버깅에 대한 정보를 얻으실 수 있습니다.


 


 

'KB > tutorial' 카테고리의 다른 글

Implementation FAT File System  (0) 2004.11.04
How to i386 32bit OS Kernel Compile in VC6  (0) 2004.11.04
DDK in VC with SoftICE  (1) 2004.11.04
COM 형식 라이브러리 사용하기  (0) 2004.11.04
[winsock] 윈속 소켓 정리  (0) 2004.03.19
snmp 프로그래밍  (0) 2004.03.19
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST
  1. BlogIcon sayheart 2012.12.12 18:19 신고  댓글주소  수정/삭제  댓글쓰기

    감사합니다. ㅇ_ㅇ''




[형식 라이브러리 추출]


원하는 인터페이스의 idl을 midl 컴파일러를 통해 형식 라이브러리(tlb)를 얻어내어
이를 #import 하여 쉽게 COM을 억세스 하도록 합니다.


이해를 돕기 위하여 Microsoft의 MSN Messenger COM Library인 Messenger 인터페이스를
가져다 쓰는 예를 들며 설명합니다.


 


먼저, 원하는 인터페이스의 idl을 얻어내야 합니다. idl 파일이 직접 있다면 이를
이용하고, 없다면 VC++의 툴인 OLE/COM Object Viewer를 연 후, 원하는 COM 오브젝을
찾아내야 합니다.


 


Type Libraries 노드 밑에 Messenger API Type Library를 선택한 후 오른클릭
하여 View를 선택합니다.


( MSN 5.0 이하에서는 All Object 노드에서 Messenger를 찾을 수 있습니다. )



 


이렇게 하면 Messenger API 형식 라이브러리의 idl 을 볼 수 있습니다. 이제 이
idl을 메뉴에서 File/ Save As로 msnmsgr.idl로 저장 한 후 ( 이름은 마음대로 정하셔도
됩니다 ) 우리가 이 인터페이스를 사용하는데 도움을 주는 형식 라이브러리 (.tlb)로
만들어 봅시다.


 


아래는 idl에서 tlb 형식라이브러리를 만드는 예입니다.


D:\>midl
/mktyplib203 msnmsgr.idl


 


MSN 5.0 이하에서는 문제없이 컴파일이 되었는데 MSN 6.0에서는 아래와 같은 결과를
출력합니다.


 


D:\>midl
/mktyplib203 msnmsgr.idl


Microsoft
(R) MIDL Compiler Version 5.01.0164


Copyright
(c) Microsoft Corp 1991-1997. All rights reserved.


Processing
.\msnmsgr.idl


msnmsgr.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\oaidl.idl


oaidl.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\objidl.idl


objidl.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\unknwn.idl


unknwn.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\wtypes.idl


wtypes.idl


.\msnmsgr.idl(76)
: error MIDL2025 : syntax error : expecting a type specificati


on
near "MOPTIONPAGE"


.\msnmsgr.idl(76)
: error MIDL2026 : cannot recover from earlier syntax errors;


aborting
compilation


 


이 idl을 노트패드에서 열어보면 MOPTIONPAGE 의 정의가 아래 쪽에 있는 것을
확인 하 실 수 있습니다. 따라서 인자로 쓰이는 구조체의 정의 부를 해당 인터페이스
정의 부 위로 옮겨야 합니다. ( 예전 버전의 MSN 인터페이스는 그러지 않았는데 새로
인자 형식이 많이 생긴 듯합니다 )


 


그런 식으로 에러 나지 않게 정의부를 위로 옮기시면 아래와 같이 컴파일이 잘
됩니다. 그리고 그 결과로 msnmsgr.tlb 파일이 생기게 됩니다.


 


D:\bro\mine\gosunet\using_tlb>midl
/mktyplib203 msnmsgr.idl


Microsoft
(R) MIDL Compiler Version 5.01.0164


Copyright
(c) Microsoft Corp 1991-1997. All rights reserved.


Processing
.\msnmsgr.idl


msnmsgr.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\oaidl.idl


oaidl.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\objidl.idl


objidl.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\unknwn.idl


unknwn.idl


Processing
C:\Program Files\Microsoft Visual Studio\VC98\include\wtypes.idl


wtypes.idl


 


이제 우리가 원하는 형식 라이브러리를 얻게 되었습니다. 이를 이용하여 COM 프로그래밍을
쉽게 할 수 있습니다.


 


간단하게 이 형식 라이브러리를 이용해서 MSN 자동 로그인을 하도록 하는 기능을
만들어 봅니다.


 


Visual C++을 실행 한 후 다이얼로그 기반의 프로젝트를 생성합니다.


그리고 방금 만든 msnmsgr.tlb를 해당 프로젝트 폴더로 복사합니다.


stdafx.h 파일에 다음을 추가합니다.


 


#import
"msnmsgr.tlb" named_guids, no_namespace


 


버튼 클릭 시 아래와 같은 코드가 실행되게 합니다.


 


    AfxOleInit();
// COM 초기화는 App::InitInstance() 에 두길 추천합니다.


 


    IMessengerPtr
msn;


    msn.CreateInstance(
__uuidof(Messenger), NULL, CLSCTX_LOCAL_SERVER );       


 


    msn->AutoSignin();


 


버튼 클릭으로 위의 코드가 실행되면 msn 메신저가 로그 오프일 때 자동 로그인이
실행되게 됩니다.


 


만일 형식라이브러리를 Import하여 스마트 포인터를 사용하지 않는다면


 


HRESULT
get_MyContacts(


    IDispatch
** ppMContacts


);


 


를 사용하려 한다면 raw한 COM 프로그래밍으로 하려면



  IMessengerGroups* Groups;

  IMessenger2*
msg;


     :


  IDispatch*
lpGroups;


  msg->get_MyGroups(
&lpGroups );


  Groups
= lpGroups;


  lpGroups->Release();


 


이렇게 해야 합니다만, 형식라이브러리 Import 후 스마트 포인터를 사용하면 아래와
같이 간결하고 안전한 코드를 짤 수 있습니다.


 


  IMessengerGroupsPtr
Groups;


  IMessenger2Ptr
msg;


  Groups
= msg->MyGroups;


 


이는 형식라이브러리를 import 할때 인터페이스의 프로퍼티를 자동으로 추가해주어서
가능한것으로 VB에서 프로퍼티 접근 하듯이 무척 간단하고 보기 좋은 프로그래밍을
할 수 있게 됩니다.


 


참고로, 만일 형식라이브러리를 사용하지 않고 직접 스마트 포인터를 사용하게
된다면


 


#include
"msgruaid.h"


#include
"msgrua.h"


#pragma
comment(lib,"msgrguid.lib")


 


_COM_SMARTPTR_TYPEDEF(IMessenger,
__uuidof(IMessenger));


_COM_SMARTPTR_TYPEDEF(IMessenger2,
__uuidof(IMessenger2)););


_COM_SMARTPTR_TYPEDEF(IMessenger3,
__uuidof(IMessenger3)););


 :


위처럼 사용하여 스마트 포인터를 쓸 수는 있지만 프로퍼티를 위에서 했듯이 불편한
방법으로 접근해야 할 것입니다.


 


 


[이벤트 받기]


 


 이벤트를 받는 코드를 raw하게 짜려 한다면 무척 불편하기도 합니다. 따라서
저는 ATL의 이벤트 받는 방식으로 접근합니다. 장점은 무엇보다 이벤트 메소드를
정의해서 쓰므로 가독성도 좋고 코드도 보기 좋다는 점입니다.


 


 만일 signin 말고 다른 이벤트를 받기 위해서 이벤트의 메소드 아이디를
알고 싶으시면 모든 인터페이스의 정의가 되어 있는 idl 파일에서 그 값을 보실 수
있습니다.


 


예를 들어 아래 0x400의 OnSign 이벤트는


BEGIN_SINK_MAP(CMessengerEvents)


   SINK_ENTRY_EX(0,
DIID_DMessengerEvents, 0x400, OnSigninx)


END_SINK_MAP()


 


idl에서 아래에서 찾아볼 수 있고 id는 0x400인것을 확인 하실 수 있습니다.


    [


      uuid(C9A6A6B6-9BC1-43A5-B06B-E58874EEBC96),


      helpstring("Messenger
Events"),


      hidden


    ]


    dispinterface
DMessengerEvents {


        properties:


        methods:


            [id(0x00000415),
helpstring("A new group has been created.")]


            void
OnGroupAdded(


                            [in]
long hr,


                            [in]
IDispatch* pMGroup);


                :


        [id(0x00000400),
helpstring("Signin attempt complete.")]


            void
OnSignin([in] long hr);


 


 또한 재미있는 팁은 msn에서 파일 보내기 메소드를 실행시 파일을 바로
보내지 못하게 msn 4.x 이후에는 보안상 막아두었습니다. ( msn 바이러스가 퍼지지
못하도록 하기 위해서 였겠죠 ) 제 프로젝트 성격상 파일 보내기가 자동으로 되어야
하기 때문에 작성된 저의 트릭을 보실 수 있을 것입니다. 98/2000/xp에서 잘 수행되었습니다.


  


 이벤트 받는 코드를 포함한 모든 저의 샘플 코드를 적어 놓도록 하겠습니다.


 


msnx.h


#if
!defined(AFX_MSNX_H__F7BD2C8E_8C37_4B4F_B106_CAB2E772167E__INCLUDED_)


#define
AFX_MSNX_H__F7BD2C8E_8C37_4B4F_B106_CAB2E772167E__INCLUDED_


 


#if
_MSC_VER > 1000


#pragma
once


#endif
// _MSC_VER > 1000


 


#include
<atlbase.h>


 


//You
may derive a class from CComModule and use it if you want to override


//something,
but do not change the name of _Module


class
CExeModule : public CComModule


{


public:


    CExeModule();


};


extern
CExeModule _Module;


 


#include
<atlcom.h>


#include
<atlwin.h>


 


 


#import
"msn6.tlb" named_guids, no_namespace


 


 


IMessengerPtr
GetMessenger();


 


class
CMessengerEvents :


    public
IDispEventImpl<0,CMessengerEvents,&DIID_DMessengerEvents,&LIBID_MessengerAPI,1,0>


{


public:


    IUnknownPtr     m_psinkObj;


    bool
Create( IUnknownPtr p )


    {


        m_psinkObj
= p;


        return
( DispEventAdvise(p,&DIID_DMessengerEvents) == S_OK);


    }


    void
Close()


    {


        if(
m_psinkObj )


        {


            DispEventUnadvise(m_psinkObj);


            m_psinkObj
= NULL;


        }


    }


 


    virtual
~CMessengerEvents() { Close(); }


 


    STDMETHOD
(OnSigninx) ( long hr )


    {


        return
S_OK;


    }


 


BEGIN_SINK_MAP(CMessengerEvents)


   SINK_ENTRY_EX(0,
DIID_DMessengerEvents, 0x400, OnSigninx)


END_SINK_MAP()


};


 


 


class
CMessengerAPI : public IMessengerPtr, public CMessengerEvents


{


public:


    


    virtual
~CMessengerAPI()


    {


        Close();


    }


    void
Close()


    {


        CMessengerEvents::Close();


    }


 


    bool
Create()


    {


        IMessengerPtr
msn = GetMessenger();


        if(
msn )


        {


            *(IMessengerPtr*)this
= msn;


            return
CMessengerEvents::Create( msn );


        }


        return
false;


    }


    


    CString
GetMyID();


    CString
GetMyNick();


 


    IMessengerWindowPtr
SendFile( LPCTSTR pszSigninName, LPCTSTR pszPath );


    IMessengerWindowPtr
InstantMessage( LPCTSTR pszSigninName );


 


    STDMETHOD
(OnSigninx)( long hr )


    {


        //
로그인 될때 이벤트가 떨어집니다.


        return
S_OK;


    }


};


 


#endif
// !defined(AFX_MSNX_H__F7BD2C8E_8C37_4B4F_B106_CAB2E772167E__INCLUDED_)


 


msnx.cpp


#include
"stdafx.h"


#include
"resource.h"


#include
"msnx.h"


 


#ifdef
_DEBUG


#undef
THIS_FILE


static
char THIS_FILE[]=__FILE__;


#define
new DEBUG_NEW


#endif


 


//////////////////////////////////////////////////////////////////////


//
Construction/Destruction


//////////////////////////////////////////////////////////////////////


CExeModule
_Module;


BEGIN_OBJECT_MAP(ObjectMap)


END_OBJECT_MAP()


 


CExeModule::CExeModule()


{


    Init(ObjectMap,
NULL, &LIBID_MessengerAPI);


}


 


IMessengerPtr
GetMessenger()


{


    IMessengerPtr
msn = NULL;


    try


    {


        msn.CreateInstance(
__uuidof(Messenger), NULL, CLSCTX_LOCAL_SERVER );       


    }


    //catch(_com_error
&e)


    catch(...)


    {


        //PrintComError(e);


        return
NULL;


    }


 


    return
msn;


}


 


IMessengerWindowPtr
CMessengerAPI::InstantMessage( LPCTSTR pszSigninName )


{


    IMessengerWindowPtr
window = NULL;


    try


    {   


        IMessengerPtr
msn = *this;


        IMessengerContactPtr
contact = (IMessengerContactPtr)msn->GetContact( (_bstr_t)pszSigninName,
(_bstr_t)msn->GetMyServiceId() );


        if(
contact )


            window
= msn->InstantMessage( _variant_t((IDispatch*)contact,true));


    }


    //catch(_com_error
&e)


    catch(...)


    {


        //PrintComError(e);


        return
NULL;


    }


    return
window;


}


 


CString
CMessengerAPI::GetMyID()


{


    CString
id;


    try


    {   


        IMessengerPtr
msn = *this;


        id
= (LPCTSTR)msn->MySigninName;


    }


    //catch(_com_error
&e)


    catch(...)


    {


        //PrintComError(e);


        return
_T("");


    }


    


    return
id;


}


 


CString
CMessengerAPI::GetMyNick()


{


    CString
nick;


    try


    {   


'KB > tutorial' 카테고리의 다른 글

How to i386 32bit OS Kernel Compile in VC6  (0) 2004.11.04
DDK in VC with SoftICE  (1) 2004.11.04
COM 형식 라이브러리 사용하기  (0) 2004.11.04
[winsock] 윈속 소켓 정리  (0) 2004.03.19
snmp 프로그래밍  (0) 2004.03.19
MSN 프로그래밍 하기  (0) 2004.03.19
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST


오전 11:26 2002-01-11
조경민 bro(bro@shinbiro.com) 윈속 소켓 정리
=====================================================================

소켓이 생성되면 기본적으로 블러킹 소켓(동기소켓,blocking socket)이 된다.
윈속 블러켕 소켓에서는 해당 작업이 바로 처리되지 못한다면 수행이 완료될때
까지 멈추게 된다.
소켓 리소스를 쓰지 못하는 상황에서는 수행은 실패(SOCKET_ERROR)로
리턴되며 WSAGetLastError() 호출시 WSAEWOULDBLOCK에러로 발생되게 된다.
이런 경우엔, 다시 쓸 수 있게 되는 시점에서 다시 해당 수행을 시도해야한다.

넌블러킹소켓(비동기소켓,non-blocking socket)으로 만들려면
비동기 소켓으로 만드는 법
1. ioctlsocket를 FIONBIO 명령으로 주거나
2. WSAEventSelect
3. WSAAyncSelect
를 이용한다.

Overlapped Socket은 프로그래밍 모델상 Blocking Socket과 Non-Blocking socket
과는 관계가 없다. 이론적으로는 블러킹소켓에서든 넌블러킹소켓에서든 오버랩드
기능은 제공되어야 하나 현재, 윈속은 넌블러킹 모드의 소켓에 대해서만 오버랩드
를 지원하게 된다.

WSAAsyncSelect는 소켓 이벤트 통보를 윈도우 메시지에 연결시키게 되어 싱글 스레드
GUI 애플리케이션을 위한 최고의 모델이 될 수 있다.

WSAEventSelect는 WSAWaitForMultipleEvents를 이용해 네트워크 이벤트가 도착할때까지
기다리다가 이벤트가 도착하면 WSAEnumNetworkEvents를 이용하여 소켓 이벤트 상태를
얻어와 소켓 이벤트 통보를 할 수 있도록 한다.


오버랩드 모드로 소켓을 생설하려면
( 오버랩드를 쓰지 않더라도 WSASocket을 쓰면 WSA_FLAG_OVERLAPPED를 함께
주는것이 Win95/NT 호환문제로 추천한다
INFO: WSA_FLAG_OVERLAPPED Is Needed for Non-Blocking Sockets MSDN 참조)

WSASocket함수 호출시 WSA_FLAG_OVERLAPPED를 플래그로
함께 주면 오버랩드 소켓으로 생성된다. 이때 어떤 수행이 바로 완료되지 못하
게 되면 바로 해당 수행에 대해 실패(SOCKET_ERROR)가 리턴되며 WSAGetLastError()
나 GetLastError() 로 실패 이유를 체크하면 WSA_IO_PENDING나 ERROR_IO_PENDING가
리턴딘다. 이렇게 리턴되는 경우엔 언젠가 해당 작업이 완료가 될것이므로 안료루틴을
만들어 타게 하거나 차후에 GetOverlappedResult 함수로 완료가 되었는지 알아내거나
완료될때까지 기다릴 수 있다.
오버랩드 속성으로 소켓을 생성했다고 오버랩드 수행이 일어나지는 않는다.
예로 WSARecv/WSASend시 Overlapped 구조체나 Completion 함수를 NULL로 주게되면
오버랩드 동작이 아닌 recv와 같은 동작을 하게된다.

Winsock 1 에서는 오버랩드 소켓을 생성(socket)후 ReadFile/WriteFile 등으로
오버랩드 I/O 작업을 하였다.
Winsock 2에서는 WSASocket를 WSA_FLAG_OVERLAPPED로 오버랩드 솤켓을 생성 후
ReadFile/WriteFile 또는 WSASend, WSASendTo, WSARecv, WSARecvFrom 함수를 이용해
오버랩드 I/O 작업을 할 수 있다.

만일 setsockopt를 SO_RCVBUF와 SO_SNDBUF 옵션으로 TCP스택의 받는 버퍼와 보내는
버퍼의 크기를 0 으로 주게 되면, I/O 작업 호출시 바로 직접 사용자의 버퍼를
이용하여 I/O 작업을 하게끔 할 수 있다. 이는 넌블러킹 소켓에서 Overlapped 소켓
에서 매번 I/O 호출에 대해 TCP스택과 사용자 버퍼간에 일어나는 복사를 생략하여
성능을 향상 시킬 수 있다. 그러나 이렇게 하기 위해서는 사용자 버퍼가 오버랩드
수행이 이뤄지는 동안 그리고 오버랩드 수행이 끝나기 전까지는 그 사용자 버퍼를
접근(access)해서는 안될 것이다.

오버랩드 수행이 완료되었는지 알기 위해서는 다음과 같은 방법이 있을수 있다.
1. WSAOVERLAPPED구조체 안에 hEvent 핸들을 써서 이벤트 핸들이 signal될때까지
   기다린다.
2. GetOverlappedResult또는 WSAGetOverlappedResult를 이용하여 오버랩드 I/O
   수행 상태를 계속 체크해 볼 수 있다.(poll) NT의 경우 WSAOVERLAPPED 구조체내의
   hEvent를 NULL로 줘도 되지만, 95에서는 오버랩드 구조체에서는 사용자에 의해
   리셋되는 이벤트 핸들이 필요하다. ( maual reset event handle )
3. ReadFileEx, WriteFileEx, WSARecv, WSARecvFrom, WSASend, WSASendTo함수들은
   I/O 작업이 완료되면 이를 위해 설정했던 완료함수가 호출된다. 만일 완료함수가
   호출되게끔 하려면 오버랩드 I/O 수행을 시작한 이후 Win32 Wait 함수나 WSA 버전
   의 Wait 함수를 써서 non-signal인 핸들인 상태에서 I/O작업을 호출한 스레드가
   alertable wait state( 깨울수 있는 대기 상태 )로 놓아야 한다.
   Overlapped I/O 수행이 끝나면 완료 함수가 호출되고, wait 시키는 함수(SleepEx 같은것)는
   WAIT_IO_COMPLETION을 리턴하고, I/O 작업을 호출했던 스레드는 잠에서 깨어나
   다음 코드를 진행 하게 된다. (wake up)
4. Overlapped I/O 속성이 있으면, GetQueuedCompletionStatus와 associate a socket를
   이용한 Windows NT I/O Completio Port( IOCP )를 이용할 수 있다.
   IOCP를 이용하면 완료 함수를 작성하지 않아도 되며, 시그널되기 위한 이벤트 핸들을
   wait 시킬 필요도 없으며, Overlapped 수행의 상태를 계속 감시(poll)할 필요도없다.
   IOCP를 쓰게되면 오버랩드 소켓핸들은 IOCP에 추가되고 I/O API함수들 ( recv, recvfrom,
   send, sendto를 제외한 Read/WriteFile 등 함수 및 WSASend/Recv 관련 함수들)을 이용해
   오버랩드 수행을 할 수 있다. I/O 완료 패킷을 얻기 위해 GetQueuedCompletionStatus
   API에 의한 wait에 의해 작업용 워커 스레드를 블럭시키게 된다. Overlapped I/O가 완료
   되면 I/O Completion Packet이 GetQueuedCompletionStatus에서 리턴되게 된다.
   Windows NT에서 제공되는 IOCP를 이용하면 확장성있고 높은 수준의 서버 성능을 매우
   간단한 스레딩 코드와 블러킹 코드만을 이용하여 구현 할 수 있다.

'KB > tutorial' 카테고리의 다른 글

DDK in VC with SoftICE  (1) 2004.11.04
COM 형식 라이브러리 사용하기  (0) 2004.11.04
[winsock] 윈속 소켓 정리  (0) 2004.03.19
snmp 프로그래밍  (0) 2004.03.19
MSN 프로그래밍 하기  (0) 2004.03.19
COM+ 컴포넌트 만들기  (0) 2004.03.19
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST


.

'KB > tutorial' 카테고리의 다른 글

COM 형식 라이브러리 사용하기  (0) 2004.11.04
[winsock] 윈속 소켓 정리  (0) 2004.03.19
snmp 프로그래밍  (0) 2004.03.19
MSN 프로그래밍 하기  (0) 2004.03.19
COM+ 컴포넌트 만들기  (0) 2004.03.19
MFC DLL 간단 설명  (0) 2004.03.19
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST


Messenger Application Programming Interfaces
2002-08-27 조경민 (bro@shinbiro.com)

.NET Messenger Service 개요
.NET 메신저 서비스에 접속하는 MS의 클라이언트 제품은 두가지 종류가 있다. 하나는 Microsoft® Windows® Messenger라고 불리는 Windows XP에 기본으로 내장된 메신저 제품( 이하 XP메신저 또는 윈도우메신저라 함)과 여러분이 인터넷에서 다운받아서 쓰고 있는 MSN Messenger가 그것이다.
.NET 메신저 서비스는 여러가지가 있을 수 있으며 그중 기본적인 것이 Microsoft .NET Messenger Service이다. 그리고 그 외 Microsoft® Exchange Instant Messaging같은 다른 서비스도 있을 수 있다. 메신저 클라이언트는 이런 서비스에 접속하여 .NET Messenger 서비스를 이용하게 된다. Microsoft .NET Messenger Service를 메신저 서버라고 짧게 부르도록 한다.

메신저와 관련된 프로그래밍 인터페이스(API)는 세가지 종류로 나뉜다.
-        Messenger APIs
현재 설치되어 실행중인 MSN메신저와 연동되어 여러가지 재미있는 일을 할 수 있는 API로 XP메신저와 MSN메신저 둘다 해당되는 API이다.
주로, 두가지 기능에 초점이 맞춰져 있으며, 메신저 친구 리스트 관리와 메신저 클라이언트 자체에 대한 자동화(Automation)이다.
-        Messenger Service APIs
서비스API는 메신저클라이언트가 접속해서 새로운 서비스를 받을 수 있도록 하는 서비스 API이다. 서비스API는 메신저서버와 연동이 필요없는 API이다. 그러나 메신저서버를 통하지 않더라도 .NET Messenger Service를 통하게 된다면, .NET Messenger Service 동의서에 동의하고 특별한 ID와 key 쌍을 받아야 한다. 이 서비스 API를 이용하여 만들어진 테스트 서버 혹은 상용 서버는 msgrapi@microsoft.com 에 메일을 보내서 사용 가능한 ID와 Key를 부여 받아야 한다.  주료 서비스로 들어온 클라이언트를 인증과정을 거친후 해당 서비스의 세션을 생성하고, 해당 서비스에 다른 클라이언트를 초대하여 참여하여 서비스가 이뤄지게 된다.
-        Messenger Add-In APIs
XP에서 동작하는 윈도우 메신저의 애드온 API들이다. 이 API를 통해서 이메일보내기시 특수한 작업을 하거나, 채팅룸 가기시 특수한 다른 채팅서비스를 하거나, 개인정보를 수정하거나, 문자메시지를 보내거나, 광고 같은것을 띄울 수 있다.

'KB > tutorial' 카테고리의 다른 글

[winsock] 윈속 소켓 정리  (0) 2004.03.19
snmp 프로그래밍  (0) 2004.03.19
MSN 프로그래밍 하기  (0) 2004.03.19
COM+ 컴포넌트 만들기  (0) 2004.03.19
MFC DLL 간단 설명  (0) 2004.03.19
MySQL 에서 Text file Import 하기.doc  (0) 2004.03.19
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST


오후 12:04 2002-03-12
COM+ 컴포넌트 만들기
조경민 ( bro@shinbiro.com ) from 문형석
=================================================

일딴 MTS 서포트 프로젝트를 시작하자
--------------------------------------------------
1. VC에서 ATL 프로젝트를 시작한다.
  (Inproc server dll로 선택한다. MTS에 올리면 MTS가
  알아서 컴포넌트를 격리시켜 Out-Of-Process 컴으로
  돌려준다. - DLLHOST.EXE에 붙어 돌아가게 된다. -
  그리고 Out-Of-Process로 돌아가야 하기때문에 Aggregation
  되게 바꾸는 이유가 된다)
2. Support MTS를 선택한다.

3. ClassView에서 오른클릭 New ATL Object한 후
   MS Transaction Server Component를 선택한다.
   TestMTS라고 이름짓고, Nest 탭에서
   Support IObjectControl과 Can be pooled를 선택한다.
   생성한다.

4. 헤더파일을 바꿔야 한다.
   싱글 스레드 모델을 멀티로 바꾼다.
   (VC 6가 나올당시 NT4.0기반의 MTS하였으나 이제는
   윈도우 2000기반의 COM+의 Netural모델이 생겨이를 쓰는것이다)
class ATL_NO_VTABLE CTestMTS :
        public CComObjectRootEx<CComMultiThreadModel>,        // 멀티로 바꾼당

   또한 Aggregation을 안한다에서 한다로 바꾼다.
//DECLARE_NOT_AGGREGATABLE(CTestMTS)        // 바뀌었다.
DECLARE_AGGREGATABLE(CTestMTS)

   Resource Files에서 TestMTS.rgs에 보면 스레딩 모델이 있는데
   역시 아래처럼 바꾼다.  
                        val ThreadingModel = s 'Neutral'

      
이젠 MTS 기능을 넣자
--------------------------------------------------------
아래 세 기본적인 MTS 서포트 함수가 미리 제공되는데 하는일을
이해한후 넣는다.

HRESULT CTestMTS::Activate()
{
        HRESULT hr = GetObjectContext(&m_spObjectContext);
        // 이부분에다가 맴버변수,로직의 초기화를 넣는다.


        if (SUCCEEDED(hr))
                return S_OK;
        
        return hr;
}

BOOL CTestMTS::CanBePooled()
{
        // 클라이언트에서 TestMTS를 다 쓰고 Set Nothing시킬때
        // 호출되는데 아직 Deactivate가 다 끝나지 않았다면
        // 좀더 기다리라고 FALSE를 리턴해야 할것이다.
        return TRUE;
}

void CTestMTS::Deactivate()
{
        // 초기화했던거 다 릴리즈 해준다.
        m_spObjectContext.Release();
}

내 ATLCOM 메소드를 추가한다.

이젠 MTS에 올려넣자
------------------------------------------------------------
시작->프로그램->관리도구->구성요소 서비스 로 MTS를 실행시킨다.
콘솔루트->구성요소서비스->컴퓨터->내컴퓨터->COM+응용 프로그램에서
오른클릭 새로만들기 응용프로그램 한 후 빈 응용 문서 패키지를
만든다. 이름은 아싸패키지
그리고 패키지안의 구성요소에서 오른클릭 추가해서 새 구성요소를
설치를 선택하여 해당 컴파일한 TestMTS.dll을 로드한다.

패키지의 오른클릭 시스템 종료로 메모리에 올라온 컴포넌트를 내릴수있다.
(재컴파일시 좀 해주어야 한다)

이젠 디버깅이다
------------------------------------------------------------
VC에서 TestMTS프로젝트를 로드한다. 주메뉴->프로젝트세팅에서
디버깅 탭에서
Executable for debug session을
C:\winnt\system32\dllhost.exe를 선택한다.
Program arguments:를
/ProcessID:{FC2A8088-1299-4E28-AFDF-8A757415FA64}
로 선택한다. 프로세스 아이디 뒷부분은
아까 MTS에 아싸패키지 오른클릭 등록정보 고급에 보면
디버깅 섹션에 디버거 경로의 뒷부분을 참고한다.

VC에서 디버깅할 위치에 브레이크포인트를 걸고, F5로 디버깅시작
그럼 VB에서 또는 웹페이지에서 해당 MTS TestMTS객체를 호출할때
VC에서 디버깅 브레이크가 되어서 디버깅할 수있다.

이젠 분산 DCOM이다
------------------------------------------------------------
MTS에서 아싸패키지 오른클릭 내보내기를 한 후
내보내기 설치MSI이름을 정해준 후 프록시 패키지로 선택한 후
종료하면 MSI가 생긴다. 이를 다른 컴터에 깔면 된다.
(같은 MTS 서버가 깔린 컴터여야 한다)
그리고 그 컴퓨터 사용자가 내 MTS 컴포넌트를 쓰려면 사용자계정을
갖아야 한다.
아싸패키지 역활에서 오른클릭 새 역활 후 '같이쓰자' 만든후
사용자를 새로 만들어 해당 사용자를 추가하면 된다.

MTS컴포넌트서버                  클라이언트
--------------                   -----------
TestMTS.dll풀링  <-- DCOM 호출 -- Proxy

'KB > tutorial' 카테고리의 다른 글

snmp 프로그래밍  (0) 2004.03.19
MSN 프로그래밍 하기  (0) 2004.03.19
COM+ 컴포넌트 만들기  (0) 2004.03.19
MFC DLL 간단 설명  (0) 2004.03.19
MySQL 에서 Text file Import 하기.doc  (0) 2004.03.19
MSSQL 데이터 MySQL로 Migration하기  (0) 2004.03.19
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST


오후 5:05 2002-01-23
조경민 MFC DLL 간단 설명
========================================================================

MFC에서 DLL의 종류는 두가지이다. _USRDLL(MFC정규DLL)과 _AFXDLL(MFC확장DLL)이다.
_USRDLL은  MFC라이브러리의 초기화 종료를 위해 하나의 CWinApp 객체를 필요로한다.
_AFXDLL은 CWinApp 객체를 필요로 하지 않는다. 이는 애플리케이션의 MFC 클래스들을
공유하기 때문이다.

AFXEXT는 AFXDLL이다.
_AFXDLL이 정의가 되면, AfxGetResourceHandle함수로 리턴되는 값은 MFC의 전역 변수로
저장된 값이 리턴되는데 이 값은 EXE나 확장 DLL또는 MFC DLL 모두가 공유하는 값이다.
엄격히 말해, 리소스를 로드한다는 말은 인스턴스 핸들 보다는 모듈 핸들가 연관있다.
(인스턴스는 각 다른 데이타를 갖는 모듈(즉, 코드와 리소스들)들을 공유한다. )
DLL은 EXE의 핸들과 다른 모듈 핸들을 갖는다.
::GetModuleHandle을 DLL의 모듈핸들을 얻기 위해 쓸수 있고, AfxSetResourceHandle을
통해서 DLL이 리소스를 찾을려고 할때 바로 찾을수 있게 처음에 위치하게 할 수 있다.
그러나 알아둘점은 이렇게 하게 되면 .EXE 모듈을 찾을 수 없게 된다. 그래서 보통
AfxSetResourceHandle을 호출하기전에 AfxGetResourceHandle로 핸들값을 복사해 저장한다.
DLL리소스의 로드가 끝나면 다시 EXE모듈 핸들을 세팅하면된다.

- 멀티스레드와 MFC
MSDN : CWnd Derived MFC Objects and Multi-threaded Applications
멀티스레드 애플리케이션을 MFC를이용하여 짠다면, MFC 객체를 스레드간에 넘겨서는
안된다. 이 일반적인 룰에서 한 스레드는 오직 자신이 생성한 MFC객체들만 접근해야한다.
이렇게 하지 않으면 assertion이나 기대치 못한 행동등의 런타임 문제를 발생시킨다.

Win32 프로세스 주소영역 안에서 동작하는 모든 스레드는 모든 전역,static 자료를
접근할 수 있다. 스레드는 TLS(Thread-Local-Storage)로 스레드 자신만의 자료를 쓸수있다.

윈도우는 스레드들에 의해 관리되므로 멀티스레드 환경하에서, MFC는 Thread Local Storage
안에 임시적적이거나 영구적인(temporary and permanent) 윈도우 핸들 맵을 관리한다.
물론 다른 GDI 객체나 디바이스 컨텍스트를 위한 핸들 맵 역시 그렇게 관리한다.
TLS에 관리되는 윈도우 핸들 맵은 여러 스레드들 사이에서 동시에 접근되어선 안된다.

CHandleMap::LookupPermanent()와 CHandleMap::LookupTemporary() 함수는 이런 사실들의
직접적인 예이다. 주어진 윈도우 핸들에 대해 이런 함수들은 MFC CWnd를 상속받은 객체와
연관있는 현재 스레드의 permenent하거나 temporary한 윈도우 핸들맵을 체크한다.
만일 그런 함수로 다른 스레드에 의해 생성된 윈도우 객체에 대한 MFC 객체를 찾으려고 한다면
함수 호출은 실패가 될것이다.

CHandleMap::LookupPermanent()와 CHandleMap::LookupTemporary()를 호출하는 함수들이
몇개 있다. CWnd::AssertValid()( ASSERT_VALID라는 매크로도 역시) 그런 함수들중 하나다.
이 함수는 객체의 유효성을 체크하기 위해 쓰인다. 만일 AssertValid()가 MFC 객체의 m_hWnd
맴버를 핸들맵에서 찾지 못하거나, 잘못된 엔트리에서 찾게되면 이 함수는 assertion을 일으킨다.
VC++ 2.1에서는 이런 assertion은 Wincore.cpp파일의 797과 788번재줄이며
VC++ 2.2에서는 Wincore.cpp파일의 804와 805번째줄,
VC++ 4.0에서는 Wincore.cpp파일의 871과 872번째 줄에 assertion이 생기게 된다

ASSERT_VALID 매크로는 MFC 소스코드 전반에 걸쳐 많이 존재한다. 따라서 만일 특정스레드에서
다른 스레드에 속하는 MFC 윈도우 객체에 ASSERT_VALID를 호출한다면, assertion이 발생한다.
만일 assertion이 발생하지 않았더라도, 다른 스레드에 생성된 윈도우를 조작하는것은 허락
된 것이 아니므로 비정상적으로 동작할 것이다.

이런경우 MFC 객체를 이용하지 말고 윈도우 핸들로 작업을 해야 한다. 윈도우 핸들을 스레드간
에 전달하는것은 안전하다. 만일 스레드 A에서 스레드 B로 윈도우 핸들을 전달했다면, 스레드 B는
이 윈도우 핸들로 send 나 post message를 할 수 있다. 이 메시지가 처리될때, 스레드 A의
컨텍스트로 바뀌게 되고, CWnd::AssertValid()은 스레드 A의 윈도우 핸들맵을 체크하고 성공을
리턴할 것이다.

이런 시나리오에서 스레드 B는 역시 CWnd::FromHandle()함수를 통해서 스레드 B의 임시적인
(temporary)핸들맵에 임시적인 CWnd객체를 생성할 수 있다. 그러나 이 객체는 스레드 A의 핸들
맵에 존재하는 MFC 객체와 동기화하는 방법이 없으므로 사용상 제약을 받을 것이다.


-Accessing MFC Objects from Non-MFC Threads
MSDN : Multithreading: Programming Tips
만일 CWinThread 객체를 이용하지 않고 멀티스레드 애플리케이션을 만들었다면 다른 스레드의
MFC 객체를 접근할 수 없을 것이다. 반대로 말해 MFC객체를 이차 스레드에서 접근하려면
반드시 AfxBeginThread를 사용해 CWinThread의 GUI 스레드 방식을 쓰거나 . 워커 스레드 방식
으로 써야 한다.
이는 멀티스레드 애플리케이션이 클래스 라이브러리를 초기화하고 필요한 내부변수를 핸들링
하기 위한 것이다.

CWinThread* AFXAPI AfxBeginThread(AFX_THREADPROC pfnThreadProc, LPVOID pParam,
        int nPriority, UINT nStackSize, DWORD dwCreateFlags,
        LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
        pfnThreadProc;
        pParam;
        nPriority;
        nStackSize;
        dwCreateFlags;
        lpSecurityAttrs;

        return NULL;
#else
        ASSERT(pfnThreadProc != NULL);

        CWinThread* pThread = DEBUG_NEW CWinThread(pfnThreadProc, pParam);
        ASSERT_VALID(pThread);

        if (!pThread->CreateThread(dwCreateFlags|CREATE_SUSPENDED, nStackSize,
                lpSecurityAttrs)) // CreateThread에서
        {
                pThread->Delete();
                return NULL;
        }
        VERIFY(pThread->SetThreadPriority(nPriority));
        if (!(dwCreateFlags & CREATE_SUSPENDED))
                VERIFY(pThread->ResumeThread() != (DWORD)-1);

        return pThread;
#endif //!_MT)
}


BOOL CWinThread::CreateThread(DWORD dwCreateFlags, UINT nStackSize,
        LPSECURITY_ATTRIBUTES lpSecurityAttrs)
{
#ifndef _MT
        dwCreateFlags;
        nStackSize;
        lpSecurityAttrs;

        return FALSE;
#else
        ASSERT(m_hThread == NULL);  // already created?

        // setup startup structure for thread initialization
        _AFX_THREAD_STARTUP startup; memset(&startup, 0, sizeof(startup));
        startup.pThreadState = AfxGetThreadState(); // 스레드 상태를 얻어온다.
        startup.pThread = this;
        startup.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
        startup.hEvent2 = ::CreateEvent(NULL, TRUE, FALSE, NULL);
        startup.dwCreateFlags = dwCreateFlags;
        if (startup.hEvent == NULL || startup.hEvent2 == NULL)
        {
                TRACE0("Warning: CreateEvent failed in CWinThread::CreateThread.\n");
                if (startup.hEvent != NULL)
                        ::CloseHandle(startup.hEvent);
                if (startup.hEvent2 != NULL)
                        ::CloseHandle(startup.hEvent2);
                return FALSE;
        }

        // create the thread (it may or may not start to run)
        m_hThread = (HANDLE)_beginthreadex(lpSecurityAttrs, nStackSize,
                &_AfxThreadEntry, &startup, dwCreateFlags | CREATE_SUSPENDED, (UINT*)&m_nThreadID);
        if (m_hThread == NULL)
                return FALSE;



// MFC 스레드 상태 구조체 안에는 여러 핸들맵등 중요한 스레드별로 관리되는
// 핸들들이 있다. 따라서 AfxBeginThread를 이용해야만 2차 스레드가 핸들맵을
// 옳바로 전달 받을 수 있게 된다.

// AFX_MODULE_THREAD_STATE (local to thread *and* module)
class AFX_MODULE_THREAD_STATE : public CNoTrackObject
{
public:
        AFX_MODULE_THREAD_STATE();
        virtual ~AFX_MODULE_THREAD_STATE();

        // current CWinThread pointer
        CWinThread* m_pCurrentWinThread;

        // list of CFrameWnd objects for thread
        CTypedSimpleList<CFrameWnd*> m_frameList;

        // temporary/permanent map state
        DWORD m_nTempMapLock;           // if not 0, temp maps locked
        CHandleMap* m_pmapHWND;
        CHandleMap* m_pmapHMENU;
        CHandleMap* m_pmapHDC;
        CHandleMap* m_pmapHGDIOBJ;
        CHandleMap* m_pmapHIMAGELIST;

        // thread-local MFC new handler (separate from C-runtime)
        _PNH m_pfnNewHandler;

#ifndef _AFX_NO_SOCKET_SUPPORT
        // WinSock specific thread state
        HWND m_hSocketWindow;
#ifdef _AFXDLL
        CEmbeddedButActsLikePtr<CMapPtrToPtr> m_pmapSocketHandle;
        CEmbeddedButActsLikePtr<CMapPtrToPtr> m_pmapDeadSockets;
        CEmbeddedButActsLikePtr<CPtrList> m_plistSocketNotifications;
#else
        CMapPtrToPtr* m_pmapSocketHandle;
        CMapPtrToPtr* m_pmapDeadSockets;
        CPtrList* m_plistSocketNotifications;
#endif
#endif
};

class _AFX_THREAD_STATE : public CNoTrackObject
{
public:
        _AFX_THREAD_STATE();
        virtual ~_AFX_THREAD_STATE();

        // override for m_pModuleState in _AFX_APP_STATE
        AFX_MODULE_STATE* m_pModuleState;
        AFX_MODULE_STATE* m_pPrevModuleState;

        // memory safety pool for temp maps
        void* m_pSafetyPoolBuffer;    // current buffer

        // thread local exception context
        AFX_EXCEPTION_CONTEXT m_exceptionContext;

        // CWnd create, gray dialog hook, and other hook data
        CWnd* m_pWndInit;
        CWnd* m_pAlternateWndInit;      // special case commdlg hooking
        DWORD m_dwPropStyle;
        DWORD m_dwPropExStyle;
        HWND m_hWndInit;
        BOOL m_bDlgCreate;
        HHOOK m_hHookOldCbtFilter;
        HHOOK m_hHookOldMsgFilter;

        // other CWnd modal data
        MSG m_lastSentMsg;              // see CWnd::WindowProc
        HWND m_hTrackingWindow;         // see CWnd::TrackPopupMenu
        HMENU m_hTrackingMenu;
        TCHAR m_szTempClassName[96];    // see AfxRegisterWndClass
        HWND m_hLockoutNotifyWindow;    // see CWnd::OnCommand
        BOOL m_bInMsgFilter;

        // other framework modal data
        CView* m_pRoutingView;          // see CCmdTarget::GetRoutingView
        CFrameWnd* m_pRoutingFrame;     // see CCmdTarget::GetRoutingFrame

        // MFC/DB thread-local data
        BOOL m_bWaitForDataSource;

        // common controls thread state
        CToolTipCtrl* m_pToolTip;
        CWnd* m_pLastHit;       // last window to own tooltip
        int m_nLastHit;         // last hittest code
        TOOLINFO m_lastInfo;    // last TOOLINFO structure
        int m_nLastStatus;      // last flyby status message
        CControlBar* m_pLastStatus; // last flyby status control bar

        // OLE control thread-local data
        CWnd* m_pWndPark;       // "parking space" window
        long m_nCtrlRef;        // reference count on parking window
        BOOL m_bNeedTerm;       // TRUE if OleUninitialize needs to be called
};

EXTERN_THREAD_LOCAL(_AFX_THREAD_STATE, _afxThreadState)

_AFX_THREAD_STATE* AFXAPI AfxGetThreadState();


- 애플리케이션 리소스 Localization하기
  CWinApp상속받은 나의 App 클래스의 맴버로
HINSTANCE m_hInstDLL;
를 추가한 후
  OnInitInstance() 첫줄에

   if ((m_hInstDLL = ::LoadLibrary("mylocal.dll")) < HINSTANCE_ERROR)
   {
      return FALSE; // failed to load the localized resources
   }
   else
   {
      AfxSetResourceHandle(m_hInstDLL); // get resources from the DLL
   }

하고 OnExitIntance() 부에

   int CMyApp::ExitInstance()
   {
      FreeLibrary(m_hInstDLL);
      return CWinApp::ExitInstance();
   }
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST


MySQL 에서 Text file Import 하기.doc
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST


첨부..
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST


Jakarta-Tomcat 설치
JSP 예제 애플리케이션 만들기
JavaBeans 컴포넌트 설치
=========================================================

1. Jakarta-Tomcat 설치
------------------------------
jakarta-tomcat.zip ( 1.3버전)을
아무 디렉토리에 설치를 한다.

그리고 시스템 환경변수에
JAVA_HOME=c:\jdk1.3
이 설정되어 있나 살펴 본다.
없으면 만든다.

톰캣이 설치된 디렉토리\bin\startup.bat를
실행하면 톰캣이 실행된다.

http://localhost:8080
을 해보면 톰캣이 나오고 example을 실행해서
되면 성공이다.

2. JSP 예제 애플리케이션 만들기
---------------------------------
study라는 애플리케이션을 만들어보겠다.

%TOMCAT_HOME%은 자카르타 톰캣이 깔린 디렉토리로
기본적으로 %TOMCAT_HOME% = c:\jakarta-tomcat에 깔면 된다.

%TOMCAT_HOME%\conf\server.xml 파일 안에서 다음과 같이
study 에 대한 애플리케이션을 추가한다.

        <!-- example - how to override AutoSetup actions -->
        <Context path="/study" docBase="webapps/study" debug="0" reloadable="true" >
        </Context>

그리고 다음과 같이 디렉토리를 설정한다.
%TOMCAT_HOME%
    |    
    +webapp
        +
        |
        +- study
             +- TestBean
             |     +- index.html      디폴트 index html
             |     +- TestBean.jsp    테스트용 JSP파일
             +- META-INF
             |     +- MANIFEST.MF
             +- WEB-INF
                   +-web.xml
                   +-classes
                        +- TestPack   테스트용 클래스 패키지
                             +- TestBean.class 테스트용 클래스

쉽게 설정하기 위해서는 아래 있는 두 디렉토리를

META-INF
     +- MANIFEST.MF
WEB-INF
     +- web.xml
     +- classes ( classes 디렉토리 속은 아무것도 없게 지우고 )

%TOMCAT_HOME%\webapp\examples 에 있는 아래것을 복사해서
%TOMCAT_HOME%\webapp\study에 놓으면 된다.


3.JavaBeans 컴포넌트 설치
------------------------------------------

이제 Visual J++이나 다른걸로 다음을 코드한후 컴파일 한다.

..........................TestBean.java.....................
package TestPack;

public class TestBean
{
        private int nData;
        
        public TestBean()
        {
                this(0);
        }
        
        public TestBean( int nData )
        {
                this.nData = nData;
        }
        
        public int getData()
        {
                return nData;
        }
        
        public void setData( int nData )
        {
                this.nData = nData;
        }
}
............................................................
Visual J++ 컴파일 하기 또는
javac TestBean.java 로 TestBean.class를 얻어낸후
이 클래스 파일을
%TOMCAT_HOME%\webapp\study\WEB-INF\classes\TestPack\TestBean.class
로 복사한다.

다음은 index.html파일의 내용이다.
<html><body>Hello World</body></html>

그런후
http://localhost:8080/study/TestBean
을 실행해보면 Hello World가 찍힐 것이다.
( 이것은 study애플리케이션안의 TestBean이라는 디렉토리가 톰캣에
의해서 잘 돌아가고 있다는 것을 확인하는 것이다. )


다음은 TestBean.jsp 파일의 내용이다.
.........................TestBean.jsp.........................
<%@ page import = "TestPack.TestBean" %>

<jsp:useBean id="test" class="TestPack.TestBean" scope="page"/>
<jsp:setProperty name="test" property="*"/>

<html>
<body>
        TestBean Data is <%= test.getData() %>.
</body>
</html>
..............................................................

그런후
http://localhost:8080/study/TestBean/TestBean.jsp?data=5
를 해보면 아래와 같이 찍히게 된다.
TestBean Data is 5.

이렇게 된다면 모든것이 성공적으로 이뤄 진것이다.

그렇지만 다음과 같이 url을 넣게 되면 컴파일 에러가 난다.
http://localhost:8080/study/TestBean/TestBean.jsp?Data=5
Data 앞에 대문자는 안된다.
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST


오후 2:38 2000-05-03
조경민
ATL COM to WWW
===================================================================

먼저 ATL 프로젝트로 프로그램을 만든다.

( 이때 Support MTS를 하면 MTS 함수를 불러다 쓸수 있다.
물론 서포트를 안해도 MTS에 올릴 수 있다. )

MTS에 등록하기
먼저 시작 -> 관리도구 -> 서비스 구성요소 를 들어간다.

그리고 콘솔루트/구성요소서비스/컴퓨터/내컴퓨터/COM+응용프로그램
을 들어가서 오른클릭 새로 만들기를 해서 빈 문서를 만든다.
이름 : NSTChart
그리고 NSTChart에서 오른클릭후 구성요소에서
새 구성요소를 설치합니다.
를 선택해서 내가 만든 com dll을 등록한다.
( 이때 com dll이 c:\project\testcom\release\testcom.dll 처럼
꼭 system32 디렉토리에 없어도 된다.  )
*만일 MTS에 안올리고 인터넷 서비스에서 관리하면 꼭 system32에
놔야만 하고 만일 새롭게 갱신된 dll로 엎으려고 하면 캐슁되어서
서비스를 내려야 한다. 서비스 잘 안내려가서 부팅을 계속 해야 한다*

그리고 regsvr32 c:\project\testcom\release\testcom.dll 를 해서
등록을 하면 된다.

사용은 아래 처름 ASP로 하면 되고.
만일 새롭게 갱신된 testcom.dll을 다시 갱신하고 싶으면
컴파일 하기 전에 ( 컴파일후 바로 파일 바뀌기가 되므로 )
구성요소 서비스에서 NSTChart에서 오른클릭후 시스템 종료를 하면
모듈이 내려가서 갱신 할 수도 있고 편리하다.
  



다음은 ASP 페이지다.

<HTML>

<BODY>
Chart ATL Component Test Site
<HR>
<%

    Response.Write "==================================="

    Dim obj
    Dim obj2  
    Dim sOutURL    
    
    On Error Resume Next
    
    Set obj = Server.CreateObject("NSTChart.ChartGen")

    
    If Err.Number <> 0 Then
    
        Response.Write "에러 발생"+Err.Description
    
    End If

    obj.m_nWidth = 100
    obj.m_nHeight = 200
    obj.fnDrawChart "NSTPieChart", 0, 0, 100, 200

    sOutURL = obj.fnGenerateImage()
    Response.Write "생성 성공"+ cstr(obj.m_nHeight)
    Response.Write "<IMG SRC="+sOutURL+">"
    

    Set obj = Nothing
    
%>

<HR>
</BODY>
</HTML>
YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST


티스토리 툴바