Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
clean-and-itasks
clean-ide
Commits
4205e6ce
Commit
4205e6ce
authored
Oct 15, 2001
by
Diederik van Arkel
Browse files
Add missing sources
parent
7e5709f0
Changes
5
Hide whitespace changes
Inline
Side-by-side
Win/Clean System Files/util_io_shell_lib
0 → 100644
View file @
4205e6ce
shell32.dll
ShellExecuteA@24
Win/PatchConsoleEvents/Redirect.c
0 → 100644
View file @
4205e6ce
// Redirect.c : implementation file
//
#define CcWmCONSOLEQUIT 141
#define CcWmCONSOLEOUT 142
#define CcWmCONSOLEERR 143
#define WM_CONSOLEQUIT 0x0407
#define WM_CONSOLEOUT 0x0408
#define WM_CONSOLEERR 0x0409
/********************************************************************************************
Include section.
********************************************************************************************/
#include "cCrossCall_121.h"
#include "cAcceleratorTable_121.h" // Contains the implementation of accelerator tables.
#include "cCrossCallCursor_121.h" // Contains the implementation of cursors.
#include <commctrl.h>
void
VERIFY
(
BOOL
bl
)
{
return
;
}
void
ASSERT
(
BOOL
bl
)
{
return
;
}
//extern HANDLE ghMainWindow;
void
OnChildStdOutWrite
(
char
*
buffer
)
{
char
*
commandstring
;
int
len
;
// SendMessage ((HWND) ghMainWindow, WM_CONSOLEOUT, (WPARAM)cleanstring(buffer), (LPARAM)NULL);
len
=
lstrlen
(
buffer
)
+
1
;
commandstring
=
rmalloc
(
len
);
/* this pointer is passed to and freed in the Clean code. */
lstrcpyn
(
commandstring
,
buffer
,
len
);
PostMessage
((
HWND
)
ghMainWindow
,
WM_CONSOLEOUT
,
(
WPARAM
)
commandstring
,
(
LPARAM
)
NULL
);
// PostMessage(ghMainWindow,0x0
}
void
OnChildStdErrWrite
(
char
*
buffer
)
{
// SendMessage ((HWND) ghMainWindow, WM_CONSOLEERR, (WPARAM)cleanstring(buffer), (LPARAM)NULL);
char
*
commandstring
;
int
len
;
len
=
lstrlen
(
buffer
)
+
1
;
commandstring
=
rmalloc
(
len
);
/* this pointer is passed to and freed in the Clean code. */
lstrcpyn
(
commandstring
,
buffer
,
len
);
PostMessage
((
HWND
)
ghMainWindow
,
WM_CONSOLEERR
,
(
WPARAM
)
commandstring
,
(
LPARAM
)
NULL
);
}
void
OnChildStarted
()
{}
void
OnChildTerminate
()
{
// SendMessage ((HWND) ghMainWindow, WM_CONSOLEQUIT, (WPARAM)NULL, (LPARAM)NULL);
PostMessage
((
HWND
)
ghMainWindow
,
WM_CONSOLEQUIT
,
(
WPARAM
)
NULL
,
(
LPARAM
)
NULL
);
}
#define BUFFER_SIZE 256
static
int
StdOutThread
(
HANDLE
);
static
int
StdErrThread
(
HANDLE
);
static
int
ProcessThread
();
HANDLE
PrepAndLaunchRedirectedChild
(
LPCSTR
lpszCmdLine
,
HANDLE
hStdOut
,
HANDLE
hStdIn
,
HANDLE
hStdErr
,
BOOL
bShowChildWindow
);
/*
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
*/
// This class is build from the following MSDN article.
// Q190351 HOWTO: Spawn Console Processes with Redirected Standard Handles.
HANDLE
m_hExitEvent
;
// Child input(stdin) & output(stdout, stderr) pipes
HANDLE
m_hStdIn
,
m_hStdOut
,
m_hStdErr
;
// Parent output(stdin) & input(stdout) pipe
HANDLE
m_hStdInWrite
,
m_hStdOutRead
,
m_hStdErrRead
;
// stdout, stderr write threads
HANDLE
m_hStdOutThread
,
m_hStdErrThread
;
// Monitoring thread
HANDLE
m_hProcessThread
;
// Child process handle
HANDLE
m_hChildProcess
;
static
BOOL
m_bRunThread
;
/////////////////////////////////////////////////////////////////////////////
// API Function
BOOL
WINAPI
IsWinNT
()
{
// get windows version
DWORD
WindowsVersion
=
GetVersion
();
DWORD
WindowsMajorVersion
=
(
DWORD
)(
LOBYTE
(
LOWORD
(
WindowsVersion
)));
DWORD
WindowsMinorVersion
=
(
DWORD
)(
HIBYTE
(
LOWORD
(
WindowsVersion
)));
// Running on WIN9x ?
if
(
WindowsVersion
>=
0x80000000
)
return
FALSE
;
// Running on NT
return
TRUE
;
}
// Create standard handles, try to start child from command line.
BOOL
StartChildProcess
(
LPCSTR
lpszCmdLine
,
BOOL
bShowChildWindow
)
{
HANDLE
hProcess
;
SECURITY_ATTRIBUTES
sa
;
HANDLE
hStdInWriteTmp
,
hStdOutReadTmp
,
hStdErrReadTmp
;
DWORD
dwThreadID
;
// Initialisation.
m_hStdIn
=
NULL
;
m_hStdOut
=
NULL
;
m_hStdErr
=
NULL
;
m_hStdInWrite
=
NULL
;
m_hStdOutRead
=
NULL
;
m_hStdErrRead
=
NULL
;
m_hChildProcess
=
NULL
;
m_hStdOutThread
=
NULL
;
m_hStdErrThread
=
NULL
;
m_hProcessThread
=
NULL
;
m_hExitEvent
=
NULL
;
m_bRunThread
=
FALSE
;
hProcess
=
GetCurrentProcess
();
// Set up the security attributes struct.
ZeroMemory
(
&
sa
,
sizeof
(
SECURITY_ATTRIBUTES
));
sa
.
nLength
=
sizeof
(
SECURITY_ATTRIBUTES
);
sa
.
lpSecurityDescriptor
=
NULL
;
sa
.
bInheritHandle
=
TRUE
;
// Create the child stdin pipe.
VERIFY
(
CreatePipe
(
&
m_hStdIn
,
&
hStdInWriteTmp
,
&
sa
,
0
));
// Create the child stdout pipe.
VERIFY
(
CreatePipe
(
&
hStdOutReadTmp
,
&
m_hStdOut
,
&
sa
,
0
));
// Create the child stderr pipe.
VERIFY
(
CreatePipe
(
&
hStdErrReadTmp
,
&
m_hStdErr
,
&
sa
,
0
));
// Create new stdin write, stdout and stderr read handles.
// Set the properties to FALSE. Otherwise, the child inherits the
// properties and, as a result, non-closeable handles to the pipes
// are created.
VERIFY
(
DuplicateHandle
(
hProcess
,
hStdInWriteTmp
,
hProcess
,
&
m_hStdInWrite
,
0
,
FALSE
,
DUPLICATE_SAME_ACCESS
));
VERIFY
(
DuplicateHandle
(
hProcess
,
hStdOutReadTmp
,
hProcess
,
&
m_hStdOutRead
,
0
,
FALSE
,
DUPLICATE_SAME_ACCESS
));
VERIFY
(
DuplicateHandle
(
hProcess
,
hStdErrReadTmp
,
hProcess
,
&
m_hStdErrRead
,
0
,
FALSE
,
DUPLICATE_SAME_ACCESS
));
// Close inheritable copies of the handles you do not want to be
// inherited.
VERIFY
(
CloseHandle
(
hStdInWriteTmp
));
VERIFY
(
CloseHandle
(
hStdOutReadTmp
));
VERIFY
(
CloseHandle
(
hStdErrReadTmp
));
// Start child process with redirected stdout, stdin & stderr
m_hChildProcess
=
PrepAndLaunchRedirectedChild
(
lpszCmdLine
,
m_hStdOut
,
m_hStdIn
,
m_hStdErr
,
bShowChildWindow
);
if
(
m_hChildProcess
==
NULL
)
{
TCHAR
lpszBuffer
[
BUFFER_SIZE
];
// sprintf(lpszBuffer, "Unable to start %s\n", lpszCmdLine);
// replace by something that works..
OnChildStdOutWrite
(
lpszBuffer
);
// close all handles and return FALSE
VERIFY
(
CloseHandle
(
m_hStdIn
));
m_hStdIn
=
NULL
;
VERIFY
(
CloseHandle
(
m_hStdOut
));
m_hStdOut
=
NULL
;
VERIFY
(
CloseHandle
(
m_hStdErr
));
m_hStdErr
=
NULL
;
return
FALSE
;
}
m_bRunThread
=
TRUE
;
// Create Exit event
m_hExitEvent
=
CreateEvent
(
NULL
,
TRUE
,
FALSE
,
NULL
);
VERIFY
(
m_hExitEvent
!=
NULL
);
// Launch the thread that read the child stdout.
m_hStdOutThread
=
CreateThread
(
NULL
,
0
,
(
LPTHREAD_START_ROUTINE
)
StdOutThread
,
(
LPVOID
)
m_hStdOutRead
,
0
,
&
dwThreadID
);
VERIFY
(
m_hStdOutThread
!=
NULL
);
// Launch the thread that read the child stderr.
m_hStdErrThread
=
CreateThread
(
NULL
,
0
,
(
LPTHREAD_START_ROUTINE
)
StdErrThread
,
(
LPVOID
)
m_hStdErrRead
,
0
,
&
dwThreadID
);
VERIFY
(
m_hStdErrThread
!=
NULL
);
// Launch the thread that monitoring the child process.
m_hProcessThread
=
CreateThread
(
NULL
,
0
,
(
LPTHREAD_START_ROUTINE
)
ProcessThread
,
(
LPVOID
)
0
,
0
,
&
dwThreadID
);
VERIFY
(
m_hProcessThread
!=
NULL
);
// Virtual function to notify derived class that the child is started.
OnChildStarted
(
lpszCmdLine
);
return
TRUE
;
}
// Check if the child process is running.
// On NT/2000 the handle must have PROCESS_QUERY_INFORMATION access.
BOOL
IsChildRunning
()
{
DWORD
dwExitCode
;
if
(
m_hChildProcess
==
NULL
)
return
FALSE
;
GetExitCodeProcess
(
m_hChildProcess
,
&
dwExitCode
);
return
(
dwExitCode
==
STILL_ACTIVE
)
?
TRUE
:
FALSE
;
}
void
TerminateChildProcess
()
{
// Tell the threads to exit and wait for process thread to die.
m_bRunThread
=
FALSE
;
SetEvent
(
m_hExitEvent
);
Sleep
(
500
);
// Check the process thread.
if
(
m_hProcessThread
!=
NULL
)
{
VERIFY
(
WaitForSingleObject
(
m_hProcessThread
,
1000
)
!=
WAIT_TIMEOUT
);
m_hProcessThread
=
NULL
;
}
// Close all child handles first.
if
(
m_hStdIn
!=
NULL
)
VERIFY
(
CloseHandle
(
m_hStdIn
));
m_hStdIn
=
NULL
;
if
(
m_hStdOut
!=
NULL
)
VERIFY
(
CloseHandle
(
m_hStdOut
));
m_hStdOut
=
NULL
;
if
(
m_hStdErr
!=
NULL
)
VERIFY
(
CloseHandle
(
m_hStdErr
));
m_hStdErr
=
NULL
;
Sleep
(
100
);
// Close all parent handles.
if
(
m_hStdInWrite
!=
NULL
)
VERIFY
(
CloseHandle
(
m_hStdInWrite
));
m_hStdInWrite
=
NULL
;
if
(
m_hStdOutRead
!=
NULL
)
VERIFY
(
CloseHandle
(
m_hStdOutRead
));
m_hStdOutRead
=
NULL
;
if
(
m_hStdErrRead
!=
NULL
)
VERIFY
(
CloseHandle
(
m_hStdErrRead
));
m_hStdErrRead
=
NULL
;
Sleep
(
100
);
// Stop the stdout read thread.
if
(
m_hStdOutThread
!=
NULL
)
{
if
(
!
IsWinNT
())
TerminateThread
(
m_hStdOutThread
,
1
);
VERIFY
(
WaitForSingleObject
(
m_hStdOutThread
,
1000
)
!=
WAIT_TIMEOUT
);
m_hStdOutThread
=
NULL
;
}
// Stop the stderr read thread.
if
(
m_hStdErrThread
!=
NULL
)
{
if
(
!
IsWinNT
())
TerminateThread
(
m_hStdErrThread
,
1
);
VERIFY
(
WaitForSingleObject
(
m_hStdErrThread
,
1000
)
!=
WAIT_TIMEOUT
);
m_hStdErrThread
=
NULL
;
}
Sleep
(
100
);
// Stop the child process if not already stopped.
// It's not the best solution, but it is a solution.
// On Win98 it may crash the system if the child process is the COMMAND.COM.
// The best way is to terminate the COMMAND.COM process with an "exit" command.
if
(
IsChildRunning
())
{
VERIFY
(
TerminateProcess
(
m_hChildProcess
,
1
));
VERIFY
(
WaitForSingleObject
(
m_hChildProcess
,
1000
)
!=
WAIT_TIMEOUT
);
}
m_hChildProcess
=
NULL
;
// cleanup the exit event
if
(
m_hExitEvent
!=
NULL
)
VERIFY
(
CloseHandle
(
m_hExitEvent
));
m_hExitEvent
=
NULL
;
}
// Launch the process that you want to redirect.
HANDLE
PrepAndLaunchRedirectedChild
(
LPCSTR
lpszCmdLine
,
HANDLE
hStdOut
,
HANDLE
hStdIn
,
HANDLE
hStdErr
,
BOOL
bShowChildWindow
)
{
BOOL
bResult
;
HANDLE
hProcess
=
GetCurrentProcess
();
PROCESS_INFORMATION
pi
;
LPVOID
lpSD
;
LPSECURITY_ATTRIBUTES
lpSA
;
char
path
[
_MAX_PATH
];
char
*
thepath
;
int
i
;
// Set up the start up info struct.
STARTUPINFO
si
;
ZeroMemory
(
&
si
,
sizeof
(
STARTUPINFO
));
si
.
cb
=
sizeof
(
STARTUPINFO
);
si
.
dwFlags
=
STARTF_USESTDHANDLES
|
STARTF_USESHOWWINDOW
;
si
.
hStdOutput
=
hStdOut
;
si
.
hStdInput
=
hStdIn
;
si
.
hStdError
=
hStdErr
;
// Use this if you want to show the child.
si
.
wShowWindow
=
bShowChildWindow
?
SW_SHOW
:
SW_HIDE
;
// Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
// use the wShowWindow flags.
// Create the NULL security token for the process
lpSD
=
NULL
;
lpSA
=
NULL
;
// On NT/2000 the handle must have PROCESS_QUERY_INFORMATION access.
// This is made using an empty security descriptor. It is not the same
// as using a NULL pointer for the security attribute!
if
(
IsWinNT
())
{
/* lpSD = GlobalAlloc(GPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
VERIFY(InitializeSecurityDescriptor(lpSD, SECURITY_DESCRIPTOR_REVISION));
VERIFY(SetSecurityDescriptorDacl(lpSD, -1, 0, 0));
lpSA = (LPSECURITY_ATTRIBUTES)GlobalAlloc(GPTR, sizeof(SECURITY_ATTRIBUTES));
lpSA->nLength = sizeof(SECURITY_ATTRIBUTES);
lpSA->lpSecurityDescriptor = lpSD;
lpSA->bInheritHandle = TRUE;
*/
}
// try to get startupdir info in order...
lstrcpy
(
path
,
lpszCmdLine
);
for
(
i
=
lstrlen
(
path
);
path
[
i
]
!=
'\\'
&&
i
>=
0
;
i
--
)
path
[
i
]
=
0
;
if
(
i
==
0
)
thepath
=
NULL
;
else
{
/* path[i] = '\"'; */
thepath
=
path
+
1
;
}
// thepath = NULL;
// Try to spawn the process.
bResult
=
CreateProcess
(
NULL
/* pointer to name of executable module */
,
(
char
*
)
lpszCmdLine
/* pointer to command line string */
,
lpSA
/* pointer to process security attributes */
,
NULL
/* pointer to thread security attributes */
,
TRUE
/* handle inheritance flag */
,
CREATE_NEW_CONSOLE
/* creation flags */
,
NULL
/* pointer to new environment block */
,
thepath
/* pointer to current directory name */
,
&
si
/* pointer to STARTUPINFO */
,
&
pi
/* pointer to PROCESS_INFORMATION */
);
// Cleanup memory allocation
if
(
lpSA
!=
NULL
)
GlobalFree
(
lpSA
);
if
(
lpSD
!=
NULL
)
GlobalFree
(
lpSD
);
// Return if an error occurs.
if
(
!
bResult
)
return
FALSE
;
// Close any unnecessary handles.
VERIFY
(
CloseHandle
(
pi
.
hThread
));
// Save global child process handle to cause threads to exit.
return
pi
.
hProcess
;
}
BOOL
m_bRunThread
=
TRUE
;
// Thread to read the child stdout.
static
int
StdOutThread
(
HANDLE
hStdOutRead
)
{
DWORD
nBytesRead
;
CHAR
lpszBuffer
[
BUFFER_SIZE
+
1
];
while
(
m_bRunThread
)
{
Sleep
(
50
);
if
(
!
ReadFile
(
hStdOutRead
,
lpszBuffer
,
BUFFER_SIZE
,
&
nBytesRead
,
NULL
)
||
!
nBytesRead
)
{
if
(
GetLastError
()
==
ERROR_BROKEN_PIPE
)
break
;
// pipe done - normal exit path.
else
break
;
//ASSERT(FALSE); // Something bad happened.
}
if
(
nBytesRead
)
{
// Virtual function to notify derived class that
// characters are writted to stdout.
lpszBuffer
[
nBytesRead
]
=
'\0'
;
OnChildStdOutWrite
(
lpszBuffer
);
}
}
return
0
;
}
// Thread to read the child stderr.
static
int
StdErrThread
(
HANDLE
hStdErrRead
)
{
DWORD
nBytesRead
;
CHAR
lpszBuffer
[
BUFFER_SIZE
+
1
];
while
(
m_bRunThread
)
{
if
(
!
ReadFile
(
hStdErrRead
,
lpszBuffer
,
BUFFER_SIZE
,
&
nBytesRead
,
NULL
)
||
!
nBytesRead
)
{
if
(
GetLastError
()
==
ERROR_BROKEN_PIPE
)
break
;
// pipe done - normal exit path.
else
break
;
//ASSERT(FALSE); // Something bad happened.
}
if
(
nBytesRead
)
{
// Virtual function to notify derived class that
// characters are writted to stderr.
lpszBuffer
[
nBytesRead
]
=
'\0'
;
OnChildStdErrWrite
(
lpszBuffer
);
}
}
return
0
;
}
// Thread to monitoring the child process.
static
int
ProcessThread
()
{
HANDLE
hWaitHandle
[
2
];
hWaitHandle
[
0
]
=
m_hExitEvent
;
hWaitHandle
[
1
]
=
m_hChildProcess
;
while
(
m_bRunThread
)
{
switch
(
WaitForMultipleObjects
(
2
,
hWaitHandle
,
FALSE
,
1
))
{
case
WAIT_OBJECT_0
+
0
:
// exit on event
ASSERT
(
m_bRunThread
==
FALSE
);
m_bRunThread
=
FALSE
;
break
;
case
WAIT_OBJECT_0
+
1
:
// child process exit
ASSERT
(
m_bRunThread
==
TRUE
);
m_bRunThread
=
FALSE
;
break
;
}
}
// Virtual function to notify derived class that
// child process is terminated.
// Application must call TerminateChildProcess()
// but not direcly from this thread!
OnChildTerminate
();
return
0
;
}
// Function that write to the child stdin.
int
WriteChildStdIn
(
LPCSTR
lpszInput
)
{
DWORD
nBytesWrote
;
DWORD
Length
=
strlen
(
lpszInput
);
if
(
m_hStdInWrite
!=
NULL
&&
Length
>
0
)
{
if
(
!
WriteFile
(
m_hStdInWrite
,
lpszInput
,
Length
,
&
nBytesWrote
,
NULL
))
{
if
(
GetLastError
()
==
ERROR_NO_DATA
)
return
1
;
// Pipe was closed (do nothing).
else
//ASSERT(FALSE); // Something bad happened.
return
2
;
}
}
return
0
;
}
//==========
WNDPROC
g_pWndProc
=
NULL
;
LRESULT
CALLBACK
SubClassWndProc
(
HWND
p_hWnd
,
UINT
p_uMsg
,
WPARAM
p_wParam
,
LPARAM
p_lParam
)
{
switch
(
p_uMsg
)
{
case
WM_CONSOLEQUIT
:
{
SendMessage0ToClean
(
CcWmCONSOLEQUIT
);
return
0
;
}
break
;
case
WM_CONSOLEOUT
:
{
SendMessage1ToClean
(
CcWmCONSOLEOUT
,
p_wParam
);
return
0
;
}
break
;
case
WM_CONSOLEERR
:
{
SendMessage1ToClean
(
CcWmCONSOLEERR
,
p_wParam
);
return
0
;
}
break
;
}
return
CallWindowProc
(
g_pWndProc
,
p_hWnd
,
p_uMsg
,
p_wParam
,
p_lParam
);
}
BOOL
AddMainWindowHook
(
BOOL
dummy
)
{
if
(
g_pWndProc
==
NULL
)
{
g_pWndProc
=
(
WNDPROC
)
SetWindowLong
(
ghMainWindow
,
GWL_WNDPROC
,(
LPARAM
)
SubClassWndProc
);
return
TRUE
;
}
return
FALSE
;
}
Win/PatchConsoleEvents/cCrossCallMaarten.c
0 → 100644
View file @
4205e6ce
#include "util_121.h"
#include <Windows.h>
#include <Windowsx.h>
#include "cCCallWindows_121.h"
#include "cCCallSystem_121.h"
#include "cCrossCallWindows_121.h"
#include "cCrossCall_121.h"
#include "cAcceleratorTable_121.h"
#include "cCrossCallxDI_121.h"
#define CcRqSHELLDEFAULT 1476
/* shell execute interface */
#define CcRqALTDIRECTORYDIALOG 1475
/* alternative directory selector */
#define CcRqSETWINDOWICON 1474
/* Set icon associated with window */
#define CcRqADDWINDOWHOOK 1473
/* */
#define CcRqSETWINDOWFRAME 1472
/* */
#define CcRqMDMDELCONTROLTIP 1471
/* remove controls from tooltip areas. */
#define CcRqMDMADDCONTROLTIP 1470
/* add controls to tooltip areas. */
static
UINT
rectTTId
;
//static HWND ghwndRT;
WNDPROC
g_pTool0WndProc
;
WNDPROC
g_pTool1WndProc
;
POINT
FAR
gFarPoint
;
static
int
left
,
top
;
void
InitialiseCrossCallMaarten
();
/* Add controls to tooltip area. */
void
EvalCcRqMDMADDCONTROLTIP
(
CrossCallInfo
*
pcci
)
/* parentPtr, controlPtr, textPtr; no result. */
{
HWND
hwndParent
;
/* The handle to the window. */
TOOLINFO
ti
;
/* The tool information that is sent to the tooltip control. */
char
*
text
;
int
left
,
top
,
right
,
bottom
;
rectTTId
++
;
hwndParent
=
(
HWND
)
pcci
->
p1
;
left
=
(
INT
)
pcci
->
p2
;
top
=
(
INT
)
pcci
->
p3
;
right
=
(
INT
)
pcci
->
p4
;
bottom
=
(
INT
)
pcci
->
p5
;
text
=
(
char
*
)
pcci
->
p6
;
/* Fill the tooltip info with the appropriate information. */
ti
.
cbSize
=
sizeof
(
TOOLINFO
);
ti
.
uFlags
=
TTF_SUBCLASS
;
ti
.
hwnd
=
hwndParent
;
ti
.
uId
=
(
UINT
)
rectTTId
;
ti
.
rect
.
left
=
left
;
ti
.
rect
.
top
=
top
;
ti
.
rect
.
right
=
right
;
ti
.
rect
.
bottom
=
bottom
;
ti
.
hinst
=
ghInst
;
ti
.
lpszText
=
(
LPSTR
)
text
;
SendMessage
(
ghwndTT
,
TTM_ADDTOOL
,
0
,
(
LPARAM
)
(
LPTOOLINFO
)
&
ti
);
MakeReturn1Cci
(
pcci
,
rectTTId
);
}
/* Remove controls from tooltip area. */