// parse and send msg
            // will raise an exception on any failure
            private void _sendMsg(QueueMsg msg)
            {
                string data = _toJSON(msg.data);

                _logDbgMsg(data);

                try {
                    _sendServer(msg.uri, data);
                } catch (System.Net.WebException e) {
                    switch (e.Status)
                    {
                    // retrieve syntax error message from the server
                    case System.Net.WebExceptionStatus.ProtocolError:
                        if (msg.IsCritical())
                        {
                            System.Net.WebResponse resp = e.Response;
                            Stream       strm           = resp.GetResponseStream();
                            StreamReader sr             = new StreamReader(strm, System.Text.Encoding.UTF8);
                            string       serverMsg      = sr.ReadToEnd();
                            strm.Close();
                            throw new CriticalMessageIllFormedException(serverMsg);
                        }
                        break;

                    case System.Net.WebExceptionStatus.ConnectFailure:
                        throw new ServerDownException(e.Message);

                    // unhandled
                    default:
                        _logException("Unexpected status", e);
                        throw;
                    }
                }
            }
            private void _gamesenseWrk()
            {
                QueueMsg pendingMsg = null;

                System.Diagnostics.Stopwatch tLastMsg = new System.Diagnostics.Stopwatch();
                tLastMsg.Start();

                while (_mGameSenseWrkShouldRun)
                {
                    switch (_mClientState)
                    {
                    case ClientState.Active:

                        // see if there is any message to process
                        QueueMsg msg;
                        while ((msg = _mMsgQueue.CDequeue()) == null)
                        {
                            // no messages in queue, sleep a bit before checking again
                            System.Threading.Thread.Sleep(_MsgCheckInterval);

                            // if a heartbeat event is due, send it now
                            if (tLastMsg.ElapsedMilliseconds > _MaxIdleTimeBeforeHeartbeat)
                            {
                                msg      = new QueueMsgSendHeartbeat();
                                msg.data = new Game(GameName);

                                break;
                            }
                        }

                        try {
                            // attempt to send a message
                            _sendMsg(msg);

                            // message sent successfully, reset timer
                            tLastMsg.Reset();
                            tLastMsg.Start();
                        } catch (ServerDownException e) {
                            // GameSense server down, save the message and go to probing state
                            _logException("Failed connecting to GameSense server", e);
                            pendingMsg = msg;
                            _setClientState(ClientState.Probing);
                        } catch (CriticalMessageIllFormedException e) {
                            // RegisterGame or RegisterEvent or BindEvent messages are not well formed
                            // log and disable
                            _logException("Message ill-formed", e);
                            _setClientState(ClientState.Inactive);
                        } catch (System.Exception e) {
                            _logException("Failed processing msg", e);
                        }

                        break;

                    case ClientState.Probing:

                        // parse props to get GameSense server port
                        _mServerPort = _getServerPort();
                        if (_mServerPort == null)
                        {
                            // SSE3 not installed or coreprops garbled
                            // this failure is beyond anything we can do, GameSense will remain disabled
                            _logWarning("Failed to obtain GameSense server port. GameSense will not function");
                            _setClientState(ClientState.Inactive);
                            break;
                        }

                        // port information successfully parsed, init uris
                        _initializeUris();

                        if (pendingMsg != null)
                        {
                            // attempt to send a pending message
                            try {
                                _sendMsg(pendingMsg);
                                pendingMsg = null;
                            } catch (ServerDownException e) {
                                // GameSense server down, wait a bit and retry
                                _logException("Failed connecting to GameSense server", e);
                                _logDbgMsg("Retrying in 5 seconds...");
                                System.Threading.Thread.Sleep(_ServerProbeInterval);
                                break;
                            }
                        }

                        // we can attempt to send data, go to active state
                        _setClientState(ClientState.Active);
                        break;

                    case ClientState.Inactive:
                        // quit
                        _logDbgMsg("Entering inactive state");
                        _mGameSenseWrkShouldRun = false;
                        break;

                    default:
                        // unknown state, must abort
                        _logErrorMsg("Unknown GameSense client state");
                        _setClientState(ClientState.Inactive);
                        break;
                    }
                }

                _logDbgMsg("Worker exiting");
            }