* If there is any build error and it cannot be solved, you should clean folder and retry all process as you planned. 


[OpenSSL 1.1.0e] - succeeded


1. Download OpenSSL latest version (1.1.0e)

2. Install Perl program ( ActiveState Perl recommended )

3. Install NASM (32bit or 64bit) http://www.nasm.us

4. Set path NASM

5. See INSTALL on OpenSSL folder

5.1 Perl Configure ( VC-WIN32 or VC-WIN64A or debug-VC-WIN32 debug-VC-WIN64A ) --prefix=C:\Build-OpenSSL-VC-32-dbg

5.1.1 for avoid license issue "perl Configure debug-VC-WIN32 no-idea no-md2 no-mdc2 no-rc5 no-rc4 enable-capieng no-shared --debug --prefix=c:\Build-OpenSSL-VC-32-dbg"

5.2 nmake

5.3 nmake test

5.4 nmake install <- Need administration privileges. 


[OpenSSL 1.0.2k] - Failed

1. Download OpenSSL latest version (1.0.2k)

2. Install Perl program ( ActiveState Perl recommended )

3. Install NASM (32bit or 64bit) http://www.nasm.us

4. Set path NASM

5. See INSTALL on OpenSSL folder

5.1 Perl Configure ( VC-WIN32 or VC-WIN64A or debug-VC-WIN32 debug-VC-WIN64A ) --prefix=C:\Build-OpenSSL-VC-32-dbg

5.1.1 if there a asm error on nmake, add "no-asm" option. It would be working but it may affect performance lower. 

5.1.2 for avoid license issue "perl Configure debug-VC-WIN32 no-idea no-md2 no-mdc2 no-rc5 no-rc4 enable-capieng no-shared --debug --prefix=c:\Build-OpenSSL-VC-32-dbg"

5.2 ms\do_win64a.bat

5.2.1 For 32bit with nasm, ms\do_nasm.bat

5.2.2 For 32bit with ms, perl Confire no-asm -> ms\do_ms.bat 

5.3 nmake -f ms\nt.mak

5.3.1 if you need to build with DLL, use "nmake -f ms\ntdll.mak"


5.4 See output folder

5.4.1 lib : out32

5.4.2 bin : out32

5.4.3 include : inc32

5.4.4 if the configuration is debug, see out32.dbg


5.5 If you want to test built modules, please do "nmake -f ms\nt.mak test"

5.5.1 it might be failed due to "no-idea". Please ignore. 


5.6 Install modules to --prefix using by "nmake -f ms\nt.mak install"


[Curl]

Place all libs to ../deps/lib (openssl should be named libeay32.lib, ssleay32.lib)

Place all includes to ../deps/include


Set RTLIBCFG=static

!IF "$(RTLIBCFG)"=="static"
RTLIB = /MT
RTLIB_DEBUG = /MTd
!ELSE
RTLIB = /MD
RTLIB_DEBUG = /MDd

!ENDIF 


1. command line build on "~\winbuild\" - Should select command line for machine type

nmake /f Makefile.vc mode=static VC=9 WITH_SSL=static ENABLE_IDN=no DEBUG=no GEN_PDB=yes MACHINE=x64 

1.1 After build without error, please see "~\builds\???"


2. Open "/projects/Windows/VC version/curl-all.sln" by VC++

2.1 Add essential library

2.2 Add CURL_STATICLIB on preprocessor if curl library is static built

2.3 Select build option. 

// 폴더가 존재하는지 체크한다. 없으면 반복적으로 만든다.

p = _tcsrchr(szLogFile, _T('\\'));

*p = _T('\0'); // 임시로 폴더까지만 제한한다.

if ( _access(szLogFile, 00) != 0 )

{

*p = _T('\\'); // 없앴던것을 다시 복구.


// 폴더를 만드는작업을 수행한다.

TCHAR *p1 = _tcschr(szLogFile, _T('\\')); // c:\ 찾음

TCHAR *p2 = _tcschr(p1+1, _T('\\')); // c:\aaa\ 찾음

while(p2 != NULL) 

{

if ( p2 == NULL )

break;


*p2 = _T('\0'); // 임시로 \ 없애고

if ( _access(szLogFile, 00) != 0 ) // 폴더가 있는지 체크한뒤에

{

if ( 0 == CreateDirectory(szLogFile, NULL) ) // 폴더가 없으면 폴더를 만들고

break; // something wrong

}

*p2 = _T('\\'); // 임시로 없앤 \ 를 복구

p1 = p2;

p2 = _tcschr(p1+1, _T('\\'));

}

}

else

*p = _T('\\'); // 없앴던것을 다시 복구



#include <json/writer.h>
#include <json/json.h>

#ifdef _DEBUG
#pragma comment(lib, "json_vc71_libmtd.lib")
#else
#pragma comment(lib, "json_vc71_libmt.lib")
#endif

void pjson(Json::Value& v)
{
    if ( true == v.isArray() )
    {
        for (int i=0, max=v.size(); i<max; i++)
        {
            if ( true == v[i].isObject() )
                pjson(v[i]);
            else
                printf(":%s \n", v[i].asString().c_str());
        }
        return;
    }
    else if ( true == v.isObject() )
    {
        Json::Value::Members members(v.getMemberNames());

        for (Json::Value::Members::iterator it = members.begin(); it != members.end(); ++it)
        {
            printf("%s:", (*it).c_str());
            pjson(v[(*it).c_str()]);
        }
    }
    else
    {
        printf(":%s \n", v.asString().c_str());
        return;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    int nRet = 0;
    char* buffer = 0;
    long size = 0;
    FILE *file = fopen("setting.json", "rb");

    if ( file == 0 )
    {
        printf("Cannot open setting.json file \n");
        return 0;
    }

    fseek(file, 0, SEEK_END);
    size=ftell(file);

    buffer = (char*)malloc(sizeof(char)*size+1);
    rewind( file );
    if ( fread(buffer, sizeof(char), size, file) != size )
    {
        printf("Failed to read json file \n");
    }
    fclose (file);

    Json::Reader reader;
    Json::Value root;
    bool ok = reader.parse(buffer, root);

    free(buffer);

    if ( reader.getFormattedErrorMessages().size() != 0 )
    {
        printf("Read json error : %s \n", reader.getFormattedErrorMessages().c_str());
    }

    if ( reader.getStructuredErrors().size() != 0 )
    {
        for ( int i=0; i<reader.getStructuredErrors().size(); i++)
            printf("Read json error : %s \n", reader.getStructuredErrors()[i].message.c_str());
    }

    unsigned short ui = root["address"]["aaa"].asUInt();
    pjson(root);

    return 0;


1. curl 사이트에서 최신 라이브러리 다운로드. http://curl.haxx.se/


2. 적당한 위치에 압축 해제.


3. {압축해제위치}\projects\Windows\{빌드할 VS 버전 선택}\curl.sln 열기


4. 빌드할 라이브러리 선택해서 빌드. (Multi-threaded 옵션은 직접 수정해야함)


5. {압축해제위치}\build\Win32\{빌드한 VS 버전}\LIB [Debug/Release]\libcurl.lib 을 프로젝트로 복사


6. {압축해제위치}\include 를 프로젝트로 복사


7. 프로젝트 설정에서 include 와 lib 설정


8. 만일 라이브러리 타입이 정적라이브러인 경우 Project Property -> C/C++ -> Preprocessor 에 "CURL_STATICLIB"를 반드시 추가. 하지 않으면 링크 에러남
   error LNK2001: unresolved external symbol __imp__curl_easy_init <- 요런에러


9. ldap 관련 에러가 나면 "#pragma comment(lib, "wldap32.lib")" 라이브러리 링크해야함.

1>libcurld.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_unbind_s referenced in function _Curl_ldap
1>libcurld.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_msgfree referenced in function _Curl_ldap
1>libcurld.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ber_free referenced in function _Curl_ldap
1>libcurld.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_memfree referenced in function _Curl_ldap
1>libcurld.lib(ldap.obj) : error LNK2019: unresolved external symbol __imp__ldap_value_free_len referenced in function _Curl_ldap 


10. 여기까지 하니까 빌드 및 동작함.


    CURL *curl;
    CURLcode res;
    char buffer[10];
    curl = curl_easy_init();

    if ( 0 != curl )
    {
        curl_easy_setopt(curl, CURLOPT_URL, "http://www.naver.com");
        res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
        if ( res == 0) {
        }
        else {
        }
    } 




#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>
#include <atlcomcli.h>

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

class EventSink : public IWbemObjectSink
{
    LONG m_lRef;
    bool bDone;

public:
    EventSink() { m_lRef = 0; }
   ~EventSink() { bDone = true; }

    virtual ULONG STDMETHODCALLTYPE AddRef()
	{
		return InterlockedIncrement(&m_lRef);
	}
    virtual ULONG STDMETHODCALLTYPE Release()
	{
		LONG lRef = InterlockedDecrement(&m_lRef);
		if(lRef == 0)
			delete this;
		return lRef;
	}
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv)
	{
		if (riid == IID_IUnknown || riid == IID_IWbemObjectSink)
		{
			*ppv = (IWbemObjectSink *) this;
			AddRef();
			return WBEM_S_NO_ERROR;
		}
		else return E_NOINTERFACE;
	}

    virtual HRESULT STDMETHODCALLTYPE Indicate( 
            LONG lObjectCount,
            IWbemClassObject __RPC_FAR *__RPC_FAR *apObjArray
            )
	{
		HRESULT hres = S_OK;

		for (int i = 0; i < lObjectCount; i++)
		{
			IWbemClassObject *pObj = apObjArray[i];
			_variant_t vtProp;
			// Get the value of the Name property
			// ->GetNames();
			//HRESULT hr = pObj->Get(L"Name", 0, &vtProp, 0, 0);
			HRESULT hr = pObj->Get(_bstr_t(L"TargetInstance"), 0, &vtProp, 0, 0);
			if (!FAILED(hr))
			{
				IUnknown* str = vtProp;
				hr = str->QueryInterface( IID_IWbemClassObject, reinterpret_cast< void** >( &apObjArray[i] ) );
				if ( SUCCEEDED( hr ) )
				{
					_variant_t cn;
					hr = apObjArray[i]->Get( L"Name", 0, &cn, NULL, NULL );
					if ( SUCCEEDED( hr ) )
					{
						if ((cn.vt==VT_NULL) || (cn.vt==VT_EMPTY))
							std::wcout << "Name : " << ((cn.vt==VT_NULL) ? "NULL" : "EMPTY") << endl;
						else
							std::wcout << "Name : " << cn.bstrVal << endl;
					}
					VariantClear(&cn);

					hr = apObjArray[i]->Get( L"ProcessId", 0, &cn, NULL, NULL );
					if ( SUCCEEDED( hr ) )
					{
						if ((cn.vt==VT_NULL) || (cn.vt==VT_EMPTY))
							std::wcout << "PID : " << ((cn.vt==VT_NULL) ? "NULL" : "EMPTY") << endl;
						else if ( cn.vt == VT_I4 )
							std::wcout << "PID : " << cn.intVal << endl;
						else
							std::wcout << "PID : " << cn.bstrVal << endl;
					}
					VariantClear(&cn);
				}
			}
			VariantClear(&vtProp);
		}

		return WBEM_S_NO_ERROR;
	}
        
    virtual HRESULT STDMETHODCALLTYPE SetStatus( 
            /* [in] */ LONG lFlags,
            /* [in] */ HRESULT hResult,
            /* [in] */ BSTR strParam,
            /* [in] */ IWbemClassObject __RPC_FAR *pObjParam
            )
	{
		if(lFlags == WBEM_STATUS_COMPLETE)
		{
			printf("Call complete. hResult = 0x%X\n", hResult);
		}
		else if(lFlags == WBEM_STATUS_PROGRESS)
		{
			printf("Call in progress.\n");
		}

		return WBEM_S_NO_ERROR;
	}
};

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hres;

    // Step 1: --------------------------------------------------
    // Initialize COM. ------------------------------------------

    hres =  CoInitializeEx(0, COINIT_MULTITHREADED); 
    if (FAILED(hres))
    {
        cout << "Failed to initialize COM library. Error code = 0x" 
             << hex << hres << endl;
        return 1;                  // Program has failed.
    }

    // Step 2: --------------------------------------------------
    // Set general COM security levels --------------------------

    hres =  CoInitializeSecurity(
        NULL, 
        -1,                          // COM negotiates service
        NULL,                        // Authentication services
        NULL,                        // Reserved
        RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication 
        RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation  
        NULL,                        // Authentication info
        EOAC_NONE,                   // Additional capabilities 
        NULL                         // Reserved
        );

                      
    if (FAILED(hres))
    {
        cout << "Failed to initialize security. Error code = 0x" 
             << hex << hres << endl;
        CoUninitialize();
        return 1;                      // Program has failed.
    }
    
    // Step 3: ---------------------------------------------------
    // Obtain the initial locator to WMI -------------------------

    IWbemLocator *pLoc = NULL;

    hres = CoCreateInstance(
        CLSID_WbemLocator,             
        0, 
        CLSCTX_INPROC_SERVER, 
        IID_IWbemLocator, (LPVOID *) &pLoc);
 
    if (FAILED(hres))
    {
        cout << "Failed to create IWbemLocator object. "
             << "Err code = 0x"
             << hex << hres << endl;
        CoUninitialize();
        return 1;                 // Program has failed.
    }

    // Step 4: ---------------------------------------------------
    // Connect to WMI through the IWbemLocator::ConnectServer method

    IWbemServices *pSvc = NULL;
 
    // Connect to the local root\cimv2 namespace
    // and obtain pointer pSvc to make IWbemServices calls.
    hres = pLoc->ConnectServer(
        _bstr_t(L"ROOT\\CIMV2"), 
        NULL,
        NULL, 
        0, 
        NULL, 
        0, 
        0, 
        &pSvc
    );
     
    if (FAILED(hres))
    {
        cout << "Could not connect. Error code = 0x" 
             << hex << hres << endl;
        pLoc->Release();     
        CoUninitialize();
        return 1;                // Program has failed.
    }

    cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl;


    // Step 5: --------------------------------------------------
    // Set security levels on the proxy -------------------------

    hres = CoSetProxyBlanket(
        pSvc,                        // Indicates the proxy to set
        RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx 
        RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx 
        NULL,                        // Server principal name 
        RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx 
        RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
        NULL,                        // client identity
        EOAC_NONE                    // proxy capabilities 
    );

    if (FAILED(hres))
    {
        cout << "Could not set proxy blanket. Error code = 0x" 
             << hex << hres << endl;
        pSvc->Release();
        pLoc->Release();     
        CoUninitialize();
        return 1;               // Program has failed.
    }

    // Step 6: -------------------------------------------------
    // Receive event notifications -----------------------------

    // Use an unsecured apartment for security
    IUnsecuredApartment* pUnsecApp = NULL;

    hres = CoCreateInstance(CLSID_UnsecuredApartment, NULL, 
        CLSCTX_LOCAL_SERVER, IID_IUnsecuredApartment, 
        (void**)&pUnsecApp);
 
    EventSink* pSink = new EventSink;
    pSink->AddRef();

    IUnknown* pStubUnk = NULL; 
    pUnsecApp->CreateObjectStub(pSink, &pStubUnk);

    IWbemObjectSink* pStubSink = NULL;
    pStubUnk->QueryInterface(IID_IWbemObjectSink,
        (void **) &pStubSink);

    // The ExecNotificationQueryAsync method will call
    // The EventQuery::Indicate method when an event occurs
    hres = pSvc->ExecNotificationQueryAsync(
        _bstr_t("WQL"), 
        _bstr_t("SELECT * " 
            "FROM __InstanceCreationEvent WITHIN 1 "
            "WHERE TargetInstance ISA 'Win32_Process'"), 
        WBEM_FLAG_SEND_STATUS, 
        NULL, 
        pStubSink);

    // Check for errors.
    if (FAILED(hres))
    {
        printf("ExecNotificationQueryAsync failed "
            "with = 0x%X\n", hres);
        pSvc->Release();
        pLoc->Release();
        pUnsecApp->Release();
        pStubUnk->Release();
        pSink->Release();
        pStubSink->Release();
        CoUninitialize();    
        return 1;
    }

    // Wait for the event
    Sleep(20000);
         
    hres = pSvc->CancelAsyncCall(pStubSink);

    // Cleanup
    // ========

    pSvc->Release();
    pLoc->Release();
    pUnsecApp->Release();
    pStubUnk->Release();
    pSink->Release();
    pStubSink->Release();
    CoUninitialize();

    return 0;   // Program successfully completed.
}

template <typename T>
    makecmd & operator<<(T const & datum)
    {
      append( boost::lexical_cast<std::string>(datum) );
      return *this;
    }


http://www.codeproject.com/Articles/518159/10-Even-More-Visual-Studio-Debugging-Tips-for-Nati



MS 의 소스를 살펴보다가 평소에 알지 못하던 코드가 궁금하여(영어를 이해못했나?) 테스트 해봤습니다. 

    iResult = getaddrinfo(SeverAddress.c_str(), ServerPort, &hints, &result);

    if ( iResult != 0 ) 

{

        printf("getaddrinfo failed with error: %d\n", iResult);

        WSACleanup();

        return 1;

    }

// Attempt to connect to an address until one succeeds

for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) 

{

// Create a SOCKET for connecting to server

        socket_ = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);

        if (socket_ == INVALID_SOCKET)

{

m_dwLastError = WSAGetLastError();

WSACleanup();

return false;

}


// Connect to server.

iResult = ::connect(socket_, ptr->ai_addr, (int)ptr->ai_addrlen);

if (iResult == SOCKET_ERROR) 

{

closesocket(socket_);

socket_ = INVALID_SOCKET;

continue;

}

break;

} 


getaddrinfo 는 어떤식으로 동작할까나? MSDN 의 소스를 실행해보니

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

Calling getaddrinfo with following parameters: 

       nodename = www.google.com

        servname (or port) = HTTP


getaddrinfo returned success

getaddrinfo response 1

        Flags: 0x0

        Family: AF_INET (IPv4)

        IPv4 address 74.125.224.114

        Socket type: SOCK_STREAM (stream)

        Protocol: IPPROTO_TCP (TCP)

        Length of this sockaddr: 16

        Canonical name: (null)

getaddrinfo response 2

        Flags: 0x0

        Family: AF_INET (IPv4)

        IPv4 address 74.125.224.113

        Socket type: SOCK_STREAM (stream)

        Protocol: IPPROTO_TCP (TCP)

        Length of this sockaddr: 16

        Canonical name: (null)

getaddrinfo response 3

        Flags: 0x0

        Family: AF_INET (IPv4)

        IPv4 address 74.125.224.116

        Socket type: SOCK_STREAM (stream)

        Protocol: IPPROTO_TCP (TCP)

        Length of this sockaddr: 16

        Canonical name: (null)

getaddrinfo response 4

        Flags: 0x0

        Family: AF_INET (IPv4)

        IPv4 address 74.125.224.115

        Socket type: SOCK_STREAM (stream)

        Protocol: IPPROTO_TCP (TCP)

        Length of this sockaddr: 16

        Canonical name: (null)

getaddrinfo response 5

        Flags: 0x0

        Family: AF_INET (IPv4)

        IPv4 address 74.125.224.112

        Socket type: SOCK_STREAM (stream)

        Protocol: IPPROTO_TCP (TCP)

        Length of this sockaddr: 16

        Canonical name: (null)

Press any key to continue . . .


www.google.com 도메인이 지칭하는 아이피 주소들 중에 하나씩 접속을 시도해보기 위한 코드였습니다. 

참고로 www.google.com 의 domain 정보는

C:\>nslookup www.google.com
Server:  hq-dc01-b.nexon.net

Address:  10.1.100.10


Non-authoritative answer:

Name:    www.google.com

Addresses:  2607:f8b0:4005:802::1011

          74.125.224.113

          74.125.224.116

          74.125.224.115

          74.125.224.112

          74.125.224.114



#include <iostream>
#include <sstream>
int main()
{
    std::cout << "The number 42 in octal:   " << std::oct << 42 << '\n'
              << "The number 42 in decimal: " << std::dec << 42 << '\n'
              << "The number 42 in hex:     " << std::hex << 42 << '\n';
    int n;
    std::istringstream("2A") >> std::hex >> n;
    std::cout << std::dec << "Parsing \"2A\" as hex gives " << n << '\n';
}


wchar_t host[128];

memset(host,0,sizeof(host));

unsigned long dwHostLen = _countof(host);


struct sockaddr_storage addr;

int addr_len = sizeof(addr);


//if(getsockname(s, (struct sockaddr*)&addr, &addr_len) == SOCKET_ERROR) // 내 아이피/포트

if(getpeername(s, (struct sockaddr*)&addr, &addr_len) == SOCKET_ERROR) // 상대방 아이피/포트

{

MyTraceW(L"getsockname error %ld", WSAGetLastError());

}

else

{

WSAAddressToStringW((LPSOCKADDR)&addr, addr_len, NULL, host, &dwHostLen);

MyTraceW(L"send called s[%d] %X %ld bytes. to %s", (unsigned int)s, buf, len, host);

}


어떨때 쓰냐면 send 훅 한담에 소캣별로 패킷을 구분하고 싶을때

[Type1]

typedef void (CClientSocket::*TSendPacket)(const CprintPacket&);

TSendPacket lpSendPacket = (TSendPacket)&CClientSocket::SendPacket;

PDWORD pSendPacket = (PDWORD)&lpSendPacket;

pSendPacket = (PDWORD)*pSendPacket;


[Type2]

template <class T1, class T2>

T1 union_cast(T2 v)

{

  static_assert(sizeof(T1) >= sizeof(T2), "Bad union_cast!");

  union UT {T1 t1; T2 t2;} u;

  u.t2 = v;

  return u.t1;

}

class MyClass

{

public:

  void foo(int);

};

 

auto p = union_cast<void *>(&MyClass::foo);


[Type3]

void *DisMember(size_t size, ...)

{

    if (size != sizeof(void *)) return NULL;

    va_list args;

    va_start(args, size);

    void *res = va_arg(args, void *);

    va_end(args);

    return res;

}

 

// snip

void Base::MyMethod() { /* ... */ }

// snip

 

void *anything = DisMember(sizeof(void (Base::*)()), &Base::MyMethod);




[Type4]

LPVOID pVoid;

       __asm

       {

              push eax

              mov eax, CPatternSearch::BytePatternSearch

              mov pVoid, eax

              pop eax

       }


HANDLE hConsoleW, hConsoleR;

enum Color { black = 0, blue, green, cyan, red, purple, yellow, grey, dgrey, lblue, lgreen, lcyan, lred, lpurple, lyellow, white};


AllocConsole();

SetConsoleTitle(_T("Some title"));

hConsoleW = GetStdHandle(STD_OUTPUT_HANDLE);

hConsoleR = GetStdHandle(STD_INPUT_HANDLE);


void ConsoleOutput(TCHAR* lpszMessage, Color color = white){

int len = wcslen(lpszMessage);

DWORD numOfCharWritten;

SetConsoleTextAttribute(hConsoleW, color);

WriteConsole(hConsoleW, lpszMessage, len, &numOfCharWritten, NULL);

}



  1. 프로그램의 초기화시에 DragAcceptFiles(hWnd, TRUE) 함수를 호출한다. 첫 번째 인자인 hWnd는 드롭의 타겟이 되는 윈도우의 핸들이다.
    혹은 다이얼로그(윈도우)의 리소스에서 Accept Files 을 true로 둔다.
  2. 탐색기로부터 파일이 드롭되는 순간에 WM_DROPFILES 메시지가 날라온다. 이를 처리한다.
    혹은 해당 다이얼로드(윈도우)의 클래스에서 WM_DROPFILES 를 재정의한다. 

    	case WM_DROPFILES :
    	{
    		POINT pt;
    		// 어느 위치에 드롭되었는지 그 항목을 알아낸다.
    		if (DragQueryPoint((HDROP)wParam, &pt)) 
    		{
    			UINT i = 0;
    			// 모두 몇 개의 파일이 드롭되었는지 알아낸다.
    			// 만일 폴더가 드롭되었다면 폴더의 이름만 넘어온다.
    			UINT uCount = DragQueryFile((HDROP)wParam, 0xFFFFFFFF, NULL ,0);
    
    			for(i = 0;i < uCount;i++)
    			{
    				// 드롭된 파일의 이름을 알아온다.
    				DragQueryFile((HDROP)wParam, i, buffer ,255);
    				// 드롭된 파일 이름을 출력해본다.
    				MessageBox(hWnd, buffer, "File Name", MB_OK);
    			}
    		}
    		// drag and drop 작업을 끝낸다.
    		DragFinish((HDROP)wParam);
    		break;
    	}
    
  3. Drag&drop을 더 사용할 필요가 없어지면 DragAcceptFiles를 호출한다.
    	DragAcceptFiles(hWnd, FALSE);




하나의 솔루션에 여러개의 프로젝트로 작업을 할 때가 있습니다. 

독입된 프로젝트로 동작한다면 디버깅에 문제는 없지만 만일 서버-클라이언트 구조라면 어떻게 해야할까요? 


VC 를 서버용, 클라용 두개를 띄워서 작업한다?

그렇게만 되면 오히려 더 깔끔할것 같긴합니다. (이건 혹시나 방법이 있을지도 모르겠네요)

PC를 2대로 테스트한다? 이건 더 좋은 방법이겠죠. (소스 통합은 형상관리툴로 하고..)


하지만 그런 상황이 안된다면 아래와 같은 방법으로 하나의 VS로 모두 디버깅을 할 수 있습니다. 


솔루션 -> 속성 -> 시작 프로젝트 -> Multiple startup project 선택



동시에 실행시킬 프로젝트에 대해서 Action 의 값을 Start 로 변경시켜줍니다.


만일 프로젝트의 실행 순서가 있다면 오른쪽 화살표로 순서를 변경시켜줍니다. 


저같은 경우 서버를 먼저 실행시키고, 클라이언트를 나중에 실행시키도록 설정하였습니다. 


그럼 이만~!






* 삭제된 헤더파일이 프로젝트에 포함되어 있는 경우

* Debug 폴더를 날린다.

* Clean 을 한다..


등등 어떤짓을 하면 해결된다는데 난 안되는군.


일단 리서치중.

약 한시간 가량 빡치게 만든에러..


MS 는 CreateProcess 의 버그를 고칠 생각이 없는가? 구조적으로 힘든가? 아무튼 이런 빡치는 상황이 발생하면 아래와 같이 수정하세요.


먼저 CreateProcess 를 사용하는 수천만명이 봤을 간단한 예제를 봅시다. 

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


빨간 부분을 수정했습니다. 


#include <windows.h> #include <stdio.h> #include <tchar.h> void _tmain( int argc, TCHAR *argv[] ) { STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); if( argc != 2 ) { printf("Usage: %s [cmdline]\n", argv[0]); return; } // Start the child process. if( !CreateProcess( NULL, // No module name (use command line) _T("notepad.exe"), // Command line NULL, // Process handle not inheritable NULL, // Thread handle not inheritable FALSE, // Set handle inheritance to FALSE 0, // No creation flags NULL, // Use parent's environment block NULL, // Use parent's starting directory &si, // Pointer to STARTUPINFO structure &pi ) // Pointer to PROCESS_INFORMATION structure ) { printf( "CreateProcess failed (%d).\n", GetLastError() ); return; } // Wait until child process exits. WaitForSingleObject( pi.hProcess, INFINITE ); // Close process and thread handles. CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); }


이렇게 한 뒤에 unicode 로 빌드하면 빡치는 상황이 발생합니다. 바로 Access violation error!!!!!!!!!!!!


그 원인은 unicode 버전의 createprocess 는 lpCommandLine 파라미터를 변경시키기 때문에 const 메모리 주소를 전달하면 안됩니다. 

아래 msdn 에 설명되어 있습니다. 


lpCommandLine [in, out, optional]

The command line to be executed. The maximum length of this string is 32,768 characters, including the Unicode terminating null character. If lpApplicationName is NULL, the module name portion of lpCommandLine is limited to MAX_PATH characters.

The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.

The lpCommandLine parameter can be NULL. In that case, the function uses the string pointed to bylpApplicationName as the command line.

If both lpApplicationName and lpCommandLine are non-NULL, the null-terminated string pointed to bylpApplicationName specifies the module to execute, and the null-terminated string pointed to by lpCommandLinespecifies the command line. The new process can use GetCommandLine to retrieve the entire command line. Console processes written in C can use the argc and argv arguments to parse the command line. Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line.

int get_computer_name(BYTE *computer_name, DWORD *computer_name_lg)

{

   HKEY hKey;

   if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,

               "SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ComputerName",

               0, KEY_QUERY_VALUE, &hKey ) != ERROR_SUCCESS)

      return FALSE;

   if (RegQueryValueEx(hKey, "ComputerName", NULL, NULL,

                       (LPBYTE) computer_name,

                       (LPDWORD) computer_name_lg) != ERROR_SUCCESS) {

      RegCloseKey(hKey);

      return FALSE;

   }

   RegCloseKey(hKey);

   return TRUE;

}


테스트는 안해봄. 

UINT64 get_cpuid(void)

{

   DWORD dwStandard = 0; 

   DWORD dwFeature = 0; 

     

   _asm { 

        mov eax, 1 

        cpuid 

        mov dwStandard, eax 

        mov dwFeature, edx 

    }

    return( ((UINT64)(dwFeature) << 32) | ((UINT64)(dwStandard)));

}


int _tmain(int argc, _TCHAR* argv[])

{

UINT64 cpuid;

cpuid = get_cpuid();

printf("CPU ID : %.I64X\n", cpuid);

return 0;

}


이 프로그램이 다른 PC에서도 먹힐까? 테스트를 해봐야 할듯.. 


ps. 같은 브랜드의 PC에서 테스트해본결과 값이 동일하다... 망 글..

간혹다가 이런 에러에 직면할때가 있습니다. 


[원인]

"AppNameDlg.h" 에서 resource.h 에 선언해둔 IDD_#### 을 참조하지 못하여 발생함. 

"AppNameDlg.cpp" 에서는 "AppName.h" 와 "AppNameDlg.h" 를 include 합니다. 

이때 "AppName.h" 내부에서 "resource.h" 를 include 하기 때문에 순서상으로 오류가 발생하지 않아야 합니다. 

하지만 간혹 "AppNameDlg" 클래스를 참조하기 위하여 다른 cpp 파일에서 "AppNameDlg.h" 를 include 를 할때가 있는데 이런 경우 위와 같은 오류가 발생합니다.  


[해결방안]

* 다른 cpp 파일에서 #include "AppNameDlg.h" 위에 #include "AppName.h" 를 추가한다. (전체적인 구조대로 구현)

* "AppNameDlg.h" 에서 #include "resource.h" 를 추가한다. 


끝!


#include<windows.h>
#include<wininet.h>
#include<iostream.h>

void main(int argc, char *argv[])
{
    if (argc != 3)
    {
        cout << "Usage: progress <host> <object>" << endl;
        return;
    }

    HINTERNET hSession = InternetOpen("WinInet Progress Sample",
                                      INTERNET_OPEN_TYPE_PRECONFIG,
                                      NULL,
                                      NULL,
                                      0);
    HINTERNET hConnection = InternetConnect(hSession,
                                            argv[1],  // Server
                                            INTERNET_DEFAULT_HTTP_PORT,
                                            NULL,     // Username
                                            NULL,     // Password
                                            INTERNET_SERVICE_HTTP,
                                            0,        // Synchronous
                                            NULL);    // No Context

    HINTERNET hRequest = HttpOpenRequest(hConnection,
                                         "GET",
                                         argv[2],
                                         NULL,    // Default HTTP Version
                                         NULL,    // No Referer
                                         (const char**)"*/*\0", // Accept
                                                                // anything
                                         0,       // Flags
                                         NULL);   // No Context
    HttpSendRequest(hRequest,
                    NULL,    // No extra headers
                    0,       // Header length
                    NULL,    // No Body
                    0);      // Body length

    DWORD dwContentLen;
    DWORD dwBufLen = sizeof(dwContentLen);
    if (HttpQueryInfo(hRequest,
                      HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
                      (LPVOID)&dwContentLen,
                      &dwBufLen,
                      0))
    {
        // You have a content length so you can calculate percent complete
        char *pData = (char*)GlobalAlloc(GMEM_FIXED, dwContentLen + 1);
        DWORD dwReadSize = dwContentLen / 10;   // We will read 10% of data
                                                // with each read.

        cout << "Download Progress:" << endl;
        cout << "    0----------100%" << endl;
        cout << "     ";
        cout.flush();

        DWORD cReadCount;
        DWORD dwBytesRead;
        char *pCopyPtr = pData;
        for (cReadCount = 0; cReadCount < 10; cReadCount++)
        {
            InternetReadFile(hRequest, pCopyPtr, dwReadSize, &dwBytesRead);
            cout << "*";
            cout.flush();
            pCopyPtr = pCopyPtr + dwBytesRead;
        }
        // extra read to account for integer division round off
        InternetReadFile(hRequest,
                         pCopyPtr,
                         dwContentLen - (pCopyPtr - pData),
                         &dwBytesRead);
        // Null terminate data
        pData[dwContentLen] = 0;

        // Display
        cout << endl << "Download Complete" << endl;
        cout << pData;
    }
    else
    {
        DWORD err = GetLastError();
        // No content length...impossible to calculate % complete
        // Just read until we are done.
        char pData[100];
        DWORD dwBytesRead = 1;
        while (dwBytesRead)
        {
            InternetReadFile(hRequest, pData, 99, &dwBytesRead);
            pData[dwBytesRead] = 0;
            cout << pData;
        }
    }
}

boost 라이브러리를 빌드할때 저처럼 VC 2005 와 VC 2008 을 함께 설치되어 있는 상태에서 boost 를 빌드하면

VC 2008 용으로 라이브러리가 생성됩니다. 

그럼 VC 2005 에서는 라이브러리를 못찾으니 링크 에러가 뜨는데요.

VC 2005 용 라이브러리를 강제로 만들수 있습니다.

1. boost 를 다운받는다. (설치 방법은 버전에 따라 다소 다를 수 있음)
2. boost 압출을 풀어서 적당한 위치에 둔다.
3. 시작 -> 모든프로그램 -> visual studio command~~~ 실행
4. boost 폴더를 이동후 bootstrap.bat 실행
5. b2 실행. (이때 옵션에 따라 라이브러리 버전을 변경할 수 있음)
 b2 toolset=msvc-8.0 엔터
 b2 toolset=msvc-9.0 엔터
 b2 toolset=msvc-10.0 variant=debug,release link=static,shared threading=single,multi address-model=32 runtime-link=static,shared


bjam.exe toolset=msvc-8.0

toolset 옵션을 변경했을 때 아래와 같이 vc80 용 라이브러리와 vc90 용 라이브러리가 함께 위치합니다.

 


VC 으로 개발을 하다보면 종종 겪는 에러 입니다. 

저는 stdafx.cpp 를 프로젝트에서 뺏다가 다시 추가했더니 이런 에러가 뜨더군요. 

2>------ Build started: Project: XXXX, Configuration: Debug Win32 ------
2>Compiling...
2>XXXX.cpp
2>d:\XXXX\XXXX.cpp(4) : error C2859: d:\XXXX\debug\vc80.pdb is not the pdb file that was used when this precompiled header was created, recreate the precompiled header.
2>d:\XXXX\XXXX.cpp(4) : error C2859: d:\XXXX\debug\vc80.idb is not the idb file that was used when this precompiled header was created, recreate the precompiled header.
2>Build log was saved at "file://d:\XXXX\Debug\BuildLog.htm"
2>XXXX - 2 error(s), 0 warning(s) 

인터넷을 살펴보면 일반적으로

프로젝트 세팅 -> C/C++ 컴파일러 -> Precompiled Headers 에서

Create/Use Precompiled Header 항목을  Not Using Precompiled Headers 으로 해결하라고 합니다. 

하지만 이 방법은 큰 프로젝트에서는 빌드시간이 늘어나는 단점이 있습니다. 

위 설명대로 vc80.idb 를 새로 생성하면 됩니다. 

솔루션 탐색기에서 stdafx.cpp 의 Property Page 를 열어서 Precompiled Headers 의 

Create/Use Precompiled Header 항목을  Create Precompiled Header (/Yc) 으로 바꾼뒤에 빌드를 하면 새로 생성됩니다. 

일반적으로 Precompiled Header 옵션으로 프로젝트를 생성하면 다른 cpp 파일은 Use Precompiled Header (/Yu) 인데 stdafx.cpp 만 Create Precompiled Header (/Yc) 임을 알 수 있습니다. 

출처 : 외계달팽 (원작자 허가받음)

소스소스
List 컨트롤을 이용하여 대량의 데이터를 표시하고자 할때 InsertItem() 으로 데이터를 막 집어넣기만 할 것인가?
만일 간단한 문자열 데이터로만 이루어져 있다면 InsertItem() 으로 그냥 집어 넣으면 될 것이다.

만일 cheatengine 을 만든다고 했을때 메모리에서 100만개의 데이터를 찾았다고 가정해보자.
이 100만개의 데이터를 리스트 컨트롤을 이용하여 표시해야하는데 이번에 만든 cheatengine의 메모리 리스트컨트롤은 너무도 화려해서 각종 이미지가 포함되어 있고, 표시되는 데이터도 연관성에 따라서 매우 많은 정보를 보여준다.
그럼 리스트 컨트롤에 100만개의 데이터를 집어넣기 위해서 100만개의 이미지(갯수만큼)와 연관 데이터를 메모리에 올려야할까?
혹 만들어야 할 프로그램이 embeded system 에서 구동해야한다면 어떻게 할 것인가?

이 문제에 대한 해답을 다음링크로부터 얻을 수 있다.
http://www.codeproject.com/KB/list/BMPList.aspx

사실 영어 실력이 딸려서 열심히 설명을 읽었지만 도통 무슨내용인지 알 수가 없다. @.@~~

실제 샘플 프로젝트를 다운 받자. 

저자가 언급한 핵심 함수 부분에 브레이크 포인트를 걸어두고 실행시켜보면

아~~~!!!!!  (띵동띵동)




간단히 요약하자면

일단 100만개의 데이터(일종의 인덱스정보)를 컴팩드한 메모리 구조로 로딩한다. (어떻게 해서든지 알아낸 데이터는 있어야하므로)
 
리스트 컨트롤에 다음과 같이 row 수 만큼만 설정한다.
// This sets the count in the list without adding any items.  This is very fast.
m_cList.SetItemCountEx((int)m_MyDataArray.GetCount(), LVSICF_NOSCROLL|LVSICF_NOINVALIDATEALL);

비록 데이터가 아주 많다고 설정하였지만 실제 ListCtrl 에 추가한것이 아니므로 메모리는 크게 잡아먹지 않는다.

이젠 화면에 표시되는 Item 수 만큼만 그때 그때 표시한다.
void CBMPListDlg::GetDispInfo(NMHDR *pNMHDR, LRESULT *pResult)
{
// 이 함수는 화면에 표시되는 Item 갯수만큼 호출된다.
// row 갯수가 10개 이고 column 이 5개 라면 총 50회만 호출된다.
// 이 함수에서는 화면에 표시해야할 이미지가 있을때 로딩한다. (파일시스템으로 부터 로딩하면 되겠죠)
// 이 함수에서 추가로 표시해야할 데이터가 있다면 이때 다시 로딩한다. (예를들어서 내가 찾은 메모리의 속성정보나 모듈이름 등등등등)
}

이렇게 하면 100만개의 데이터를 리스트 컨트롤로 표시하더라도 키값이 4byte 라면 4메가의 데이터만 필요하다!!!!!

당장 쓸건 아니지만 알아두면 좋을것 같아서 포스팅해본다~~~

윈도우 모바일에서 X버튼은 프로그램을 SDI 혹은 MDI 으로 만들어야만 보여집니다.
(물론 직접 시스템 메뉴를 넣는다면 다이얼로그에서도 보여집니다.)

언제나 간단한 프로그램만 만들던 저에겐 다이얼로그 기반으로 만든 프로그램에서 OK 버튼 처리는 매우 쉬운 문제였습니다.
단지 OnOK() 함수를 오버라이드 하면 되니까요.

하지만 SDI 에서 X버튼은 조금 다릅니다.
아니 윈도우 모바일에서 다르다는말이 맞겠군요.

일반 Windows 플랫폼의 MFC 프로그램에서는 X버튼에 대한 처리는 OnClose() 에서 오버라이드해서 처리해주면 됩니다.

하지만 윈도우 모바일에서는 X버튼을 누를 경우 다르게 동작합니다.

일단 기본적으로 윈도우 모바일에서 X버튼은 프로그램을 백그라운드로 돌려버립니다.

그런 다음에 WM_SIZE 메세지가 호출됩니다. (물론 WM_CLOSE 나 WM_DESTROY 메세지는 호출되지 않습니다)

그래서 X버튼을 눌러서 프로그램을 종료하려면 WM_SIZE 의 메세지를 받아서 다음과 같이 처리하면 됩니다.
(MainFrame 에서 처리해야합니다.)

하지만 종료할때 사용자에게 물어봐야할 경우라면?

이미 윈도우는 백그라운드로 바뀐 상태에서 WM_SIZE 메세지를 타기 때문에 메세지 박스가 표시되지 않습니다.

그래서 꽁수로 백그라운드를 못하게끔 처리하는것입니다. -_-;

뭔가 정석이 있을것 같은데 해도 해도 안되길래 이런 꽁수를 생각했습니다.

그럼 이만.. .

Windows Mobile 에서 고유번호를 알아내는 정석은 없는것 같습니다.

각 디바이스 벤더마다 알아서 시리얼 넘버를 결정하고 시리얼넘버를 보여주는 API를 제공할 수도 있고 제공하지 않을 수도 있기 때문입니다.

이곳저곳을 막 뒤지다가 발견한 소스입니다.

완벽한 시리얼 넘버를 구현할 필요가 없다면 꽁수를 써보는것을 추천하겠습니다만... 그 꽁수도 생각 나지 않아서.-_-;

다음 소스를 참고하세요.


Windows Mobile 5.0 SDK 부터는 MS 가이드라인 때문에 메뉴 구성이

새로만들기  메뉴  IME

3가지 뿐입니다. 이 것을 변경시키려고 며칠을 삽질하다가 알아낸 정보 링크를 걸어둡니다.

먼저 주 목적은 위 메뉴에서 새로만들기 메뉴를 없애거나 조작하고 싶어서 자료를 찾아보았습니다.

그러다가 발견한 m_bShowSharedNewButton = FALSE; 해주는 방법.

하지만 VS 2008 에서는 해당 변수의 선언을 찾지 못한다는 컴파일 에러가 뜹니다.

그이유는 바로 VS 2005 개발 가이드라인 때문입니다.
http://msdn.microsoft.com/en-us/library/ms838254.aspx

MS 에서 guideline 으로 위처럼 3개의 메뉴 이외에는 메뉴바에 붙이지 않도록 하겠다 라고 선포해버린거죠.

그래서 이걸 어떻게 해야하나.. 하다가 발견한 링크입니다.

http://cafe.naver.com/latem/1035

비록 원하는 메뉴를 여러개 붙일 수는 없지만 저 보기싫은 새로만들기 버튼은 조작할 수 있습니다.

이런 멋진 팁을 알려주신 분 너무 고맙습니다. (_._);

MFC 에서 스크롤바에 대한 개념이 없이 스크롤바를 제어하려다가 며칠 삽질을 했습니다.

폼뷰를 생성하여 뷰로 폼형태를 보여줄때 리소스창에서 폼뷰의 다이얼로그를 매우 크게하면 자동으로 스크롤바가 생성됩니다.

그런데 리소스창에 폼뷰의 다이얼로그를 작게 그릴 경우 스크롤바가 사라집니다.

동적으로 화면의 스크롤을 지정하려면???



원하는 시점에 이 코드를 불러주면 화면의 스크롤 영역이 지정됩니다.

스크롤을 이리저리 끌다보면 폼이 같이 움직이죠..

아 삽질한 시간이 얼마인가. ㅜㅜ

CDialog 로 UI 를 구성할 때 조건에 따라서 UI 가 동적으로 생성되어야 하는 경우가 있습니다.

이때 해당 UI 의 이벤트 처리를 담당하는 함수를 어떻게 등록하는지 알아보겠습니다.

보통 CDialog 에 콤포넌트를 등록하려면 리소스 편집기에서 콤포넌트를 추가한 다음에 이벤트 핸들러를 등록합니다.

이런것들은 통합개발툴(IDE) 에서 해주는데요.. Visual Studio 가 그런 것을 자동으로 만들어주고 메시지맵을 등록시켜줍니다.
하지만 이런 경우는 콤포넌트의 위치나 생성이 정해져 있는 경우 이므로 동적으로 처리해야하는 방법은 직접 코딩을 해주어야합니다.

먼저 동적으로 처리하는 가장 확실한 방법은 직접 해당 콤포넌트를 서브클래싱 하는 방법입니다.

예를들어서  CButton 을 동적생성하여 해당 버튼이 클릭되었을때 이벤트를 처리하려면

class CMyButton : public CButton {}

이런식으로 서브클래싱한 후에 ButtonClicked 이벤트를 직접 처리해주는 것이죠.

하지만 단순한 동작을 하기 위해서 서브클래싱을 해준다면 파일이 매우 많아질 뿐더러 소스도 지저분해지게 됩니다. (사실 이게 지저분한 소스인지는 제 주관입니다.)

그래서 다른 방법을 찾아보았습니다.

지금 사용할 방법은 동적으로 등록한 콤포넌트의 이벤트 핸들을 해당 부모 윈도우인 Dialog 에서 간단하게 처리하는 방법입닏.

ON_CONTROL_RANGE 메세지 맵을 이용합니다.

예제입니다.

CMyDialog.h


CMyDialog.cpp



이벤트 처리 핸들러를 다른것으로 등록할 경우에는 다른 콤포넌트의 이벤트를 간단하게 받을 수 있습니다 .

+ Recent posts