Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
643 lines
15 KiB
C++
643 lines
15 KiB
C++
#include "comline.hpp"
|
|
//#include <exception>
|
|
#include <stdio.h>
|
|
#include "comline.h"
|
|
|
|
#define COMLINE_WINDOW_NAME "Command Window"
|
|
#define COMLINE_WINDOW_CLASS_NAME "COMLINEWindowClass"
|
|
|
|
namespace NCOMLINE
|
|
{
|
|
const DWORD c_dwWindowedStyle = WS_OVERLAPPED|WS_VSCROLL;
|
|
const DWORD c_dwWindowedStyleEx = WS_EX_WINDOWEDGE;
|
|
const int c_FontSize = 12;
|
|
const int c_LineSpace = 2;
|
|
const int c_FlashTimerID = 37;
|
|
const int c_FlashElapse = 125;
|
|
|
|
const int c_ServerTimerID = 38;
|
|
const int c_ServerTimerElapse = 1000;
|
|
|
|
CCom_String n_Commands[] = // order is VERY important, see the ExecuteCommand function
|
|
{
|
|
"dumpfile",
|
|
"exit",
|
|
"attach",
|
|
"detach",
|
|
"killserver"
|
|
};
|
|
// CCommandLineClient *n_CurClient;
|
|
};
|
|
|
|
int CCommandLineClient::m_WndClassRefCount = 0;
|
|
using namespace NCOMLINE;
|
|
|
|
void CCommandLineClient::PushLine (const LineData& data)
|
|
{
|
|
if (m_NumLines == m_SizeLines)
|
|
{
|
|
int i;
|
|
LineData *temp;
|
|
|
|
temp = new LineData[m_SizeLines+50];
|
|
for (i=0;i<m_NumLines;i++)
|
|
{
|
|
temp[i] = m_Lines[i];
|
|
}
|
|
delete[] m_Lines;
|
|
m_Lines = temp;
|
|
m_SizeLines+=50;
|
|
}
|
|
m_Lines[m_NumLines] = data;
|
|
m_NumLines++;
|
|
|
|
|
|
LineData *iter;
|
|
|
|
iter = &(m_Lines[m_TopDisplay]);
|
|
int count=0;
|
|
int i;
|
|
for (i=m_TopDisplay;i<m_NumLines;i++)
|
|
{
|
|
count++;
|
|
iter++;
|
|
}
|
|
if (count > m_DisplayLines)
|
|
{
|
|
int min,max;
|
|
m_TopDisplay++;
|
|
GetScrollRange (m_Window,SB_VERT,&min,&max);
|
|
SetScrollRange (m_Window,SB_VERT,0,max+1,FALSE);
|
|
}
|
|
}
|
|
|
|
CCommandLineClient::CCommandLineClient (HINSTANCE inst)
|
|
{
|
|
m_Instance = inst;
|
|
m_Attached = false;
|
|
|
|
if (!m_WndClassRefCount)
|
|
{
|
|
WNDCLASS rootclass;
|
|
|
|
rootclass.style = CS_OWNDC|CS_DBLCLKS;
|
|
rootclass.lpfnWndProc = CommandLineWndProc;
|
|
rootclass.cbClsExtra = 0;
|
|
rootclass.cbWndExtra = sizeof (DWORD);
|
|
rootclass.hInstance = m_Instance;
|
|
rootclass.hIcon = LoadIcon (NULL,MAKEINTRESOURCE (IDI_APPLICATION));
|
|
rootclass.hCursor = LoadCursor (NULL,MAKEINTRESOURCE (IDC_ARROW));
|
|
rootclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
|
|
rootclass.lpszMenuName = NULL;
|
|
rootclass.lpszClassName = COMLINE_WINDOW_CLASS_NAME;
|
|
|
|
if (!RegisterClass (&rootclass)) // register the class for the command line window
|
|
return;
|
|
}
|
|
m_WndClassRefCount++;
|
|
m_Window = CreateWindowEx (c_dwWindowedStyleEx,COMLINE_WINDOW_CLASS_NAME,COMLINE_WINDOW_NAME,c_dwWindowedStyle,
|
|
CW_USEDEFAULT,CW_USEDEFAULT,80*12,(50*c_FontSize)+10,NULL,NULL,m_Instance,this);
|
|
if (!m_Window)
|
|
return;
|
|
SetScrollRange (m_Window,SB_VERT,0,1,FALSE);
|
|
SetWindowLong (m_Window,GWL_USERDATA,(LONG) this);
|
|
SetTimer (m_Window,c_FlashTimerID,c_FlashElapse,NULL);
|
|
SetTimer (m_Window,c_ServerTimerID,c_ServerTimerElapse,NULL);
|
|
|
|
m_Font = CreateFont (-c_FontSize,0,0,0,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,
|
|
OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,
|
|
DEFAULT_PITCH+FF_DONTCARE,"Courier");
|
|
|
|
m_CursorPen = CreatePen (PS_SOLID,1,RGB (0,0,0));
|
|
m_ShowCursor = true;
|
|
m_CursorLoc.x = 5;
|
|
m_CursorLoc.y = 1;
|
|
m_CursorPos = 0;
|
|
m_CurrentLine = "";
|
|
m_Lines = new NCOMLINE::LineData[50];
|
|
m_NumLines = 0;
|
|
m_SizeLines = 50;
|
|
// m_Lines.clear ();
|
|
m_TopDisplay = 0;
|
|
PushLine (LineData ("Command Line interface started",LINE_OUTPUT));
|
|
m_CurLineColor = RGB (0,0,0 );
|
|
m_CommandColor = RGB (128,0,0);
|
|
m_ArgColor = RGB (0,128,0);
|
|
m_OutColor = RGB (0,0,255);
|
|
// NCOMLINE::n_CurClient = this;
|
|
|
|
}
|
|
|
|
CCommandLineClient::~CCommandLineClient (void)
|
|
{
|
|
LineData data ("detach all",LINE_COMMAND);
|
|
ParseLine (data);
|
|
|
|
if (m_Attached)
|
|
DetachServer (data);
|
|
DeleteObject (m_Font);
|
|
DeleteObject (m_CursorPen);
|
|
m_WndClassRefCount--;
|
|
if (!m_WndClassRefCount)
|
|
UnregisterClass (COMLINE_WINDOW_CLASS_NAME,m_Instance);
|
|
}
|
|
|
|
RPC_STATUS CCommandLineClient::AttachServer (NCOMLINE::LineData& data)
|
|
{
|
|
if (m_Attached)
|
|
{
|
|
LineData fred ("detach all",LINE_COMMAND);
|
|
ParseLine (fred);
|
|
DetachServer (fred);
|
|
}
|
|
char compname[255];
|
|
RPC_STATUS status;
|
|
unsigned char * pszUuid = (unsigned char *) NULL;
|
|
// unsigned char * pszProtocolSequence = (unsigned char *) "ncalrpc";
|
|
// unsigned char * pszEndpoint = (unsigned char *) "ComLine";
|
|
|
|
// unsigned char * pszProtocolSequence = (unsigned char *) "ncacn_ip_tcp";
|
|
// unsigned char * pszEndpoint = (unsigned char *) "morfane[1404]";
|
|
unsigned char * pszNetworkAddress = (unsigned char *) NULL;
|
|
unsigned char * pszOptions = (unsigned char *) NULL;
|
|
/*
|
|
pszStringBinding = (unsigned char *) NULL;
|
|
status = RpCCom_StringBindingCompose(pszUuid,pszProtocolSequence,pszNetworkAddress,pszEndpoint,pszOptions,&pszStringBinding);
|
|
if (status)
|
|
return status;
|
|
*/
|
|
if (data.m_NumArgs == 1)
|
|
{
|
|
sprintf ((char *) pszStringBinding,"ncalrpc:[ComLine]");
|
|
}
|
|
else
|
|
{
|
|
CCom_String name;
|
|
|
|
name = data.m_Data.Mid (data.m_ArgBreak[0]+1,data.m_ArgBreak[1]-data.m_ArgBreak[0]+1);
|
|
strcpy (compname,(LPCTSTR) name);
|
|
sprintf ((char *) pszStringBinding,"ncacn_ip_tcp:%s[1404]",compname);
|
|
}
|
|
// pszStringBinding = (unsigned char *) "ncacn_ip_tcp:209.20.251.178[1404]";
|
|
|
|
/* Set the binding handle that will be used to bind to the server. */
|
|
status = RpcBindingFromStringBinding(pszStringBinding,&comline_IfHandle);
|
|
if (status)
|
|
return status;
|
|
m_Attached = true;
|
|
return 0;
|
|
}
|
|
|
|
RPC_STATUS CCommandLineClient::DetachServer (NCOMLINE::LineData& data)
|
|
{
|
|
RPC_STATUS status;
|
|
|
|
m_Attached = false;
|
|
|
|
#if 0
|
|
status = RpCCom_StringFree(&pszStringBinding); // remote calls done; unbind
|
|
if (status)
|
|
return (status);
|
|
#endif
|
|
|
|
pszStringBinding[0] = 0;
|
|
|
|
status = RpcBindingFree(&comline_IfHandle); // remote calls done; unbind
|
|
if (status)
|
|
return (status);
|
|
return 0;
|
|
|
|
}
|
|
|
|
void CCommandLineClient::ListServers (void)
|
|
{
|
|
}
|
|
|
|
void CCommandLineClient::ShowWindow (void)
|
|
{
|
|
assert (m_Window);
|
|
|
|
::ShowWindow (m_Window,SW_SHOW);
|
|
|
|
GetClientRect (m_Window,&m_WindowRect);
|
|
m_DisplayLines = (m_WindowRect.bottom - m_WindowRect.top) / (c_FontSize+c_LineSpace);
|
|
}
|
|
void CCommandLineClient::HideWindow (void)
|
|
{
|
|
assert (m_Window);
|
|
::ShowWindow (m_Window,SW_HIDE);
|
|
}
|
|
|
|
bool CCommandLineClient::DumpFile (NCOMLINE::LineData& data)
|
|
{
|
|
CCom_String filename;
|
|
|
|
if (data.m_NumArgs < 2)
|
|
return false;
|
|
filename = data.m_Data.Left (data.m_ArgBreak[1]);
|
|
filename = filename.Right (data.m_ArgBreak[1] - data.m_ComBreak-1);
|
|
return DumpFile (filename);
|
|
}
|
|
|
|
bool CCommandLineClient::DumpFile (const CCom_String& filename)
|
|
{
|
|
FILE *thefile;
|
|
CCom_String temp;
|
|
LineData *iter;
|
|
int i;
|
|
|
|
thefile = fopen (filename,"w");
|
|
if (!thefile)
|
|
return false;
|
|
|
|
iter = m_Lines;
|
|
for (i=0;i<m_NumLines;i++)
|
|
{
|
|
fprintf (thefile,(LPCTSTR) iter->m_Data);
|
|
fprintf (thefile,"\n");
|
|
iter++;
|
|
}
|
|
fprintf (thefile,(LPCTSTR) m_CurrentLine);
|
|
temp = "Data dumped to file ";
|
|
temp += filename;
|
|
PushLine ((LPCTSTR) temp);
|
|
|
|
if (!fclose (thefile))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void CCommandLineClient::DumpToFile (const char *filename)
|
|
{
|
|
DumpFile (filename);
|
|
}
|
|
|
|
void CCommandLineClient::PushLine (const char *line)
|
|
{
|
|
LineData data(line,LINE_OUTPUT);
|
|
|
|
PushLine (data);
|
|
InvalidateRect (m_Window,NULL,TRUE);
|
|
}
|
|
|
|
void CCommandLineClient::ExecuteLine (const char *line)
|
|
{
|
|
LineData data(line,LINE_COMMAND);
|
|
ParseLine (data);
|
|
PushLine (data);
|
|
ExecuteLine (data);
|
|
}
|
|
|
|
void CCommandLineClient::KillServer (NCOMLINE::LineData& data)
|
|
{
|
|
unsigned long ulCode;
|
|
|
|
m_Attached = false;
|
|
|
|
RpcTryExcept
|
|
{
|
|
Shutdown(); // shut down the server side
|
|
}
|
|
RpcExcept(1)
|
|
{
|
|
ulCode = RpcExceptionCode();
|
|
}
|
|
RpcEndExcept
|
|
}
|
|
|
|
void CCommandLineClient::ExecuteLine (LineData& data)
|
|
{
|
|
int i,count;
|
|
CCom_String com;
|
|
bool flag = false;
|
|
|
|
com = data.m_Data.Left (data.m_ComBreak);
|
|
count = sizeof (n_Commands) /sizeof (CCom_String);
|
|
for (i=0;i<count;i++)
|
|
{
|
|
if (com == n_Commands[i])
|
|
{
|
|
switch (i)
|
|
{
|
|
case 0: //dumpfile
|
|
DumpFile (data);
|
|
flag = true;
|
|
break;
|
|
case 1: // exit
|
|
PostQuitMessage (0);
|
|
flag = true;
|
|
break;
|
|
case 2: // attach
|
|
AttachServer (data);
|
|
flag = true;
|
|
break;
|
|
case 3: //detach
|
|
DetachServer (data);
|
|
flag = true;
|
|
break;
|
|
case 4: //kill server
|
|
KillServer (data);
|
|
flag = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!flag)
|
|
{
|
|
if (m_Attached)
|
|
{
|
|
try
|
|
{
|
|
::ExecuteLine ((const unsigned char *) ((LPCTSTR) data.m_Data));
|
|
}
|
|
catch (...)
|
|
{
|
|
LineData data ("detach all",LINE_COMMAND);
|
|
ParseLine (data);
|
|
DetachServer (data);
|
|
PushLine ("Error: Had to detach from server");
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
void CCommandLineClient::ParseLine (LineData& data)
|
|
{
|
|
char *buf;
|
|
int i,size;
|
|
bool ignore;
|
|
|
|
ignore = false;
|
|
data.m_Data.TrimLeft ();
|
|
data.m_ComBreak = -1;
|
|
size = data.m_Data.GetLength ();
|
|
buf = data.m_Data.GetBuffer (0);
|
|
for (i=0;i<size;i++,buf++)
|
|
{
|
|
if ((*buf == ' ') && !ignore)
|
|
{
|
|
data.m_ArgBreak[data.m_NumArgs] = i;
|
|
data.m_NumArgs++;
|
|
}
|
|
else if (*buf == '\"')
|
|
{
|
|
ignore = !ignore;
|
|
}
|
|
}
|
|
data.m_Data.ReleaseBuffer ();
|
|
data.m_ArgBreak[data.m_NumArgs] = data.m_Data.GetLength ();
|
|
data.m_NumArgs++;
|
|
data.m_ComBreak = data.m_ArgBreak[0];
|
|
}
|
|
|
|
LRESULT CCommandLineClient::WndProc (HWND thewnd,UINT themsg,WPARAM wParam,LPARAM lParam)
|
|
{
|
|
switch (themsg)
|
|
{
|
|
case WM_PAINT:
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HPEN temppen;
|
|
HFONT oldfont;
|
|
SIZE TextSize;
|
|
|
|
BeginPaint (thewnd,&ps);
|
|
|
|
int curx,cury;
|
|
LineData *iter;
|
|
int i;
|
|
|
|
SetTextAlign (ps.hdc,TA_LEFT+TA_TOP);
|
|
SetTextColor (ps.hdc,m_CommandColor);
|
|
SetBkColor (ps.hdc,RGB (0,0,0));
|
|
SetBkMode (ps.hdc,TRANSPARENT);
|
|
oldfont = (HFONT) SelectObject (ps.hdc,m_Font);
|
|
|
|
|
|
curx = 0;
|
|
cury = 0;
|
|
iter = &(m_Lines[m_TopDisplay]);
|
|
for (i=m_TopDisplay;i<m_NumLines;i++)
|
|
{
|
|
if (iter->m_Type == LINE_OUTPUT)
|
|
{
|
|
SetTextColor (ps.hdc,m_OutColor);
|
|
TextOut (ps.hdc,curx,cury,(LPCTSTR)iter->m_Data,iter->m_Data.GetLength ());
|
|
}
|
|
else if (iter->m_Type == LINE_COMMAND)
|
|
{
|
|
char *buf;
|
|
|
|
buf = iter->m_Data.GetBuffer (0);
|
|
SetTextColor (ps.hdc,m_CommandColor);
|
|
TextOut (ps.hdc,curx,cury,(LPCTSTR)buf,iter->m_ComBreak);
|
|
if ((iter->m_Data.GetLength () - iter->m_ComBreak) > 0)
|
|
{
|
|
GetTextExtentPoint32 ( ps.hdc, (LPCTSTR)buf,iter->m_ComBreak ,&TextSize );
|
|
LPtoDP (ps.hdc,(POINT *)&TextSize,1);
|
|
curx += TextSize.cx;
|
|
SetTextColor (ps.hdc,m_ArgColor);
|
|
TextOut (ps.hdc,curx,cury,(LPCTSTR)&(buf[iter->m_ComBreak]),iter->m_Data.GetLength () - iter->m_ComBreak);
|
|
}
|
|
iter->m_Data.ReleaseBuffer ();
|
|
curx = 0;
|
|
}
|
|
else
|
|
{
|
|
}
|
|
cury += c_FontSize;
|
|
cury += c_LineSpace;
|
|
iter++;
|
|
}
|
|
|
|
SetTextColor (ps.hdc,m_CurLineColor);
|
|
TextOut (ps.hdc,curx,cury,(LPCTSTR)m_CurrentLine,m_CurrentLine.GetLength ());
|
|
GetTextExtentPoint32 ( ps.hdc, (LPCTSTR)m_CurrentLine, m_CursorPos ,&TextSize );
|
|
LPtoDP (ps.hdc,(POINT *)&TextSize,1);
|
|
m_CursorLoc.x = TextSize.cx;
|
|
m_CursorLoc.y = cury;
|
|
|
|
if (m_ShowCursor && m_CursorFlash)
|
|
{
|
|
temppen = (HPEN) SelectObject (ps.hdc,m_CursorPen);
|
|
MoveToEx (ps.hdc,m_CursorLoc.x,m_CursorLoc.y,NULL);
|
|
LineTo (ps.hdc,m_CursorLoc.x,m_CursorLoc.y+c_FontSize);
|
|
SelectObject (ps.hdc,temppen);
|
|
}
|
|
|
|
SelectObject (ps.hdc,oldfont);
|
|
|
|
EndPaint (thewnd,&ps);
|
|
return 0;
|
|
}
|
|
break;
|
|
case WM_TIMER:
|
|
if (wParam == c_FlashTimerID)
|
|
{
|
|
m_CursorFlash = !m_CursorFlash;
|
|
{
|
|
RECT temprect;
|
|
temprect.left = m_CursorLoc.x;
|
|
temprect.top = m_CursorLoc.y;
|
|
temprect.right = m_CursorLoc.x+2;
|
|
temprect.bottom = m_CursorLoc.y+c_FontSize;
|
|
InvalidateRect (m_Window,&temprect,TRUE);
|
|
}
|
|
}
|
|
else if (wParam == c_ServerTimerID)
|
|
{
|
|
int num,i;
|
|
char line[256];
|
|
|
|
if (m_Attached)
|
|
{
|
|
try
|
|
{
|
|
num = NumOutputLines ();
|
|
for (i=0;i<num;i++)
|
|
{
|
|
GetOutputLine (256,(unsigned char *) line);
|
|
PushLine (line);
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
LineData data ("detach all",LINE_COMMAND);
|
|
ParseLine (data);
|
|
|
|
DetachServer (data);
|
|
PushLine ("Error: Had to detach from server");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case WM_KEYDOWN:
|
|
switch (wParam)
|
|
{
|
|
case VK_LEFT:
|
|
m_CursorPos = clamp<int> (0,m_CursorPos-1,m_CurrentLine.GetLength ());
|
|
InvalidateRect (m_Window,NULL,TRUE);
|
|
break;
|
|
case VK_RIGHT:
|
|
m_CursorPos = clamp<int> (0,m_CursorPos+1,m_CurrentLine.GetLength ());
|
|
InvalidateRect (m_Window,NULL,TRUE);
|
|
break;
|
|
case VK_HOME:
|
|
m_CursorPos = 0;
|
|
InvalidateRect (m_Window,NULL,TRUE);
|
|
break;
|
|
case VK_END:
|
|
m_CursorPos = m_CurrentLine.GetLength ();
|
|
InvalidateRect (m_Window,NULL,TRUE);
|
|
break;
|
|
case VK_DELETE:
|
|
m_CurrentLine.Delete (m_CursorPos);
|
|
InvalidateRect (m_Window,NULL,TRUE);
|
|
break;
|
|
}
|
|
break;
|
|
case WM_VSCROLL:
|
|
switch (LOWORD (wParam))
|
|
{
|
|
case SB_BOTTOM:
|
|
break;
|
|
case SB_ENDSCROLL:
|
|
break;
|
|
case SB_LINEDOWN:
|
|
break;
|
|
case SB_LINEUP:
|
|
break;
|
|
case SB_PAGEDOWN:
|
|
break;
|
|
case SB_PAGEUP:
|
|
break;
|
|
case SB_THUMBPOSITION:
|
|
break;
|
|
case SB_THUMBTRACK:
|
|
break;
|
|
case SB_TOP:
|
|
break;
|
|
}
|
|
InvalidateRect (m_Window,NULL,FALSE);
|
|
break;
|
|
case WM_CHAR:
|
|
switch (wParam)
|
|
{
|
|
case '\b':
|
|
if (m_CursorPos)
|
|
{
|
|
m_CurrentLine.Delete (m_CursorPos-1);
|
|
m_CursorPos--;
|
|
}
|
|
break;
|
|
case '\r':
|
|
{
|
|
m_CurrentLine.Insert (m_CursorPos,'\0');
|
|
LineData data(m_CurrentLine,LINE_COMMAND);
|
|
|
|
ParseLine (data);
|
|
PushLine (data);
|
|
ExecuteLine (data);
|
|
m_CurrentLine = "";
|
|
m_CursorPos = 0;
|
|
|
|
|
|
LineData *iter;
|
|
|
|
iter = &(m_Lines[m_TopDisplay]);
|
|
int count=0;
|
|
int i;
|
|
for (i=m_TopDisplay;i<m_NumLines;i++)
|
|
{
|
|
count++;
|
|
iter++;
|
|
}
|
|
if (count > m_DisplayLines)
|
|
{
|
|
int min,max;
|
|
m_TopDisplay++;
|
|
GetScrollRange (m_Window,SB_VERT,&min,&max);
|
|
SetScrollRange (m_Window,SB_VERT,0,max+1,FALSE);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
m_CurrentLine.Insert (m_CursorPos,wParam);
|
|
m_CursorPos++;
|
|
break;
|
|
}
|
|
InvalidateRect (m_Window,NULL,TRUE);
|
|
break;
|
|
}
|
|
|
|
return DefWindowProc (thewnd,themsg,wParam,lParam);
|
|
}
|
|
|
|
LRESULT WINAPI NCOMLINE::CommandLineWndProc (HWND thewnd,UINT themsg,WPARAM wParam,LPARAM lParam)
|
|
{
|
|
void *temp;
|
|
|
|
switch (themsg)
|
|
{
|
|
case WM_CREATE:
|
|
CREATESTRUCT *cs;
|
|
|
|
cs = (CREATESTRUCT *) lParam;
|
|
temp = cs->lpCreateParams;
|
|
SetWindowLong (thewnd,GWL_USERDATA,(long) temp);
|
|
|
|
break;
|
|
case WM_DESTROY:
|
|
SetWindowLong (thewnd,GWL_USERDATA,0);
|
|
break;
|
|
default:
|
|
CCommandLineClient *fred = (CCommandLineClient *) GetWindowLong (thewnd,GWL_USERDATA);
|
|
if (fred)
|
|
return fred->WndProc (thewnd,themsg,wParam,lParam);
|
|
break;
|
|
}
|
|
|
|
return DefWindowProc (thewnd,themsg,wParam,lParam);
|
|
}
|