//-----------------------------------------------------------------------------
/// Called to indicate that a resource is being destroyed
/// detaches from anything that was attached in OnCreate
/// \param type the type of resource that is being destroyed
/// \param pPtr pointer to the resource that is being destroyed
/// \return should return false on error; true otherwise
//-----------------------------------------------------------------------------
bool DX12FrameProfilerLayer::OnDestroy(CREATION_TYPE type, void* pPtr)
{
    PS_UNREFERENCED_PARAMETER(pPtr);
    PS_UNREFERENCED_PARAMETER(type);

    return true;
}
//--------------------------------------------------------------------------
/// Invoked when the MantleTraceAnalyzerLayer is created.
/// \param inType The incoming type of interface being created.
/// \param pInPtr An interface pointer being created.
/// \returns True if creation was successful.
//--------------------------------------------------------------------------
bool MultithreadedTraceAnalyzerLayer::OnCreate(CREATION_TYPE inType, void* pInPtr)
{
    PS_UNREFERENCED_PARAMETER(inType);
    PS_UNREFERENCED_PARAMETER(pInPtr);

    return true;
}
Example #3
0
//--------------------------------------------------------------------------
/// A handler invoked when a key is pressed within the instrumented application.
/// \param inCode The code for the keypress event.
/// \param wParam The WPARAM for the keypress event. Contains the virtual key code for the pressed key.
/// \param lParam The LPARAM for the keypress event. Contains flags for the key press event.
//--------------------------------------------------------------------------
void ModernAPILayerManager::OnKeyPressed(int inCode, WPARAM wParam, LPARAM lParam)
{
    PS_UNREFERENCED_PARAMETER(inCode);
    PS_UNREFERENCED_PARAMETER(lParam);

    // If the capture key was pressed, we need to trigger a capture.
    if (wParam == kPrintScreenVirtualKeyCode)
    {
        mbTraceTriggeredFromKeypress = true;
    }
}
//--------------------------------------------------------------------------
/// Called by the CommandProcessor to give the CommandResponse a chance to
/// parse all of the command parameters before the app can call IsActive()
/// \param rCommObj incoming command which has the parameters that need to
///     be parsed
/// \return true if the params could be parsed; false otherwise
//--------------------------------------------------------------------------
bool CommandResponse::GetParams(CommandObject& rCommObj)
{
    PS_UNREFERENCED_PARAMETER(rCommObj);

    /// the default CommandResponse has no parameters,
    /// so there is nothing to do here.
    /// this should be overwritten when CommandResponse is inherited
    return true;
}
//-----------------------------------------------------------------------------
/// Called to indicate that a resource is being created
/// The layer must create its resources and hook functions here
/// \param type the type of resource that is being created
/// \param pPtr pointer to the resource that is being created
/// \return should return false on error; true otherwise
//-----------------------------------------------------------------------------
bool DX12FrameProfilerLayer::OnCreate(CREATION_TYPE type, void* pPtr)
{
    PS_UNREFERENCED_PARAMETER(type);
    PS_UNREFERENCED_PARAMETER(pPtr);

    bool bInitSuccessful = false;

    // Don't initialize the Frame Profiler more than once.
    static bool bAlreadyInitialized = false;

    if (bAlreadyInitialized == false)
    {
        bInitSuccessful = true;
        bAlreadyInitialized = true;
    }

    return bInitSuccessful;
}
Example #6
0
/// Debug function used to filter out specific commands.
/// \param strCmd Command string
/// \return True if allowed, false if not
bool AllowCommand(char* strCmd)
{
    PS_UNREFERENCED_PARAMETER(strCmd);

    return true;

    // Leave commented out while developing
    //if ( strstr ( strCmd, "IndexBufferInfo.xml" ) != NULL )
    //{
    //   return true;
    //}
    //if ( strstr ( strCmd, "BreakPoint" ) != NULL )
    //{
    //   return true;
    //}
    //return false;
}
//--------------------------------------------------------------
//  LaunchAppInNewProcess
//--------------------------------------------------------------
PROCESS_INFORMATION ProcessTracker::LaunchAppInNewProcess(gtASCIIString strApp, gtASCIIString strDir, gtASCIIString strArgs, osModuleArchitecture binaryType)
{
#ifdef _LINUX
    PS_UNREFERENCED_PARAMETER(binaryType);
#endif
    LogConsole(logMESSAGE, "About to launch: %s\n", strApp.asCharArray());
    LogConsole(logMESSAGE, "Params: %s\n", strArgs.asCharArray());
    LogConsole(logMESSAGE, "Working Directory: %s\n", strDir.asCharArray());

    // Get app directory and make it default
    if (strDir.isEmpty())
    {
        size_t pos = strApp.find_last_of("\\");

        if (pos != std::string::npos)
        {
            strDir = strApp.substr(0, (int)pos);

            if (strApp[0] == '\"')
            {
                strDir += "\"";
            }
        }
    }

    PROCESS_INFORMATION pi;
    ZeroMemory(&pi, sizeof(pi));

#ifdef _WIN32
    DWORD dwFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED;

    SetLastError(0);

    STARTUPINFO si;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
#endif

    // Cmd line has to include the exe name since many apps expect the executable name to be in argv[0]!
    // Note argv[0] on the command line needs to be surrounded with quotes if it contains spaces.
    // The arguments in strArgs have already been "quoted" as they are parsed.

    gtASCIIString strCmdLine = AddQuotesIfStringHasSpaces(strApp.asCharArray());
    strCmdLine += " ";
    strCmdLine += strArgs;

    LogConsole(logMESSAGE, "strApp: %s\n", strApp.asCharArray());
    LogConsole(logMESSAGE, "strCmdLine: %s\n", strCmdLine.asCharArray());

    // Attempt to initialize the environment that the new process will run in. The child process should inherit "this" environment.
    if (!PrelaunchEnvironmentInitialization())
    {
        // Log a warning if this failed- initializing the environment for the new process can fail if Mantle support isn't installed.
        // In these cases, if the user is attempting to debug a Mantle application, they will have bigger problems to deal with.
        // In cases where a DX/GL app is being debugged, this warning can be ignored without any side effects.
        Log(logWARNING, "Environment initialization failed. If using DX/GL, it is safe to ignore this warning.\n");
    }

    BOOL succeeded = FALSE;

#ifdef _WIN32

    char microDLLPath[PS_MAX_PATH];
    const char* strServerPath;
    strServerPath = SG_GET_PATH(ServerPath);

    if (SG_GET_BOOL(OptionDllReplacement) == true)
    {
        DllReplacement::SetDllDirectory(binaryType == OS_X86_64_ARCHITECTURE);
    }

    // if using manual dll replacement or the AppInit_DLLs registry setting, don't use any kind of dll injection
    if (SG_GET_BOOL(OptionManualDllReplacement) == true || SG_GET_BOOL(OptionAppInitDll))
    {
        succeeded = CreateProcess(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(), NULL, NULL, TRUE, dwFlags, NULL, strDir.asCharArray(), &si, &pi);
    }
    else
    {
#ifdef X64

        // can only launch 64 bit applications
        if (binaryType != OS_X86_64_ARCHITECTURE)
        {
            sprintf_s(microDLLPath, PS_MAX_PATH, "%s" MICRODLLNAME "%s%s.dll", SG_GET_PATH(ServerPath), GDT_DEBUG_SUFFIX, GDT_BUILD_SUFFIX);
            succeeded = AMDT::CreateProcessAndInjectDll(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(),
                                                        NULL, NULL, TRUE, dwFlags, NULL,
                                                        strDir.asCharArray(),
                                                        &si, &pi,
                                                        microDLLPath);
        }

#else

        if (binaryType != OS_I386_ARCHITECTURE)
        {
            sprintf_s(microDLLPath, PS_MAX_PATH, "%s" MICRODLLNAME "-x64%s%s.dll", SG_GET_PATH(ServerPath), GDT_DEBUG_SUFFIX, GDT_BUILD_SUFFIX);
            succeeded = AMDT::CreateProcessAndInjectDll(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(),
                                                        NULL, NULL, TRUE, dwFlags, NULL,
                                                        strDir.asCharArray(),
                                                        &si, &pi,
                                                        microDLLPath);
        }

#endif // X64
        else
        {
            succeeded = AMDT::CreateProcessAndInjectDll(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(),
                                                        NULL, NULL, TRUE, dwFlags, NULL,
                                                        strDir.asCharArray(),
                                                        &si, &pi,
                                                        SG_GET_PATH(MicroDLLPath));
        }
    }

#else

    // Create the app process
    succeeded = CreateProcess(strApp.asCharArray(), strCmdLine.asCharArray(), strDir.asCharArray(), &pi);
#endif // _WIN32

    if (!succeeded)
    {
        osSystemErrorCode systemLastError = osGetLastSystemError();

        gtString systemErrorString;
        osGetLastSystemErrorAsString(systemErrorString);

        Log(logERROR, "CreateProcessAndInjectDll failed; Error %d: %s\n", systemLastError, systemErrorString.asASCIICharArray());
        pi.dwProcessId = 0;
    }

#ifdef _WIN32
    else
    {
        // Check to see if the Steam.exe has been hooked and if so, set the value in shared memory
        // If Steam.exe was used to launch the target application, then the checks for cmd.exe and fcx.exe
        // need to be ignored.
        if (strApp.length() > 0)
        {
            if (strstr(strApp.toLowerCase().asCharArray(), "steam.exe") != NULL)
            {
                SG_SET_BOOL(SteamInjected, true);
            }

            ShowLauncherReminder(strApp.toLowerCase().asCharArray());
        }
        else
        {
            if (strstr(strCmdLine.toLowerCase().asCharArray(), "steam.exe") != NULL)
            {
                SG_SET_BOOL(SteamInjected, true);
            }

            ShowLauncherReminder(strCmdLine.toLowerCase().asCharArray());
        }
    }

#endif // _WIN32

    return pi;
}
bool ProcessTracker::HandleRequest(HTTPRequestHeader* pRequestHeader,
                                   CommunicationID requestID,
                                   NetSocket* pClientSocket,
                                   bool renderLoopStalled)
{
    char* ptr = pRequestHeader->GetUrl();
    char* sCmd = &ptr[1];

#ifdef CODEXL_GRAPHICS
#ifdef USE_GRAPHICS_SERVER_STATUS_RETURN_CODES

    // Handle process not running condition
    if (pRequestHeader->CheckProcessStillRunning() == false)
    {
        Log(logMESSAGE, "Rejecting the command above due to process no longer running: %s\n", pRequestHeader->GetUrl());
        // Need to return the correct error data for process not running
        HandleServerStatusResponse(GRAPHICS_SERVER_STATE_PROCESS_NOT_RUNNING, pRequestHeader, pClientSocket);
        // This request never gets passed to the graphics server.
        return true;
    }

    // Check if renering has stalled.
    if (renderLoopStalled == true)
    {
        Log(logMESSAGE, "Rejecting the command above due to render stall: %s\n", pRequestHeader->GetUrl());
        // Need to return the correct error data for process not running
        HandleServerStatusResponse(GRAPHICS_SERVER_STATE_STALLED, pRequestHeader, pClientSocket);
        // This request never gets passed to the graphics server.
        return true;
    }

#else
    UNREFERENCED_PARAMETER(renderLoopStalled);
#endif
#else
    PS_UNREFERENCED_PARAMETER(renderLoopStalled);
#endif


#ifdef _WIN32

    // if using AppInit_Dll, clear the registry as soon as a process.xml request is sent.
    // TODO: move this to where a connection has definately been made
    if (SG_GET_BOOL(OptionAppInitDll) == true)
    {
        if (registryCleared == false)
        {
            RestoreAppInit();
            registryCleared = true;
        }
    }

#endif

    if (IsToken(&sCmd, "inject?"))
    {
        DoInjectCommand(requestID, &sCmd, pClientSocket);
    }
    else
    {
        // do commands that depend on PID

        for (WrapperMap::iterator wrapperIter = g_activeWrappersMap.begin();
             wrapperIter != g_activeWrappersMap.end();
             ++wrapperIter)
        {
            // parse out the process ID
            unsigned long pid = 0;
            sscanf_s(wrapperIter->first.c_str(), "%lu/", &pid);

            // make PID string for easier parsing of the command
            gtASCIIString strPID;
            strPID.appendFormattedString("%lu", pid);

            gtASCIIString strPidSlashPlugin = wrapperIter->first.c_str();

            // the key in the activeWrappersMap is formatted as pid/plugin (ie: "435/DX11")
            // and this conveniently matches the format of the commands coming into the server, so parse for matching commands
            gtASCIIString tmpString = strPidSlashPlugin;
            tmpString += "/";

            if (IsToken(&sCmd, tmpString.asCharArray()))
            {
                // we know this command is targetting the current plugin
                pRequestHeader->SetUrl(sCmd);

                // pass the request to the plugin
#ifdef _WIN32
                const char* memoryName = strPidSlashPlugin.asCharArray();
#else
                // the '/' character can't be used as a filename in Linux, so just use the plugin name as the shared memory name
                // (ignore the process ID)
                char memoryName[PS_MAX_PATH];
                char pluginShortDesc[ PS_MAX_PATH ];
                sscanf_s(strPidSlashPlugin.asCharArray(), "%lu/%s", &pid, pluginShortDesc, sizeof(pluginShortDesc));
                sprintf_s(memoryName, PS_MAX_PATH, "%lu %s", pid, pluginShortDesc);
#endif

                if (PassRequestToPlugin(memoryName, pRequestHeader, pid, pClientSocket))
                {
                    return true;
                }
                else
                {
                    Log(logERROR, "Request '%s' is not targeted to an active plugin.\n", GetRequestText(requestID));
                    SendHTMLResponse(requestID, "<html>Error: Targeted plugin is not currently active.</html>", pClientSocket);
                }
            }
            else
            {
                tmpString = strPID;
                tmpString += "/Kill";

                if (IsToken(&sCmd, tmpString.asCharArray()))
                {
                    if (KillProcess(pid))
                    {
                        SendTextResponse(requestID, "Ok", pClientSocket);

                        // if the app was launched from the command line or drag and drop
                        // then killing the app should cause the server to shutdown also
                        if (g_bAppSpecifiedAtCmdLine)
                        {
                            g_shutdownEvent.Signal();
                            CloseStreamThread();
                            CloseStreamSockets();
                            SendTextResponse(requestID, "OK", pClientSocket);
                            return true;
                        }
                    }
                    else
                    {
                        Log(logERROR, "Failed to Kill the process\n");
                        SendTextResponse(requestID, "Error: Failed to Kill the process.", pClientSocket);
                    }
                }
            }
        }
    }

    return false;
}
////////////////////////////////////////////////////////////////////////////////////////////
/// Passes the supplied request header to the specifed plugin
/// \param strDestination the short description of the plugin to pass the request to
/// \param pRequestHeader the request header to pass to the plugin
/// \param pid the process id to pass the request to.
/// \param pClientSocket the socket used to read the request
/// \return true if the request was passed to the plugin; false otherwise
////////////////////////////////////////////////////////////////////////////////////////////
bool ProcessTracker::PassRequestToPlugin(const char* strDestination,
                                         HTTPRequestHeader* pRequestHeader,
                                         unsigned long pid,
                                         NetSocket* pClientSocket)
{
    if (strstr(pRequestHeader->GetHeaderData()->url, STR_STREAM_TOKEN) != NULL)
    {
        Log(logTRACE, "Duplicating Socket for request: %s\n", pRequestHeader->GetHeaderData()->url);

        // this is a streaming request, duplicate the socket
        // to reduce the overhead incurred by the shared memory approach
#if defined (_WIN32)

        if (pClientSocket->DuplicateToPID(pid, &pRequestHeader->GetHeaderData()->ProtoInfo) != 0)
#else
        PS_UNREFERENCED_PARAMETER(pid);

        if (CreateStreamSocket(pRequestHeader, pClientSocket) == false)
#endif
        {
            Log(logERROR, "Failed to duplicate socket for streaming request. Error %lu\n", osGetLastSystemError());
            return false;
        }
    }

    bool bResult = false;

    if (smLockPut(strDestination, sizeof(HTTPRequestHeader), 1))
    {
        // Check to see if the postdata failed to come over in some way.
        if (pRequestHeader->GetPostDataSize() > 0 &&  pRequestHeader->GetPostData() == NULL)
        {
            Log(logERROR, "PostData size is %d, but PostData is NULL\n", pRequestHeader->GetPostDataSize());
            // Force the data size to zero
            pRequestHeader->SetPostDataSize(0);
        }

        // Create a new record of this request
        // We will check to see when it comes back from the server.
        //RequestInFlight* pNewRequest = new RequestInFlight(pRequestHeader, pClientSocket);

        // Add the new record to the DB.
        //RequestsInFlightDatabase::Instance()->Add(pClientSocket, pNewRequest);

        bResult = smPut(strDestination, pRequestHeader->GetHeaderData(), sizeof(HTTPHeaderData));

        // Keep in for debugging
        //StreamLog::Ref() << "1) smPut HTTPRequestHeader\n" ;

        // If there is POST data we must send that too.
        if (pRequestHeader->GetPostDataSize() > 0 && pRequestHeader->GetPostData() != NULL)
        {
            bResult = smPut(strDestination, pRequestHeader->GetPostData(), pRequestHeader->GetPostDataSize());

            // Keep in for debugging
            //StreamLog::Ref() << "2) smPut Shader Code: " << pRequestHeader->pPostData << "\n";
        }

        smUnlockPut(strDestination);
#ifdef DEBUG_COMMS_PERFORMANCE
        CommandTimingManager::Instance()->IncrementServerLoadingCount(1) ;
#endif
    }

#ifdef DEBUG_COMMS_PERFORMANCE
    // Record the time now that we have sent the command over the shared memory.
    CommandTiming* cmdTiming = CommandTimingManager::Instance()->GetTimingFromPendingList(pClientSocket);

    if (cmdTiming != NULL)
    {
        LARGE_INTEGER nPerformanceCount;
        QueryPerformanceCounter(&nPerformanceCount);
        cmdTiming->SetPostSharedMemorySend(nPerformanceCount);
        // Set the current server loading value
        cmdTiming->SetServerLoadingCount(CommandTimingManager::Instance()->GetServerLoadingCount());
    }

#endif

    return bResult;
}
Example #10
0
//--------------------------------------------------------------
/// Creates and then waits for the PLUGINS_TO_GPS_SEMAPHORE to be
/// signaled, then reads the responses in PLUGINS_TO_GPS shared
/// memory and sends them to the requester.
/// \param pData should be NULL (it is ignored though)
//--------------------------------------------------------------
void PluginResponseThread::WaitForPluginResponses(void* pData)
{
    PS_UNREFERENCED_PARAMETER(pData);

    //create a semaphore which allows putting at most MAX_SEM_COUNT response into SM;
    NamedSemaphore semaphore;
    semaphore.Create("PLUGINS_TO_GPS_SEMAPHORE");

    bool bEvent;

    for (;;)    // loop forever
    {
        bEvent = semaphore.Wait();

        if (bEvent == false)
        {
            Log(logERROR, "Failed to wait on an event (Error %d). Closing response thread.\n", osGetLastSystemError());
            smClose("PLUGINS_TO_GPS");
            semaphore.Close();
            return;
        }

        // the PLUGINS_TO_GPS_EVENT was signaled
        // retrieve and send the response
        if (smLockGet("PLUGINS_TO_GPS"))
        {
            // read data from shared memory
            CommunicationID requestID = 0;
            char pcMimeType[PS_MAX_PATH];

            // the response was signaled, read out one response
            if (bEvent == true)
            {
                requestID = 0;
                memset(pcMimeType, 0, PS_MAX_PATH);
                char* pResponse = NULL;
                unsigned long uResponseSize = 0;

                LARGE_INTEGER nPreSharedMemoryGetTime;
                OSWrappers::QueryPerformanceCounter(&nPreSharedMemoryGetTime);

                if (smGet("PLUGINS_TO_GPS", &requestID, sizeof(CommunicationID)) == sizeof(CommunicationID))
                {
                    // successfully got the requestID

                    // Get the socket associated with this requestID and remove the socket since we're now donw with it.
                    NetSocket* client_socket = ProcessTracker::Instance()->GetSocketFromHandle(requestID);
                    ProcessTracker::Instance()->RemoveSocketFromMap(requestID);

#ifdef CODEXL_GRAPHICS
#ifdef USE_GRAPHICS_SERVER_STATUS_RETURN_CODES
                    // We can remove this message from the DB as we no longer need to monitor it anymore
                    RequestsInFlightDatabase::Instance()->Remove(client_socket);
#endif
#endif
                    // now try to get the mime type
                    if (smGet("PLUGINS_TO_GPS", &pcMimeType, PS_MAX_PATH) > 0)
                    {
                        // successfully got the mime type

                        // get response size
                        while (uResponseSize == 0)
                        {
                            uResponseSize = smGet("PLUGINS_TO_GPS", NULL, 0);
                        }

                        try
                        {
                            pResponse = new char[uResponseSize];
                            memset(pResponse, 0, uResponseSize * sizeof(char));
                        }
                        catch (std::bad_alloc)
                        {
                            Log(logERROR, "Failed to allocate memory for response of size: %lu\n", uResponseSize);
                            pResponse = NULL;
                        }

                        if (pResponse != NULL)
                        {
                            // Read from shared memory
                            if (smGet("PLUGINS_TO_GPS", pResponse, uResponseSize) == 0)
                            {
                                Log(logERROR, "Failed to get response from sharedMemory.\n");
                                smReset("PLUGINS_TO_GPS");
                            }

#ifdef DEBUG_COMMS_PERFORMANCE
                            // Record the time now
                            CommandTiming* pTiming = CommandTimingManager::Instance()->HandleResponse((NetSocket*)requestID);

                            if (pTiming != NULL)
                            {
                                LARGE_INTEGER nPerformanceCount;
                                QueryPerformanceCounter(&nPerformanceCount);
                                pTiming->SetWebServerRoundTripEnd(nPerformanceCount);    // Must set this one before SetPreSharedMemoryGet
                                pTiming->SetPreSharedMemoryGet(nPreSharedMemoryGetTime);
                                pTiming->SetResponseSize(uResponseSize);
                            }

#endif

                            // Send the data back to the client
                            SendMimeResponse(requestID, pcMimeType, pResponse, uResponseSize, client_socket);

#ifdef DEBUG_COMMS_PERFORMANCE
                            CommandTimingManager::Instance()->IncrementServerLoadingCount(-1);
#endif

                            SAFE_DELETE_ARRAY(pResponse);
                        }
                        else
                        {
                            smReset("PLUGINS_TO_GPS");
                            SendMimeResponse(requestID, "plain/text", "Error: Failed to get response from shared memory\n", 49 * sizeof(char), client_socket);
                        }

                    }
                    else
                    {
                        smReset("PLUGINS_TO_GPS");
                        SendMimeResponse(requestID, "plain/text", "Error: Could not read mime type\n", 32 * sizeof(char), client_socket);
                    }
                }
                else
                {
                    // in this case, we don't know socket the communication was on, so we can't send any errors or even close the socket
                    // just have to let the client time out.
                    smReset("PLUGINS_TO_GPS");
                    Log(logERROR, "Failed to get requestID from sharedMemory.\n");
                }

            } // end while sm has data or no responses have been read

            smUnlockGet("PLUGINS_TO_GPS");

        }
        else
        {
            Log(logERROR, "LockGet Failed\n");
        }
    }
}
Example #11
0
//--------------------------------------------------------------
///   DllMain
/// \param hModule Module
/// \param ul_reason_for_call Reason for call
/// \param lpReserved Reserved
/// \return True or false
//--------------------------------------------------------------
BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    PS_UNREFERENCED_PARAMETER(hModule);
    PS_UNREFERENCED_PARAMETER(lpReserved);

    if (AMDT::InitHookDLL(ul_reason_for_call) == false)
    {
        return TRUE;
    }

    BOOL retVal = TRUE;
    char modulename[MAX_PATH];
    GetModuleFileNameA(NULL, modulename, MAX_PATH);

    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        {
            if (OSWrappers::IsProcessRunning(sWebServerName, true) && InjectionAllowed(modulename))
            {
                Log(logMESSAGE, "Attaching to %s\n", modulename);
                Log(logMESSAGE, "DllMain DLL_PROCESS_ATTACH module %s\n", modulename);

                if (SG_GET_BOOL(OptionDllReplacement) == true)
                {
                    UpdateHooksOnLoadLibrary();
                }

#ifdef _WIN32
                {
                    // Initialize and register the unhandled exception handler:
                    bool rc1 = UnhandledExceptionHandler::init();

                    if (rc1)
                    {
                        Log(logMESSAGE, "Registered unhandled exception handler\n");
                    }
                    else
                    {
                        Log(logERROR, "Failed to register unhandled exception handler\n");
                    }
                }
#endif

                // @Note: Do we need to do this? Overwriting it doesn't seem to break anything?
                // get current directory so we can specify as a location for DLLs to be loaded from
                char curDir[PS_MAX_PATH];
                GetCurrentDirectory(PS_MAX_PATH, curDir);

                // SetDllDirectory requires XP SP1 or later.
                SetDllDirectory(curDir);

                sprintf_s(g_MicroDLLPath, PS_MAX_PATH, "%s", SG_GET_PATH(MicroDLLPath));

                CollectWrapperInfo();

                if (SG_GET_BOOL(OptionNoProcessTrack) == false)
                {
                    HookCreateProcess();
                    Log(logMESSAGE, "Process Tracking is ON\n");
                }
                else
                {
                    Log(logMESSAGE, "Process Tracking is OFF\n");
                }

                // At the moment, dxgi.dll is implicitly loaded from HookLoadLibrary
                // Force the Dll folder to point to our replaced dll's so the replace version
                // of dxgi.dll is loaded
                if (SG_GET_BOOL(OptionDllReplacement) == true)
                {
                    // Get architecture of parent application (32 or 64 bit)
                    osModuleArchitecture binaryType;
                    OSWrappers::GetBinaryType(modulename, &binaryType);
                    DllReplacement::SetDllDirectory(binaryType == OS_X86_64_ARCHITECTURE);
                }

                if (SG_GET_BOOL(OptionManualDllReplacement) == false)
                {
                    HookLoadLibrary();
                }
            }
            else
            {
                // set return value to FALSE. This will indicate a load error so the loader will
                // next unload this dll
                retVal = FALSE;
            }

            break;
        }

        case DLL_THREAD_ATTACH:
        {
            Log(logMESSAGE, "DllMain DLL_THREAD_ATTACH to %i %s\n", GetCurrentProcessId(), modulename);
        }
        break;

        case DLL_THREAD_DETACH:
        {
            Log(logMESSAGE, "DllMain DLL_THREAD_DETACH to %i %s\n", GetCurrentProcessId(), modulename);
        }
        break;

        case DLL_PROCESS_DETACH:
        {
            if (OSWrappers::IsProcessRunning(sWebServerName, true))
            {
                Log(logMESSAGE, "DllMain DLL_PROCESS_DETACH from module %s\n", modulename);

                if (SG_GET_BOOL(OptionNoProcessTrack) == false)
                {
                    UnhookCreateProcess();
                }

                UnhookLoadLibrary();
            }
        }
        break;

        default:
        {
            Log(logMESSAGE, "DllMain Unhandled switch case module %s\n", modulename);
        }
        break;
    }

    return retVal;
}