블로그 이미지
fiadot_old

칼퇴근을 위한 게임 서버 개발 방법론에 대한 심도있는 고찰 및 성찰을 위한 블로그!

Rss feed Tistory
Technical Article/펌 2006. 3. 13. 16:54

SQL Server 2000 Paging and Sorting

SQL Server 2000 Paging and Sorting
Using ROWCOUNT and SQL_VARIANT By DejaVudew
http://www.codeproject.com/useritems/SQLServer2KPagingSorting.asp
,
Technical Article/펌 2006. 3. 10. 14:50

MFC Assertions

Visual Studio

MFC Assertions
MFC defines the ASSERT macro for assertion checking. It also defines the MFC ASSERT_VALID and CObject::AssertValid for checking the internal state of a CObject-derived object.

The MFC ASSERT macro halts program execution and alerts the user if the argument (an expression) evaluates to zero or false. If the expression evaluates to a nonzero, execution continues.

When an assertion fails, a message dialog box displays the name of the source file and the line number of the assertion. If you choose Retry in the dialog box, a call to AfxDebugBreak causes execution to break to the debugger. At that point, you can examine the call stack and other debugger facilities to determine the cause of the assertion failure. If you have enabled Just-in-time debugging, the dialog box can launch the debugger if it was not running when the assertion failure occurred.

The following example shows how to use ASSERT to check the return value of a function:

int x = SomeFunc(y);
ASSERT(x >= 0); // Assertion fails if x is negative
You can use ASSERT with the IsKindOf function to provide type checking of function arguments:

ASSERT( pObject1->IsKindOf( RUNTIME_CLASS( CPerson ) ) );
The ASSERT macro catches program errors only in the Debug version of your program. The macro produces no code in the Release version. If you need to evaluate the expression in the Release version, use the VERIFY macro instead of ASSERT.

See Also
Assertions
,
Technical Article/펌 2006. 3. 10. 14:28

프로젝트별 sdk 및 libaray 경로 지정하는 방법

Visual Studio C++ 6.0 기준



header include :
Project - Settings - C/C++ - Category(Preprocessor) - Additional include directories : 헤더 경로 추가

additional library path :
Project - Settings - Link - Category(Input) - Additional libray path : 라입 경로 추가


작성일 : 2006/03/10 14:28



Visual Studio .Net 2003 버젼

사용자 삽입 이미지

사용자 삽입 이미지

,
Technical Article/펌 2006. 2. 8. 06:37

C1010 에러날떄

C1010 에러날떄.... stdafx.h 프로젝트 셋팅 바꿔!
,
Technical Article/펌 2006. 1. 26. 08:26

[팁] 노트패드를 이용한 덤프

[팁] 노트패드를 이용한 덤프 안창근 / prodori



어딘가에서 노트패드를 이용한 덤프 방법이 있길래 살펴보았다가 하나 만들어 두는것도 괜찮다 싶어서 만들어 보았습니다.



기능

1. 노트패드가 실행되지 않았을 경우 노트패드를 실행시킨다.

2. 노트패드가 특정 파일을 열어놓은 상태에서도 출력이 원활하게 이루어지도록 한다.

3. 출력은 항상 제일 하단에 위치하도록 한다.



사용 방법

#include "http://pds5.devpia.com/vc_lec/8000/7241/DumpToNotePAD.h"



DumpToNotePAD("노트패드로 내용 출력중..");

DumpToNotePAD("[%s] %d, %.10lf, %s", sVal1, nVal, fVal, sVal2);

DumpToNotePAD("샘플 : %s", sVal);



되도록 MFC 클래스는 배제한 상태로 짰습니다. CPP의 내용은 다음과 같습니다.



#include "Tlhelp32.h"



static HWND hWndNotePad=NULL;



BOOL CALLBACK EnumWndProcForNotePAD(HWND hwnd, LPARAM lParam)

{

DWORD processID;

::GetWindowThreadProcessId(hwnd, &processID);

if (processID == (DWORD)lParam)

{

//동일한 프로세스 핸들.

//프로세스 핸들이 일정한 상태에서 최상위 부모윈도우를 찾는다.

HWND hParent = hwnd;

do

{

hwnd = hParent;

hParent = ::GetWindow(hParent, GW_OWNER);

::GetWindowThreadProcessId(hParent, &processID);

} while (hParent && processID == (DWORD)lParam);

if (::IsWindowVisible(hwnd))

{

//해당 윈도우가 사용가능한 윈도우인지 검사한다.

hWndNotePad = hwnd;

return FALSE; //작업을 종료한다.

}

}

return TRUE;

}



void DumpToNotePAD(char *fmt, ...)

{

char buffer[4096]={0};

va_list argptr;

va_start(argptr, fmt);

vsprintf(buffer, fmt, argptr);

va_end(argptr);

strcat(buffer, "\r\n");



//-------------------------------------------------------------------

//NotePAD를 찾는다.

for (int T=0; T<2; T++)

{

if (hWndNotePad == NULL || !IsWindow(hWndNotePad))

{

HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);

if (hSnapshot != (HANDLE)(-1))

{

//설정된 프로세스명을 가진 모든 프로세스ID를 획득한다.

PROCESSENTRY32 Process;

Process.dwSize = sizeof(PROCESSENTRY32);

if (Process32First(hSnapshot, &Process))

{

do

{

char handleName[512]={0};

strcpy(handleName, Process.szExeFile);

int len = strlen(handleName);

if (len > 1 && handleName[1] == ':') //전체 경로 포함

{

char drive[_MAX_DRIVE]={0};

char dir[_MAX_DIR]={0};

char fname[_MAX_FNAME]={0};

char ext[_MAX_EXT]={0};



_splitpath(handleName, drive, dir, fname, ext);

sprintf(handleName, "%s%s", fname, ext);

}

len = strlen(handleName);

//대문자로 변경

for (int i=0; i
*(handleName + i) = toupper(*(handleName+i));

//실행 프로세스 이미지명은 너무 길면 확장자 영역이 잘린다.

//따라서 첫부분이 일치하면 해당 프로세스를 찾은것으로 한다.

if (strstr(handleName, "NOTEPAD"))

::EnumWindows(EnumWndProcForNotePAD, (LPARAM)Process.th32ProcessID);

} while (Process32Next(hSnapshot, &Process));

}

CloseHandle(hSnapshot);

}

}



//NotePAD를 찾지 못한 경우 NotePAD를 실행시킨다.

if (hWndNotePad == NULL || !IsWindow(hWndNotePad))

{

::ShellExecute(NULL, NULL, "NotePAD.exe", NULL, NULL, SW_SHOWNORMAL);

Sleep(100); //NotePAD가 실행될 수 있는 시간을 준다.

}

else

break;

}

if (hWndNotePad == NULL || !IsWindow(hWndNotePad)) return; //예외 처리

//----------------------------------------------------------------------------



//NotePAD의 에디터 창을 찾는다.

//NotePAD는 구성상 첫번째 자식이 에디터 창이다.(SPY++로 확인 가능)

HWND hWnd = ::GetWindow(hWndNotePad, GW_CHILD);



//NotePAD의 하단으로 커서 위치 이동

::SendMessage(hWnd, EM_SETSEL,(DWORD)0,(DWORD)-1);

::SendMessage(hWnd, EM_SETSEL,(DWORD)-1,(DWORD)0);

//내용 출력

::SendMessage(hWnd, EM_REPLACESEL, 0, (LPARAM)buffer);

}
,
Technical Article/펌 2005. 10. 13. 14:15

빌드후 bat실행

빌드후 bat파일을 실행하려면
Project Settings에서 Post-build step에 *.bat파일명을 적어준다.
,
Technical Article/펌 2005. 10. 13. 11:32

디버그 모드 방지~

아래 어느분이 고생하셧다는 글을 보고.. 생각나 한자 적습니다..



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

if(IsDebuggerPresent())
{
AfxMessageBox("디버거가 검출되었음.. 크래킹 좀 하지 마.. 이자식아..!!");
// SystemHalt(); - 파란화면 의사코드 ;;;; 또는 Reboot(); or Format("C:");
}

F5 디버깅모드로 실행해보세요..~~

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


위와 같이 해버리면 IsDebuggerPresent 이놈 break point 걸어 jmp 해버리면 그만입니다.

크래킹은 결국은 할수 있는것이죠..

컴파일 되어봐야 어차피 이진코드로 된 코드이기 때문입니다.



다만 크래커들을 골탕을 먹이거나 복잡해 스스로 포기하게 만들면 어느정도의 효과는 기대해도 좋을 것입니다.



__declspec( naked ) int IsDebuggerRunning(void)
{
__asm
{
mov eax, dword ptr fs:[00000018h];
mov eax, dword ptr [eax+30h];
movzx eax, byte ptr [eax+2];
ret
}
}



실제 IsDebuggerPresent 내용을 그대로 옮긴것임.. 아마 NT계열 일때.. 98은 잘 모르겠습니다. 직접 어셈블리 내부로 트레이싱해보시길;;



fs:[00000018h] 이놈이 고정주소값이라 쓸 수 있는것이죠;;


물론 자신의 코드이기때문에 크래커들은 모릅니다. - 이점을 이용하는것이죠





그리고 메세지박스나 기타 윈도우를 띄울경우 그놈도 break point로 잡을 수 있으니

가급적 그 기회를 주지 말아야 합니다.



타이머를 이용해 아래를 주기적으로 체크하면서..



if(IsDebuggerRunning())

{

PostMessage(.....); // 이렇게 비동기적으로 한번 더 꼬아라..

}



void OnMessageHandler(...)

{

SendLocalInfoToServer(ip, computer_name, etc); // 잡아서 혼내주세요..

Reboot();

}



어떤 안내메세지도 띄우지 마세요.. 그것이 또 크래킹의 도구가 될수 있음..

조용히 컴터를 부팅하거나 꺼버리세요..



크래커들은 당황할것입니다.



여러 응용도 가능할것입니다. 자신만의 Anti-Cracking을 해보는것도...
,
Technical Article/펌 2005. 10. 11. 18:07

Class Wizard에서 지원안되는 클래스 나타내기

Class Wizard에서 지원안되는 클래스(3thd Party포함)를 Class Wizard에 나타내기 김인식 / iskim71



볼랜드,IBM 툴에 익숙해 있다 얼마전부터 비주얼스튜디오(이름이 넘 거창함)를 이용하여 개발을 하고 있습니다.

MFC로 개발하다 보니 Class Wizard란 넘을 자주 쓰게 되는데 이넘이 잘 나가다 가끔 삼천포로 빠지더군요



지원되는 기본 컴포넌트의 부실로 부득이 하게 3thd Party 클래스들을 이용하고 있습니다.

그런데... 클래스를 새로 생성할때 MFC로 생성하면 상속받을 클래스 선택권이 일방적이더군요...

결국 Generic으로 생성하게 되는데... 그렇게 되면 ClassWizard에 보이질 않는다는게 OTL...



여러 방법으로 삽질(?)을 하다 대략 2가지 방법으로 압축되더군요...



첫번째 방법은 다른 MFC클래스로 상속받은수 코딩으로 수정해준다.(사기치는거 같아 별로 맘에 안듬)



두번째 방법은

1. Generic으로 상속받을 클래스를 명기해주고 생성한뒤,

2. .h 파일이랑 .cpp파일에 MessageMap 선언을 해준다.



<예> Test.h 파일



#if !defined(AFX_TEST_H__214F2136_D953_4915_9669_E0A361C9A1EB__INCLUDED_)

#define AFX_TEST_H__214F2136_D953_4915_9669_E0A361C9A1EB__INCLUDED_



#if _MSC_VER > 1000

#pragma once

#endif // _MSC_VER > 1000



class CTest : public 상속받을클래스

{

public:

CTest();

virtual ~CTest();



// 요부분 ------------------------------------------------------------------------->>>

protected:

//{{AFX_MSG(CTest)

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

// 요부분 -------------------------------------------------------------------------<<<

};





#endif // !defined(AFX_TEST_H__214F2136_D953_4915_9669_E0A361C9A1EB__INCLUDED_)



<예> Test.cpp 파일



#include "stdafx.h"

#include "Test.h"



#ifdef _DEBUG

#undef THIS_FILE

static char THIS_FILE[]=__FILE__;

#define new DEBUG_NEW

#endif



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

// Construction/Destruction

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



CTest::CTest()

{



}



CTest::~CTest()

{



}



// 요부분 ------------------------------------------------------------------------->>>

BEGIN_MESSAGE_MAP(CTest, 상속받을클래스)

//{{AFX_MSG_MAP(CTest)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

// 요부분 -------------------------------------------------------------------------<<<





3. 저장하고 비쥬얼스튜디오(역시 거창한 이름...)를 닫는다.



4. 프로젝트 폴더로 가서 프로젝트명.ncb, 프로젝트명.clw 파일을 삭제한다.(백업하던지...)



5. 비쥬얼스튜디오(너 정말 스튜디오 맞아?)를 실행한다.



6. Rebuild All을 한번 꾹~ 눌러준다.



7. Ctrl+W 를 누르면 뭐라뭐라 하고 OK를 누르면 새로 생성하는 화면이 나온다. 여기서 우측아래에 있는

[Add All]버튼을 누르고 OK를 누른다.



8. Class Wizard에 보면 방금 추가한 클래스가 나타난다.



즐프 하시길...
,
Technical Article/펌 2005. 9. 22. 11:49

OUTPUT과 RETURN의 차이점.

OUTPUT 사용할때와 안사용할때의 차이점은? 이지헌 / hunt33



안녕하세요

CREATE PROCEDURE Test1
as
select Max(lk_no)+1
from LoanKind

exec Test1
이거랑

CREATE PROCEDURE Test2
@Code int OUTPUT
as
select @Code=Max(lk_no)+1
from LoanKind

declare @Code int
exec Test2 @Code output
select @Code
의 차이점은 모죠?

퀴리문석기에서 어차피 결과는 똑같이 나오는데 왜 OUTPUT을 사용하는거죠?






2002-11-15 오전 9:50:49 / 번호: 32126 / 평점: (-)

Re: 저의 생각 입니다.... 박진호 / pjh1365




제 같은 경우는 output 를 사용하는 경우는 배치 작업을 할 경우

그 결과 값이 정상적으로 처리 되었는지를 확인하기 위해서 많이 사용합니다.

간단한 프로시져는 그냥 작업을 합니다

또하나 output 을 사용해서 원하는 값만을 간단히 구할수도 있으니 좋구요

아니면 값을 하나씩 다시 패치 해야 하는 번거러움이 있습니다.

그래서 제 경우는 결과 값을 꼭 구해야 하는 것은 output 문장을 사용합니다.

이건 제 경우 입니다.

정확히 아시는 분 다시 한번 부탁 합니다.





2002-11-15 오전 11:16:36 / 번호: 32132 / 평점: (-)

Re: OUTPUT 파라미터와 return값, 레코드셋의 차이 이윤호 / nawhat



sp를 작성중에 내부에서 Select 하시는 경우는
그 결과값이 하나라고 하더라도 레코드셋으로 반환됩니다.

return 값으로 사용할 수 있는 것은 정수형뿐이고요.
한번 sp 실행에 하나의 값만 반환합니다.

OUTPUT 파라미터의 경우는 여러 타입으로 반환가능하고,
한번 sp 실행에 파라미터를 여러개 사용한다면 그 개수만큼 반환값을 얻을 수 있죠.


QA에서 사용하실때는 그 차이를 크게 못느끼실 수 있지만, ADO의 COMMAND 를 사용해서
호출하는 경우에 그 차이는 큽니다.
일단 레코드셋을 리턴하는 경우에는 레코드셋을 close해야만 return값과 output을 받을 수 있고요.(ADO의 경우)
처음 예시로 주신 방법으로 사용한다면 레코드셋에서 값을 추출해야 사용할 수 있게되죠.
반면에, OUTPUT 파라미터와 return 값은 레코드셋에서의 추출없이 바로 변수로 사용가능합니다.
단 return값은 정수형으로밖에 반환되지 않으며, 한번에 하나의 값밖에 얻을 수 없습니다.

저의 경우 return값은 sp의 성공여부나 에러발생시 에러번호를 리턴하고, OUTPUT은 한 레코드 정보만 받을때 사용하고요, 레코드셋이 필요한 경우에는 레코드셋을 반환시킵니다.
,
Technical Article/펌 2005. 9. 12. 10:19

IP가 고정인지' DHCP로 할당 된건지를 알 수 있는 방법

현재 IP가 고정인지' DHCP로 할당 된건지를 알 수 있는 방법 좀 갈켜 주십시...



Platform SDK 에 이에 관련된 소스가 있습니다.
작 성 자 황해수(proman2k) 첨 부
파 일
작성시각 2001-10-15 오후 12:44:01
조 회 수 493

저도 이것땜에 잠시나마 고민을 했었는데 간단하더군요.
오픈된 소스가 있으니....그냥 써버리면 되거든요.
허나..이에 대한 이해가 남달리 필요하겠지요.
아래 소스는 제가 제 프로그램에 맞게 수정한것입니다.
원 소스는 콘솔로 정보를 출력했거든요.
그리고 windows 2000에서만 되는걸로 알고 있습니다.
98이나 ME에선 아직 테스트는 못해봤습니다.

아래 소스에서 DHCP 부분에 YES 아니면 NO라는 값으로 DHCP의 사용 여부를 확인
할 수 있습니다.

수고하세요.

void CVirtualListView::OnButtonConfig()
{
// TODO: Add your command handler code here

CString IPConfigMSG; // Host Name
CString strpAddrStr; // second DNS Server
CString strNodeType; // Node Type
CString strpAdaptType; // Adatper Type
CString strAdapterName; // Adapter Name
CString strpAdaptDescription; // Adaptter Description
CString strpAdaptAddress; // MAC Address in Adapter ROM
CString strpAdaptDhcpEnabled; // DHCP Enable or Desable
CString strpAddrStrIpAddress; // Host"s IP Address
CString strpAddrStrIpMask; //Subnet Mask
CString strpAdaptGatewayList; // Default Gateway Address

CString strpAdaptDhcpServer; // DHCP Server is or not
CString strpAdaptPrimaryWinsServer; // Primary WINS Server
CString strpAdaptSecondaryWinsServer;// Secondary WINS Server


DWORD Err;

PFIXED_INFO pFixedInfo;
DWORD FixedInfoSize = 0;

PIP_ADAPTER_INFO pAdapterInfo, pAdapt;
DWORD AdapterInfoSize;
PIP_ADDR_STRING pAddrStr;

//
// Get the main IP configuration information for this machine using a FIXED_INFO structure
//
if ((Err = GetNetworkParams(NULL, &FixedInfoSize)) != 0)
{
if (Err != ERROR_BUFFER_OVERFLOW)
{
AfxMessageBox("GetNetworkParams sizing failed with error %d\n", Err);
return;
}
}

// Allocate memory from sizing information
if ((pFixedInfo = (PFIXED_INFO) GlobalAlloc(GPTR, FixedInfoSize)) == NULL)
{
AfxMessageBox("Memory allocation error\n");
return;
}

if ((Err = GetNetworkParams(pFixedInfo, &FixedInfoSize)) == 0)
{

//AfxMessageBox(pFixedInfo->HostName);

/*////////////////// Must to Print Message and Values /////////////////*/
//printf("\tHost Name . . . . . . . . . : %s\n", pFixedInfo->HostName);
//printf("\tDNS Servers . . . . . . . . : %s\n", pFixedInfo-
>DnsServerList.IpAddress.String);
//pAddrStr = pFixedInfo->DnsServerList.Next;

IPConfigMSG += pFixedInfo->HostName;

/*
while(pAddrStr)
{
strpAddrStr = pAddrStr->IpAddress.String;
//printf("%52s\n", pAddrStr->IpAddress.String);
pAddrStr = pAddrStr->Next;
}
*/

switch (pFixedInfo->NodeType)
{
case 1:
strNodeType = "Broadcast";
break;
case 2:
strNodeType = "Peer to peer";
break;
case 4:
strNodeType = "Mixed";
break;
case 8:
strNodeType = "Hybrid";
break;
default:
strNodeType = "\n";
}

} else
{
AfxMessageBox ("GetNetworkParams failed with error :" + Err);
return;
}

//
// Enumerate all of the adapter specific information using the IP_ADAPTER_INFO structure.
// Note: IP_ADAPTER_INFO contains a linked list of adapter entries.
//
AdapterInfoSize = 0;
if ((Err = GetAdaptersInfo(NULL, &AdapterInfoSize)) != 0)
{
if (Err != ERROR_BUFFER_OVERFLOW)
{
AfxMessageBox("GetAdaptersInfo sizing failed with error : " + Err);
return;
}
}

// Allocate memory from sizing information
if ((pAdapterInfo = (PIP_ADAPTER_INFO) GlobalAlloc(GPTR, AdapterInfoSize)) == NULL)
{
AfxMessageBox("Memory allocation error\n");
return;
}

// Get actual adapter information
if ((Err = GetAdaptersInfo(pAdapterInfo, &AdapterInfoSize)) != 0)
{
AfxMessageBox("GetAdaptersInfo failed with error %d\n", Err);
return;
}

pAdapt = pAdapterInfo;

while (pAdapt)
{
switch (pAdapt->Type)
{
case MIB_IF_TYPE_ETHERNET:
strpAdaptType = "Ethernet adapter";
break;
case MIB_IF_TYPE_TOKENRING:
strpAdaptType = "Token Ring adapter";
break;
case MIB_IF_TYPE_FDDI:
strpAdaptType = "FDDI adapter";
break;
case MIB_IF_TYPE_PPP:
strpAdaptType = "PPP adapter";
break;
case MIB_IF_TYPE_LOOPBACK:
strpAdaptType = "Loopback adapter";
break;
case MIB_IF_TYPE_SLIP:
strpAdaptType = "Slip adapter";
break;
case MIB_IF_TYPE_OTHER:
default:
strpAdaptType = "Other adapter";
}

strAdapterName = pAdapt->AdapterName;

strpAdaptDescription = pAdapt->Description;

CString imsi;

for (UINT i=0; i < pAdapt->AddressLength; i++)
{
if (i == 5)
{
imsi.Format("%02X",(int)pAdapt->Address[i]);
strpAdaptAddress += imsi;
}
else
{
imsi.Format("%02X-",(int)pAdapt->Address[i]);
strpAdaptAddress += imsi;
}
}

strpAdaptDhcpEnabled = pAdapt->DhcpEnabled ? "yes" : "no";

pAddrStr = &(pAdapt->IpAddressList);
while(pAddrStr)
{
strpAddrStrIpAddress = pAddrStr->IpAddress.String;
strpAddrStrIpMask = pAddrStr->IpMask.String;
pAddrStr = pAddrStr->Next;
}

strpAdaptGatewayList = pAdapt->GatewayList.IpAddress.String;
pAddrStr = pAdapt->GatewayList.Next;

while(pAddrStr)
{
pAddrStr->IpAddress.String;
pAddrStr = pAddrStr->Next;
}

strpAdaptDhcpServer = pAdapt->DhcpServer.IpAddress.String;
strpAdaptPrimaryWinsServer = pAdapt->PrimaryWinsServer.IpAddress.String;
strpAdaptSecondaryWinsServer = pAdapt->SecondaryWinsServer.IpAddress.String;


/*
struct tm *newtime;

// Display coordinated universal time - GMT
newtime = gmtime(&pAdapt->LeaseObtained);
printf( "\tLease Obtained. . . . . . . : %s", asctime( newtime ) );

newtime = gmtime(&pAdapt->LeaseExpires);
printf( "\tLease Expires . . . . . . . : %s", asctime( newtime ) );
*/ // 이부분은 제게 필요 없어서 주석처리 한것입니다. 필요하시면 사용하세요.
pAdapt = pAdapt->Next;

}
CPickTableDlg *dlg = new CPickTableDlg;
dlg->DoModal();

}







IP Changer

라입
IPHlpAPI.lib


/////////////////////////////
// IP Helper Function Header
// IPHlpAPI.lib을 쓴다.
#include
/////////////////////////////
,
TOTAL TODAY