Some C++, C#, .Net, windows service, GIS thoughts posted by me. 有度量去容忍那些不能改变的事,有勇气去改变那些可能改变的事,有智慧去区分上述两件事。
Thursday, April 27, 2006
Error Handling:
There are two ways to hanlde the error, one is check the return code of each function, and based on the code returned, determine whether to continue or abort the program.
The other way is to throw an exception , and catch it in the central place, and then determine the appropriate behavior from there.
Thread consideration:
Exceptions should be handled in each sperate thread, if an exception is thrown in a different thread, and not catched in the same thread, it will disapper sliently, and will cause confusion in trouble shooting the issue.
Saturday, April 22, 2006
Status watching thread in windows services.
Since it's running as a windows service, so it won't stop even it finds some errors, and the loop just continues running. I put this service on a client's machine, and it generates 2 GB log data in one day. It's pretty embarassing.
So, what I ends up is to create another status watching thread monitoring the log file it generates, if it finds out the log file size is exceeding the certain size, it will send a stop message to the ServieMain() thread, which will stop the service.
How ATL supports windows service.
template
class ATL_NO_VTABLE CAtlServiceModuleT : public CAtlExeModuleT
{
......
int WinMain(int nShowCmd) throw()
}
When the the service control manager (SCM) is asked to start a service, through the StartService function, it starts the process using the CreateProcess function, it will go into the int WinMain(int nShowCmd) throw() inside the class.
int WinMain(int nShowCmd) throw()
{
if (CAtlBaseModule::m_bInitFailed)
{
ATLASSERT(0);
return -1;
}
T* pT = static_cast
HRESULT hr = S_OK;
LPTSTR lpCmdLine = GetCommandLine();
if (pT->ParseCommandLine(lpCmdLine, &hr) == true)
hr = pT->Start(nShowCmd);
#ifdef _DEBUG
// Prevent false memory leak reporting. ~CAtlWinModule may be too late.
_AtlWinModule.Term();
#endif // _DEBUG
return hr;
}
This function is called by the main thread in the process, no additional thread is created yet. This function will in turn call the Start() function.
Inside the start function, it starts to hook up the real windows service stuff here, by checking the registery,
TCHAR szValue[MAX_PATH];
DWORD dwLen = MAX_PATH;
lRes = key.QueryStringValue(_T("LocalService"), szValue, &dwLen);
It will decide whether this is a service. [ In debug build, this won't be compiled and registered as a service to make life easier to do the debug.] If this is registered as a service, then a service table is created and StartServiceCtrlDispatcher is called to connect this service to the SCM.
SERVICE_TABLE_ENTRY st[] =
{
{ m_szServiceName, _ServiceMain },
{ NULL, NULL }
};
if (::StartServiceCtrlDispatcher(st) == 0)
m_status.dwWin32ExitCode = GetLastError();
StartServiceCtrlDispatcher establishes a connection that the SCM can use to send control commands to the service. StartServiceCtrlDispatcher will not return until the service has indicated that it has stopped. Once the connection to the SCM is established, StartServiceCtrlDispatcher creates a secondary thread that is the real starting point for the service. The second thread in this case is a static function called _ServiceMain, which in turn forwards the call to real ServiceMain function.
[MSDN:
When the service control manager starts a service process, it waits for the process to call the StartServiceCtrlDispatcher function. The main thread of a service process should make this call as soon as possible after it starts up. If StartServiceCtrlDispatcher succeeds, it connects the calling thread to the service control manager and does not return until all running services in the process have terminated. The service control manager uses this connection to send control and service start requests to the main thread of the service process. The main thread acts as a dispatcher by invoking the appropriate HandlerEx function to handle control requests, or by creating a new thread to execute the appropriate ServiceMain function when a new service is started.
The thread ServiceMain run is NOT the main thread referred here, the main thread is the controlling thread.
static void WINAPI _ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv) throw()
{
((T*)_pAtlModule)->ServiceMain(dwArgc, lpszArgv);
}
The real ServiceMain is here:
void ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv) throw()
{
lpszArgv;
dwArgc;
// Register the control request handler
m_status.dwCurrentState = SERVICE_START_PENDING;
m_hServiceStatus = RegisterServiceCtrlHandler(m_szServiceName, _Handler);
if (m_hServiceStatus == NULL)
{
LogEvent(_T("Handler not installed"));
return;
}
SetServiceStatus(SERVICE_START_PENDING);
m_status.dwWin32ExitCode = S_OK;
m_status.dwCheckPoint = 0;
m_status.dwWaitHint = 0;
T* pT = static_cast
#ifndef _ATL_NO_COM_SUPPORT
HRESULT hr = E_FAIL;
hr = T::InitializeCom();
if (FAILED(hr))
{
// Ignore RPC_E_CHANGED_MODE if CLR is loaded. Error is due to CLR initializing
// COM and InitializeCOM trying to initialize COM with different flags.
if (hr != RPC_E_CHANGED_MODE || GetModuleHandle(_T("Mscoree.dll")) == NULL)
{
return;
}
}
else
{
m_bComInitialized = true;
}
m_bDelayShutdown = false;
#endif //_ATL_NO_COM_SUPPORT
// When the Run function returns, the service has stopped.
m_status.dwWin32ExitCode = pT->Run(SW_HIDE);
#ifndef _ATL_NO_COM_SUPPORT
if (m_bService && m_bComInitialized)
T::UninitializeCom();
#endif
SetServiceStatus(SERVICE_STOPPED);
LogEvent(_T("Service stopped"));
}
The first thing in service is to call RegisterServiceCtrlHandler to register a callback function that the control dispatcher, inside StartServiceCtrlDispatcher, can use to pass control requests to the service. RegisterServiceCtrlHandler also returns a handle that is used in calls to the SetServiceStatus function to update the SCM’s status information about the service.
Sunday, April 16, 2006
Eclipse
I started to use eclipse to view some open source geometry alrogrithm in JTS. I like it very much just after a couple of days of using it. Sometimes, I just feel an "open source" product is not any worse than those M$ products.
I am not saying I don't like VS.2005, yes, it's great. It has tons of features, and if know 50% of its feature, I will be very satisfied myself. But if there will be an open source editor which has those features the Eclipse has, a lot of people will probably use it just liking prefer FireFox to IE.
DPack is an wonderful addon for the visual studio 2005. It adds a lot features which Eclipse has and VS 2005 doesn't have.
The best two I like is Solution Browser (Alt + S) and Code Browser (Alt + G).
Thursday, April 06, 2006
Breaking on Exceptions.
By default, the vs debugger will break if an exception is unhandled, but we can configure it to break when the exceptions are thrown. It is very helpful in some situations if we want to know exactly where the exception throws.
Under Debug Menu, select exceptions, you’ll see a tree display of all possible exceptions alongside checkboxes to indicate if the debugger should break when an exception “is thrown”, or only break if the exception is “user-unhandled”.
If you have not determined under what condition the exception occurs, or where the exception occurs, it's better to choose break on exceptions.
Sunday, April 02, 2006
Polygon intersection.
2> Combine the nodes and edges from those two polygons into one big graph, which is the initial graph we need to deal with.
3> Applies the sweep line intersection algorithm to get all the intersection points between the egdes from two polygons. The invariant is that the part of the overlay above the sweep line has been computed correctly.
4> If the event involves only edges from one of the two subdivsions, that is all; The event point is a vertex that can be re-used. If the event involves the edges from both subdivisions, we must link the doubly-connected edge lists of the two original subdivisions at the intersection point.
5> When an edge e passes through another polygon at point v, the edeg e must be replaced by two edges e1 and e2. The two half-edges become four half-edges. We create two new half-edge records whith v as the origin. The new edge e1 is represented by one new (with v as its origin) and one existing half-edge (with e's end point as its origin), and the same holds for e2.
6>
6-a> Link the edges at the end node of original edge e.
The most important part is that we have to link those edges with Prev() and Next() pointers. The Next() pointers of the two new half-edges each copy the Next() pointers of the old half-edge that is not its twin. The half-edges to which these pointers point must also update their Prev() pointer and set it to the new half edges().
6-b> Link the edge at the point v.
Consider the half edge for e1 that has v as its destiantion, it must be linked to the first half-edge, seen clockwise from e1, with v as its origin. The half edge for e1 with v as its origin must be linked to the first counterclockwise half-edge with v as its destination.