Monday, April 6, 2015

C# 디버그 메세지 출력하기

C#에서 OutputDebugString을 할려면 다음과 같은 2가지 방법이 있다.

Debug.Write(), Trace.Write()의 차이점은 Debug는 Debug모드 빌드에서만 동작하며, Trace는 Release/Debug 둘다 동작한다는 점이다. 물론 OutputDebugString기반이므로 DebugView에서도 확인할 수 있다.

System.Diagnostics.Debug.Write("Debug1");
System.Diagnostics.Debug.Write("Debug2");
System.Diagnostics.Trace.Write("Trace1");
System.Diagnostics.Trace.Write("Trace2");

DebugView의 특징과 실수하기 쉬운 것들

DebugView는 Win32 개발환경에서 디버그 메세지 확인용 함수인 OutputDebugString의 결과를 따로 모아서 보여주는 유틸리티이다.

그런데 OutputDebugString의 특성 때문에 몇가지 상황을 겪을수 있는데 그 경우의 몇가지 예는 다음과 같다.
  1. Visual Studio를 F5모드(Ctrl+F5가 아닌)로 Debug 시켰을 경우에는 메세지를 보여주지 않는다.
  2. DebugView가 두개이상 띄워졌을 경우에는 한곳에서만 Connect되어 있다. 다시 Connect할려면 [Computer] - [Connect Local]하면된다.
  3. .NET에서는 OutputDebugString이 있지만 System.Diagnostics.Trace.Write()함수를 사용한다. 그런데 System.Diagnostics.Debug.Write()를 사용하면 Release에서는 메세지가 보이지 않는다. MFC에서 TRACE()도 마찬가지이다.

PHP 4.0 SOAP

PHP로 SOAP을 구현하려면 PHP5가 있어야 된다.
PHP5에만 SOAP이 내장되어 있다.

그러나 PHP4에서 구현을 하려면 NuSOAP을 사용하면 된다.
그리고 PHP5도 동일한 방법을 사용하면 되기 때문에 NuSOAP으로 하는것이 편하다.

nusoap-0.7.3.zip
nusoap-docs-0.7.3.zip
Mail_Mime-1.4.0.gz

세개의 파일이 필요한데
Mime은 NuSOAP 0.7.3에서 바이너리를 SOAP으로 보낼때 쓴다.

첨부된 nusoap.zip을 받고
htdocs폴더에다 lib에 풀고

// NuSOAP설정
 include_once('lib/nusoap.php');
 include_once('lib/nusoapmime.php');
 // WSDL설정
 $wsdl = 'http://url/WebService/Service?wsdl';
 // NuSOAP설정
 $client = new nusoap_client($wsdl, 'wsdl');
 $client->setEndpoint("'http://url/WebService/Service");

위와 같이 하면 함수를 호출할 수 있다.
반드시 endPoint를 설정해야 한다.

다운로드

Visual C++ 다른 프로그램의 메세지 박스 자동 Yes 클릭하기

다른 Application을 띄워서 매번 실행해야되는데 시작시 수동으로 매번 MessageBox에서 Yes를 선택해야 되는 경우 자동으로 Yes를 누르는 프로그램이다.

Win32 Application을 만든다. 메인 메세지 루프에서 다음과 같이 타이머를 설정하자.

switch (message)
{
case WM_CREATE:
hwndMain = hWnd;
SetTimer(hWnd, 1, 100, NULL);
...
  case WM_TIMER:
switch(wParam){
case 1:
KillTimer(hWnd, 1);
EnumWindows(FIndMessageBoxProc, lParam);
SetTimer(hWnd, 1, 100, NULL);
...

메세지 박스는 기본적으로 IDYES와 IDNO를 아이디로 가진다. 그리고 메세지 박스의 WindowText를 알아두자. (Spy++을 사용하면 쉽다.)

EnumWindows라는 함수로 Desktop이하의 모든 윈도우를 Enumerate 한 후에 해당 MessageBox를 찾자.
BOOL CALLBACK FIndMessageBoxProc(HWND hWnd, LPARAM lParam)
{
char text[512];
GetWindowTextA(hWnd, text, 512);

if(!strcmp(text, "MessageBoxApplication")) {// 메세지 박스 이름
EnumChildWindows(hWnd, FIndYesBtnProc, lParam);
SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(BN_SETFOCUS, IDYES), (LPARAM)hwndYes);
OutputDebugStringA("ManusHider WM_COMMAND BN_SETFOCUS");

SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(BN_CLICKED, IDYES), (LPARAM)hwndYes);
OutputDebugStringA("ManusHider WM_COMMAND BN_CLICKED");

//KillTimer(hwndMain, 1);
//SetTimer(hwndMain, 2, 100, NULL);
return false;
}
return true;
}

그리고 IDYES라고 아이디를 알고 있더라도 BN_CLICKED를 발생시키려면 그 버튼의 HWND도 알아야 한다.

BOOL CALLBACK FIndYesBtnProc(HWND hWnd, LPARAM lParam)
{
char text[512];
GetWindowTextA(hWnd, text, 512);
if(!strcmp(text,"&Yes")) {
hwndYes = hWnd;
return false;
}
return true;
}

중요한 점은 현재 작성하는 프로그램에 Focus가 와있기 때문에 대상 프로그램에 BN_CLICKED를 SendMessage하기전에 BN_SETFOCUS를 먼저 줘야 한다는 점이다. 그리고 또하나는 Yes가 아니라 &Yes가 Yes버튼의 WindowText이다.

이렇게 하면 원하는 메세지박스에서 항상 Yes/No를 선택해야 되는 프로그램 테스트 실행시 자동으로 Yes를 클릭할 수 있다. 서비스 프로그램으로 만들어도 되고 위에처럼 매번 같이 실행하면서 종료해도 된다.

C++ stringstream 초기화

STL을 사용할때 buffer와 sprintf를 사용하는 대신에 stringstream을 사용하는데 이것의 사용법은 대충 알았었는데 초기화를 몰라서 얼마전에 찾았다. 아래와 같이 사용하고, 초기화를 하면 재사용을 할수 있다.

std::stringstream ss;
std::string data="1";

ss << "value:" << data;
// ss.str()를 하면 std::string으로 리턴된다.
OutputDebugStringA(ss.str().c_str());

//초기화. ss.clear()를 하면 초기화가 되지 않음
ss.str("");
//초기화 된 상태에서 다른 목적으로 재사용 가능
ss << ...