UINT32 HlprFwpmProviderContextDeleteByKey(_In_ const HANDLE engineHandle,
                                          _In_ const GUID* pProviderContextKey)
{
   ASSERT(engineHandle);
   ASSERT(pProviderContextKey);

   UINT32 status = NO_ERROR;

   status = FwpmProviderContextDeleteByKey(engineHandle,
                                           pProviderContextKey);
   if(status != NO_ERROR)
   {
      if(status != FWP_E_IN_USE &&
         status != FWP_E_BUILTIN_OBJECT &&
         status != FWP_E_PROVIDER_CONTEXT_NOT_FOUND)
         HlprLogError(L"HlprFwpmProviderContextDeleteByKey : FwpmProviderContextDeleteByKey() [status: %#x]",
                      status);
      else
      {
         HlprLogInfo(L"HlprFwpmProviderContextDeleteByKey : FwpmProviderContextDeleteByKey() [status: %#x]",
                     status);

         status = NO_ERROR;
      }
   }

   return status;
}
UINT32 HlprFwpmProviderContextAdd(_In_ const HANDLE engineHandle,
                                  _Inout_ FWPM_PROVIDER_CONTEXT* pProviderContext)
{
   ASSERT(engineHandle);
   ASSERT(pProviderContext);

   UINT32 status = NO_ERROR;

   status = FwpmProviderContextAdd(engineHandle,
                                   pProviderContext,
                                   0,
                                   &(pProviderContext->providerContextId));
   if(status != NO_ERROR)
   {
      if(status == FWP_E_ALREADY_EXISTS)
      {
         HlprLogInfo(L"ProviderContext Already Exists");

         status = NO_ERROR;
      }
      else
         HlprLogError(L"HlprFwpmProviderContextAdd : FwpmProviderContextAdd() [status: %#x]",
                      status);
   }

   return status;
}
UINT32 HlprThreadWaitForEvent(_In_ HANDLE eventHandle,
                              _In_ THREAD_DATA* pThreadData)
{
    UINT32       status         = NO_ERROR;
    const UINT32 RETRY_ATTEMPTS = 6;
    WCHAR*       pEventName     = 0;

    if(eventHandle &&
            pThreadData)
    {
        if(eventHandle == pThreadData->threadContinueEvent)
            pEventName = L"Continue Event";
        else if(eventHandle == pThreadData->threadStartEvent)
            pEventName = L"Start Event";
        else if(eventHandle == pThreadData->threadStopEvent)
            pEventName = L"Stop Event";
        else if(eventHandle == pThreadData->thread)
            pEventName = L"Thread";

        /// Try for 30 seconds before bailing
        for(UINT32 i = 0;
                WaitForSingleObject(eventHandle,
                                    5000) == WAIT_TIMEOUT;
                i++)
        {
            HlprLogInfo(L"HlprThreadWaitForEvent() Waiting for %s ...",
                        pEventName);

            if(i == RETRY_ATTEMPTS - 1)
            {
                status = WAIT_TIMEOUT;

                HlprLogInfo(L"HlprThreadWaitForEvent() [status: %#x]");

                break;
            }
        }
    }

    return status;
}
/**
 @private_function="PrvRPCTroubleshootError"
 
   Purpose:  Function to retrieve extended error information about RPC's inner workings.        <br>
                                                                                                <br>
   Notes:                                                                                       <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA378651.aspx              <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA374351.aspx              <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA375686.aspx              <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA375668.aspx              <br>
             HTTP://MSDN.Microsoft.com/En-US/Library/Windows/Desktop/AA375664.aspx              <br>
*/
VOID PrvRPCTroubleshootError()
{
   RPC_STATUS            status     = RPC_S_OK;
   RPC_ERROR_ENUM_HANDLE enumHandle = {0};

   status = RpcErrorStartEnumeration(&enumHandle);
   if(status != RPC_S_OK)
   {
      if(status != RPC_S_ENTRY_NOT_FOUND)
         HlprLogError(L"PrvRPCTroubleshootError : RpcErrorStartEnumeration() [status: %#x]",
                      status);

      HLPR_BAIL;
   }

   for(;
       status == RPC_S_OK;
       )
   {
      RPC_EXTENDED_ERROR_INFO errorInfo = {0};

      errorInfo.Version            = RPC_EEINFO_VERSION;
      errorInfo.NumberOfParameters = MaxNumberOfEEInfoParams;

      status = RpcErrorGetNextRecord(&enumHandle,
                                     TRUE,
                                     &errorInfo);
      if(status == RPC_S_ENTRY_NOT_FOUND)
      {
         HlprLogInfo(L"PrvRPCTroubleshootError : RpcErrorGetNextRecord() [status: %#x]",
                     status);

         HLPR_BAIL;
      }
      else if(status != RPC_S_OK)
      {
         HlprLogError(L"PrvRPCTroubleshootError : RpcErrorGetNextRecord() [status: %#x]",
                      status);

         HLPR_BAIL;
      }
      else
      {
         if(errorInfo.ComputerName)
         {
            HlprLogInfo(L"   [ComputerName: %S]",
                        errorInfo.ComputerName);

            HeapFree(GetProcessHeap(),
                     0,
                     errorInfo.ComputerName);
         }

         HlprLogInfo(L"   [ProcessID: %d]",
                     errorInfo.ProcessID);

         HlprLogInfo(L"   [SystemTime: %02d/%02d/%04d %02d:%02d:%02d:%03d]",
                     errorInfo.u.SystemTime.wMonth,
                     errorInfo.u.SystemTime.wDay,
                     errorInfo.u.SystemTime.wYear,
                     errorInfo.u.SystemTime.wHour,
                     errorInfo.u.SystemTime.wMinute,
                     errorInfo.u.SystemTime.wSecond,
                     errorInfo.u.SystemTime.wMilliseconds);

         HlprLogInfo(L"   [GeneratingComponent:%d]",
                     errorInfo.GeneratingComponent);

         HlprLogInfo(L"   [Status: %#x]",
                     errorInfo.Status);

         HlprLogInfo(L"   [DetectionLocation: %d]",
                     errorInfo.DetectionLocation);

         HlprLogInfo(L"   [Flags: %#x]",
                     errorInfo.Flags);

         HlprLogInfo(L"   [NumberOfParameters: %d]", 
                     errorInfo.NumberOfParameters);

         for(UINT32 i = 0;
             i < (UINT32)errorInfo.NumberOfParameters;
             i++)
         {
            PWSTR pNewLine = L"";

            if(i == ((UINT32)errorInfo.NumberOfParameters - 1))
               pNewLine = L"\n";

            switch(errorInfo.Parameters[i].ParameterType)
            {
               case eeptAnsiString:
               {
                  HlprLogInfo(L"   [AnsiString: %s]%s",
                              errorInfo.Parameters[i].u.AnsiString,
                              pNewLine);

                  HeapFree(GetProcessHeap(),
                           0, 
                           errorInfo.Parameters[i].u.AnsiString);

                  break;
               }
               case eeptUnicodeString:
               {
                  HlprLogInfo(L"   [UnicodeString: %S]%s",
                              errorInfo.Parameters[i].u.UnicodeString,
                              pNewLine);

                  HeapFree(GetProcessHeap(),
                           0, 
                           errorInfo.Parameters[i].u.UnicodeString);

                  break;
               }
               case eeptLongVal:
               {
                  HlprLogInfo(L"   [LongVal: %#x]%s",
                              errorInfo.Parameters[i].u.LVal,
                              pNewLine);

                  break;
               }
               case eeptShortVal:
               {
                  HlprLogInfo(L"   [ShortVal: %#x]%s",
                              errorInfo.Parameters[i].u.SVal,
                              pNewLine);

                  break;
               }
               case eeptPointerVal:
               {
                  HlprLogInfo(L"   [PointerVal: %#p]%s",
                              errorInfo.Parameters[i].u.PVal,
                              pNewLine);

                  break;
               }
               case eeptNone:
               {
                  HlprLogInfo(L"   [Truncated]%s",
                              pNewLine);

                  break;
               }
               default:
                  HlprLogInfo(L"   [ParameterType Invalid: %d]%s", 
                              errorInfo.Parameters[i].ParameterType,
                              pNewLine);
            }
         }
      }
   }

   HLPR_BAIL_LABEL:

   RpcErrorEndEnumeration(&enumHandle);

   return;
}
/**
 @helper_function="HlprThreadCleanup"

   Purpose:  Cleans up a previously allocated thread.                                           <br>
                                                                                                <br>
   Notes:                                                                                       <br>
                                                                                                <br>
   MSDN_Ref: HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Desktop/MS683190.aspx              <br>
             HTTP://MSDN.Microsoft.com/En-Us/Library/Windows/Desktop/MS686717.aspx              <br>
*/
VOID HlprThreadCleanup(_Inout_opt_ THREAD_DATA* pThreadData)
{
    if(pThreadData)
    {
        LPTHREAD_START_ROUTINE threadStartRoutine = pThreadData->threadStartRoutine;
        UINT32                 status             = NO_ERROR;

        if(pThreadData->thread)
        {
            if(GetExitCodeThread(pThreadData->thread,
                                 (DWORD*)&status))
            {
                if(status == STILL_ACTIVE)
                {
                    UINT64       timeAlpha    = GetTickCount64();
                    const UINT32 FOUR_MINUTES = 240000;

                    for(UINT64 timeOmega = GetTickCount64();
                            timeOmega - timeAlpha < FOUR_MINUTES;
                            timeOmega = GetTickCount64())
                    {
                        HlprEventSet(pThreadData->threadStopEvent);

                        status = HlprThreadWaitForCompletion(pThreadData);
                        if(status == NO_ERROR)
                            break;
                    }

                    if(status != NO_ERROR)
                    {
                        HlprLogInfo(L"Possible Runaway Thread");

#pragma warning(push)
#pragma warning(disable: 6258) /// This is a last resort

                        TerminateThread(pThreadData->thread,
                                        ERROR_THREAD_WAS_SUSPENDED);

#pragma warning(pop)
                    }
                }
            }
            else
            {
                status = GetLastError();

                HlprLogError(L"HlprThreadCleanup() [status:%#x]",
                             status);
            }
        }

        HLPR_CLOSE_HANDLE(pThreadData->threadStopEvent);

        HLPR_CLOSE_HANDLE(pThreadData->threadStartEvent);

        HLPR_CLOSE_HANDLE(pThreadData->threadContinueEvent);

        ZeroMemory(pThreadData,
                   sizeof(THREAD_DATA));

        pThreadData->threadStartRoutine = threadStartRoutine;
    }

    return;
}