Thursday, April 27, 2006

Error Handling:

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.

GISResearch

Saturday, April 22, 2006

Status watching thread in windows services.

I have a windows service program which needs to integrate with another vendor's program. The thing troubling me is that i cannot set up a good test environment with them. The only thing I can do is logging every error my program encounters.

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.
GISResearch

How ATL supports windows service.

If you want to create a service based on the ATL library, you normally will inherit the service from CAtlServiceModuleT which is included in atlbase.h

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(this);
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(this);
#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.
GISResearch

Sunday, April 16, 2006

Eclipse

Jon Skeet's Coding Blog : Visual Studio vs 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).
GISResearch

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.

1> We need to decompose the two polygons into nodes and edge which make up the graph.
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.
GISResearch