사용방법은 create_log_file() 후 write_log_file()함수를 호출해주면 됩니다.



// stdafx.cpp : 표준 포함 파일만 들어 있는 소스 파일입니다.
// CrevisFnIO.pch는 미리 컴파일된 헤더가 됩니다.
// stdafx.obj에는 미리 컴파일된 형식 정보가 포함됩니다.

#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <commctrl.h>
#include <direct.h>

헤더 파일 추가 후...

static char LogPath[MAX_PATH];

int write_log_file(char *wstring)
{
    FILE *LogFile;
    char l_str[100];
    SYSTEMTIME lpSystemTime;
    if ((LogFile = fopen(LogPath,"at" )) == NULL)
   {
            return(1);
   }
GetLocalTime(&lpSystemTime); // 현재 날짜, 시간 가져오는 함수
sprintf(l_str,"%02d.%02d.%04d - %02d:%02d:%02d:%03d : ",lpSystemTime.wDay,lpSystemTime.wMonth,lpSystemTime.wYear,lpSystemTime.wHour,lpSystemTime.wMinute,lpSystemTime.wSecond, lpSystemTime.wMilliseconds);

fputs( l_str, LogFile);
fputs( wstring, LogFile);
fputs( "\n", LogFile);
fclose(LogFile);
return(0);
}

int create_log_file()
{
    FILE *LogFile;
// get current application path
_getcwd(LogPath, MAX_PATH );
if(LogPath[strlen(LogPath) - 1] != '\\')
       strcat(LogPath,"\\");
strcat(LogPath,"LogFileName.log");

if ((LogFile = fopen(LogPath,"wt" )) == NULL)
{
     return(1);
}
fputs( "------------------------------ Log File Crate.... ", LogFile);
fputs( " Log File ------------------------------\n", LogFile);
fputs( "---------------------------------------------------------------------------------------------------\n", LogFile);

fclose(LogFile);
return(0);
}

출처 : http://blog.naver.com/PostView.nhn?blogId=nawoo&logNo=80135622658

Check List

  • Pointer
    초기화 되지 않은 포인터의 경우 디버그모드에서는 임의값 0xCD로 초기화를 수행하지만 릴리즈에서는 초기화를 수행하지 않는다. 디버그 모드에서 컴파일러 옵션을 조정하여 초기화 하지 않은 포인터 변수를 사용하는것을 예방 할 수 있다. /GZ 컴파일러 옵션은 기본적으로 VC++ 프로젝트 셋팅에서 기본값이 아니므로 필요하다면 추가해서 초기화 되지 않는 포인터의 값을 0xCC로 채우도록 해줘야 한다. /GZ 컴파일러 옵션의 가장 큰 목적은 초기화 하지 않은 메모리 변수의 값을 0xCCCCCCCC로 채워서 디버깅중에 개발자가 초기화 하지 않은 값임을 알 수 있도록 하는것이다.
    • 디버그모드에서 deallocator에 의해 해제된 메모리에 채워지는 값은 0xDD 이다.
  • Heap
    디버그 모드에서 힙영역에 메모리를 할당하게 되면 guard byte( 0xFD로 초기화 )를 추가적으로 할당하여 가장 흔하게 범하는 zero-base의 arrary 인덱스를 잘못 계산하여 경계를 벗어난 영역을 접근하거나 지우려고 하는 코드를 작성했다고 하더라도 디버그 모드에서는 크래쉬가 발생하지 않을 수 있다. 이런 잠재적인 버그를 가진 프로그램을 릴리즈 모드에서 실행시키게 되면 크래쉬가 발생하게 된다.
  • ASSERT
    ASSERT 구문은 디버그 모드에서는 공백으로 대체되어지는 점을 잊고 아래와 같이 쓰게 되는 경우 이 코드는 릴리즈에서는 공백으로 대체되어 실제로 체크를 할 수 없게 된다.
    ASSERT (OpenMyWindow () != NULL);
    위의 코드를 다음과 같이 바꾸게 되면 디버그와 릴리즈 모드 모두 정상적으로 동작하게 된다.
    hWND = OpenMyWindow();ASSERT (hWND != NULL);
    이런 체크를 릴리즈에서도 하고 싶다면 VERITY 매크로를 사용하도록 하자.
  • Prototypes 사용자 정의 메시지 처리를 위해 ON_MESSAGE 매크로를 사용중이라면 메시지 핸들러의 원형을 요구하는 타입에 정확하게 맞게 선언해줘야 한다. 디버그 모드에서는 원형에 일치하지 않더라도 컴파일러가 수정하여 동작하도록 만들어줘 버그를 발견하기 힘들게 만든다.
    afx_msg LRESULT <class>::OnMyMessage (WPARAM wParam, LPARAM lParam);
  • Optimization Max Speed 옵션은 속도 최적화에 촛점을 맞추고 있기 때문에 최적화 과정에서 안전하지 않을수 있다. 기본적으로 릴리즈 모드에서는 Maximize Speed 옵션이 기본이지만 안전하게 속도 최적화를 보장하는 Minimize Size를 추천한다. 그리고, #pragma 지시자를 사용하여 특정 영역의 옵션을 설정 할 수 있다는것을 기억하자.
    #pragma optimize("", off)// some code here #pragma optimize("", on)

MSDN에서 권고하는 릴리즈 빌드 문제 해결

  • ASSERT 문 검사
    • CheckList의 ASSERT
  • 디버그 빌드를 사용한 메모리 덮어쓰기 확인
    1. InitInstance 함수의 맨 처음 부분에 다음 줄을 추가합니다. 이렇게 하면 디버그 메모리 할당자가 모든 할당된 메모리 주위에 보호 바이트를 배치합니다. 그러나 이 보호 바이트의 변경 여부(변경되었으면 메모리 덮어쓰기가 발생했음)를 확인하지 않으면 보호 바이트는 아무런 소용이 없습니다. 보호 바이트의 변경 여부를 확인하면 버퍼가 제공되어 메모리 덮어쓰기를 해결할 수 있습니다.

      afxMemDF |= checkAlwaysMemDF;

      checkAlwaysMemDF 변수를 설정하면 MFC에서는 new 또는 delete가 호출될 때마다 AfxCheckMemory 함수를 호출하게 됩니다. 메모리 덮어쓰기가 감지된 경우에는 다음과 같은 TRACE 메시지가 생성됩니다.

      Damage Occurred! Block=0x5533

      이러한 메시지가 표시되는 경우에는 코드를 단계별로 실행하여 손상된 부분을 확인해야 합니다. 메모리 덮어쓰기가 발생한 부분을 보다 정확하게 구별하려면 사용자가 AfxCheckMemory를 명시적으로 호출하면 됩니다. 예를 들면 다음과 같습니다.

      ASSERT(AfxCheckMemory());    DoABunchOfStuff();ASSERT(AfxCheckMemory());

      첫 번째 ASSERT는 성공하고 두 번째 ASSERT는 실패하는 경우에는 두 호출 사이의 함수에서 메모리 덮어쓰기가 발생했을 가능성이 큽니다.

      응용 프로그램의 특성에 따라 afxMemDF를 사용하면 프로그램 실행이 너무 느려져서 테스트조차 수행할 수 없는 경우도 있습니다. 왜냐하면afxMemDF 변수는 new 및 delete가 호출될 때마다 AfxCheckMemory가 호출되도록 하기 때문입니다. 이 경우에는 위의 예제와 같이 AfxCheckMemory( ) 호출을 분산시켜야 하며, 이러한 방법으로 메모리 덮어쓰기를 구별해야 합니다.

  • 릴리스 빌드에 대한 디버그 정보 생성 활성화
    1. 프로젝트의 속성 페이지 대화 상자를 엽니다. 자세한 내용은 Visual C++ 프로젝트 속성 설정을 참조하십시오.
    2. /Z7 또는 /Zi를 활성화합니다.
    3. /INCREMENTAL:NO를 선택합니다.
    4. /DEBUG:Yes를 선택합니다.
    5. /OPT:REF를 선택합니다.
    6. /OPT:ICF를 선택합니다.
    • 이제 릴리스 빌드 응용 프로그램을 디버깅할 수 있습니다. 문제를 찾으려면 오류가 발생한 부분을 찾을 때까지 코드를 단계별로 실행하거나, Just-In-Time 디버깅을 사용하여 올바르지 않은 매개 변수 또는 코드를 확인합니다.

      프로그램이 디버그 빌드에서는 작동하지만 릴리스 빌드에서 작동하지 않으면 소스 코드에서 컴파일러 최적화 중 하나에 결함이 있는 경우일 수 있습니다. 문제를 격리하려면 문제의 원인이 되는 최적화와 파일을 찾을 때까지 각 소스 코드 파일에 대해 선택한 최적화를 비활성화해야 합니다. 예를 들어, 파일을 두 그룹으로 나누고 한 그룹에서 최적화를 비활성화한 다음 문제가 파일 하나에서만 발생할 때까지 각 그룹을 계속 나눌 수 있습니다.

      디버그 빌드에서 그러한 버그를 노출시키려면 /RTC를 사용합니다.

  • 메모리 덮어쓰기 확인
    힙 조작 함수 호출 시 액세스 위반이 발생되면 프로그램이 힙을 손상시켰을 가능성이 있습니다. 이러한 경우의 일반적인 증상은 다음과 같습니다.
    Access Violation in _searchseg
    _heapchk 함수는 디버그 빌드와 릴리스 빌드 모두에서(Windows NT에만 해당) 런타임 라이브러리 힙의 무결성을 확인하는 데 사용할 수 있습니다. _heapchk는 AfxCheckMemory 함수를 사용하여 힙 덮어쓰기를 확인하는 것과 같은 방법으로 사용할 수 있습니다. 예를 들면 다음과 같습니다.
    if(_heapchk()!=_HEAPOK)   DebugBreak();
    이 함수가 실패하는 경우에는 힙이 손상된 시점을 확인해야 합니다.

Reference

저번 포스트보다 Arp를 간단히 다뤄보고, CMD 명령어를 이용한 수정방법에 대해 알아보도록 하겠습니다. (동적을 정적으로)

일단 주소 결정 프로토콜 (Address Resolution Protocol, ARP)이란 IP 주소를 물리적 네트워크 주소로 대응시키기 위해 사용되는 프로토콜. 사용자는 IP 주소를 이용하여 인터넷과 연결하지만 이더넷상에서는 이더넷 주소를 이용하게 된다. 이를 위하여 IP 주소를 이더넷 주소로 변환시켜 주어야 하는데 이와 같이 IP 주소를 물리적 주소로 변환시키는 프로토콜을 ARP 이라 한다.  - 네이버 용어사전  펌

말로 이렇게 표현한 것보다 CMD 창에서 보도록 하겠습니다.


CMD 창에서 "arp -a" 명령어를 실행한 모습니다. 보시다시피, 명령어를 이용하면 [ 인터넷주소 : IP ]와 [물리적 주소 : MAC ] 그리고 [유형]이 보입니다.
그리고 [0xB] 저건 Adapter를 나타냅니다. Adapter란..저도 확실히 이거다 라고 설명을 못드리겠습니다. ( 저도 대충알고있어서;;) - 일단 IP와 MAC이 있는 한 공간(?)
저 Adapter가 한개 일수도, 여러개일 수도 있습니다. 따라서 Adapter 갯수가 몇개인지 알아야 됩니다. 전 일단 한개이군요.

 - ARP에 대해 좀 더 자세한 사항은 검색해보세요 ㅎㅎ (퍽!)

그럼 이제부터 Windows7 에서 저기 보이는 [유형]을 동적에서 정적으로 바꾸는 방법을 살펴보겠습니다. 제 포스트중에서 보면, 소스로 해결하는 방법이 있는데,
왜 그런지 몰라도, 윈도우7에서는 잘 작동이 안되더군요; 그래서 모색해서 명령어를 이용하는 방법을 선택했습니다 ㅠ_ㅠ;

http://www.codeproject.com/KB/IP/ARPTables.aspx 일단 여기서 CARP 클래스를 사용하도록 하겠습니다. (살색(?) 테투리안에 있는 CARP 소스만 사용하세요)

이 CARP 클래스는 ARP Table의 Entry들을 가져오거나 수정할 수 있는 함수로 구성되어 있습니다. (자세한 사항은 원본 사이트에서 보셔야됩니다.^^;)
암튼, 그래서 이 함수를 이용하여 수정하도록 시도하였지만..안되더군요;; 그래서 이 클래스의 힘(?)을 조금 빌려서 CMD 명령어와 같이 사용하였습니다.
 

일단, Static으로 바꿔주는 명렁어입니다. 참고하세요 - Windows7 용

netsh -c "interface ipv4" set neighbors "로컬 영역 연결" (혹은 인덱스번호)  "000.000.000.000" (IP) "00-00-00-00-00-00" (MAC)
  "로컬 영역 연결" 항목에는 String 값만이 아니라, 해당 인덱스번호도 들어갌 수 있습니다.
 그래서 저는 항상 로컬만 들어가기만 하는게 아니라서 인덱스번호를 이용하여 사용하였습니다.

Windows7 이외에 버전
arp -s "000.000.000.000" "00-00-00-00-00-00"



이제 소스입니다. 주석을 참고하세요 간단합니다.

Include 해야 될 것들 입니다.

#include <atlstr.h>

//
// SNMP HEADER
//
#include <snmp.h>                           
#pragma comment(lib, "snmpapi.lib")
//
// IPHLPAPI HEADER
//
#pragma comment(lib, "IPHLPAPI.lib")
#include <iphlpapi.h>
#include <iptypes.h>
// - ip help api 입니다. 이 라이브러리를 사용하면 , " /ipconfig /al " 에 대한 항목들을 가져올수있습니다. 이 라이브러리에 대해 공부하시는걸 강추합니다.
// 여러가지 좋은것들(?)이 많습니다
//
// SEHLLAPI HEADER // - cmd 명렁어를 날리기 위한 것.
//
#include <shellapi.h>



본문 소스입니니다.

void SetArpStatic(unsigned char *pIp);                                                           // Static으로 변환 함수
void GetAdapterIndexList(int* AdapterCount, int* AdapterIndexList);                  // 사용가능한 Adapter 개수와 Index List를 가져옴
bool GetMacID(unsigned char* pIp, int AdapterCount, int* AdapterIndexList, unsigned char* mac, int* AdapterIndex);   
// IP 가 있는 Adapter에서 MAC ID를 얻어옴
void ResetStatic(unsigned char* pIp, unsigned char* mac, int AdapterIndex);    //Static으로 변환 함수
void ResetStaticOtehrVersion(unsigned char* pIp, unsigned char* mac);          //WIndows7 이외에 버전용 변환 함수
int _tmain(int argc, _TCHAR* argv[])
{
 unsigned char ip[4] = {000,000,0000,000};   //변환 하고자 하는 IP 함수 입력
 //
 // 해당 IP 를 Static으로 변환
 SetArpStatic(&ip[0]);
 return 0;
}
void SetArpStatic(unsigned char *pIp)
{
 OSVERSIONINFOEX OSvER = {sizeof(OSVERSIONINFOEX),};
 GetVersionEx((OSVERSIONINFO*)&OSvER);
 int AdapterCount = 0;
 int AdapterIndex = 0;
 int AdapterIndexList[256] = {0,};
 unsigned char mac[6] = {0,};
 if(OSvER.dwMajorVersion >=6)   // Windows 7 일 경우
 {
  //
  // 사용가능한 Adapter 개수와 Adapter index 리스트 를 가져옴
  GetAdapterIndexList(&AdapterCount, AdapterIndexList);
  //
  // IP 가 있는 Adapter에서 MAC ID를 얻어옴
  GetMacID(pIp, AdapterCount, AdapterIndexList, (unsigned char*)&mac, &AdapterIndex);
  //
  // Dynamic 유형을 Static 유형으로 수정함
  ResetStatic(pIp, mac, AdapterIndex);
 }
 else         // 그 외 버전인 경우
 {
  GetAdapterIndexList(&AdapterCount, AdapterIndexList);
  GetMacID(pIp, AdapterCount, AdapterIndexList, (unsigned char*)&mac, &AdapterIndex);
  //
  // 다른 버전일 경우, 해당 IP와 MAC 주소만으로 수정함
  ResetStaticOtehrVersion(pIp, mac);
 }
}
void GetAdapterIndexList(int* AdapterCount, int* AdapterIndexList)
{
 ULONG    ulBufferSize = 0;
 PIP_ADAPTER_INFO pAdapter, pAdapterInfo = NULL;
 if (GetAdaptersInfo(NULL, &ulBufferSize) == ERROR_BUFFER_OVERFLOW)
 {
  pAdapterInfo = (PIP_ADAPTER_INFO)new BYTE[ulBufferSize];
  if (pAdapterInfo)
  {
   if (GetAdaptersInfo(pAdapterInfo, &ulBufferSize) == ERROR_SUCCESS)
   {
    pAdapter = pAdapterInfo;
    (*AdapterCount) = 0;
    while(pAdapter)
    {
     AdapterIndexList[*AdapterCount] = pAdapter->Index;
     pAdapter = pAdapter->Next;
     (*AdapterCount)++;
    }
   }
   delete pAdapterInfo;
  }
 }
}
bool GetMacID(unsigned char* pIp, int AdapterCount, int* AdapterIndexList, unsigned char* mac, int* AdapterIndex)
{
 int i;
 bool bFind = false;
 for(i = 0; i<AdapterCount; i++)
 {
  CARP  arp;
  arpTable Table[256];
  int   Counter;
  int   TableLength = sizeof(Table)/sizeof(arpTable);
  Counter = arp.GetEntries(Table, TableLength, AdapterIndexList[i]);  //Arp Table 에 대한 항목 가져오기
  for(int j=0; j<Counter; j++)
  {
   if( (Table[j].IPAddress[0] == pIp[0]) && (Table[j].IPAddress[1] == pIp[1]) &&
    (Table[j].IPAddress[2] == pIp[2]) && (Table[j].IPAddress[3] == pIp[3]) )   //해당 IP 와 같다면...
   {
    memcpy(mac, Table[j].MACAddress, sizeof(char)*6);
    (*AdapterIndex) = AdapterIndexList[i];
    bFind = true;
    break;
   }
  }
  if(bFind)
  {
   break;
  }
 }
 return false;
}
void ResetStatic(unsigned char* pIp, unsigned char* mac, int AdapterIndex)
{
 SHELLEXECUTEINFO execinfo;   //CMD 명렁어를 날리기 위한 거..
 char strParam[256] = {0, };
 sprintf_s (strParam, 256, " -c \"interface ipv4\" set neighbors %d \"%u.%u.%u.%u\" %02X-%02X-%02X-%02X-%02X-%02X",
  AdapterIndex,                                                      // AdapterIndex
  pIp[0],pIp[1],pIp[2],pIp[3],                                  // IP
  mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);  // MAC
 ZeroMemory(&execinfo, sizeof(execinfo));
 execinfo.cbSize = sizeof(execinfo);
 execinfo.lpVerb = _T("open");
 execinfo.lpFile = _T("netsh");
 execinfo.lpParameters = strParam;   
 execinfo.nShow = SW_HIDE;
 ShellExecuteEx(&execinfo);   //쿼리 뿅~
}
void ResetStaticOtehrVersion(unsigned char* pIp, unsigned char* mac)
{
 SHELLEXECUTEINFO execinfo;
 char strParam[256]= {0,};
 sprintf_s (strParam, 256, " -s \"%u.%u.%u.%u\" %02X-%02X-%02X-%02X-%02X-%02X",
  pIp[0],pIp[1],pIp[2],pIp[3],     //IP
  mac[0],mac[1],mac[2],mac[3],mac[4],mac[5]);  //MAC 
 ZeroMemory(&execinfo, sizeof(execinfo));
 execinfo.cbSize = sizeof(execinfo);
 execinfo.lpVerb = _T("open");
 execinfo.lpFile = _T("arp");
 execinfo.lpParameters = strParam;   
 execinfo.nShow = SW_HIDE;
 ShellExecuteEx(&execinfo);  //쿼리 뿅
}


혹시 어쩌나 몰라 소스 첨부합니당.



P.s Windows7 에서는 관리자권한 모드가 필요합니다.

http://seolis.tistory.com/95 참고하세요.

+ Recent posts