Notification of Missing DLL's from a child process

Is there a way to get some kind of notification when the application fails because of missing DLL's when the Application is a child process?  I am using CreateProcess(..) to spawn the process and it returns TRUE even though the process fails because of missing DLL's.
traottAsked:
Who is Participating?
 
jkrConnect With a Mentor Commented:
Thanx ;-)
BTW: The following source code is part of our own 'mini-debugger', i just removed the ImageHlp calls, simplified  and modified it to check for 'STATUS_DLL_NOT_FOUND' - NOTE that the function originally represented a thread!

#ifndef STATUS_DLL_NOT_FOUND
#define STATUS_DLL_NOT_FOUND      0xC0000135
#endif

ULONG      __stdcall      DbgThread      (      LPVOID      pv)
{
      DEBUG_EVENT      dbgev;

      char            acBuf            [      2048]; // should be sufficiant for demo purposes ;-)
      
      char            acModule      [      MAX_PATH];
      char*            pszModule      =      NULL;

      DWORD            dwStatus;

      HANDLE            hProcess      =      INVALID_HANDLE_VALUE;

      STARTUPINFO                  si;
      PROCESS_INFORMATION      pi;


      ZeroMemory      (      &si,      sizeof      (      STARTUPINFO));

      si.cb                  =      sizeof      (      STARTUPINFO);
      si.dwFlags            =      STARTF_USESHOWWINDOW;
      si.wShowWindow      =      SW_SHOWDEFAULT;

      if      (      !CreateProcess      (      NULL,
                                                --> your debuggee goes here (as you know ;-) <--,
                                                NULL,
                                                NULL,
                                                FALSE,
                                                DEBUG_ONLY_THIS_PROCESS | CREATE_NEW_CONSOLE,
                                                GetEnvironmentStrings      (),
                                                NULL,
                                                &si,
                                                &pi
                                          )
            )
            {
                  printf      (      "ERROR starting '%s'\n", pJob->job.pszModule);
                  return      (      0);
            }

      CloseHandle      (      pi.hThread);
      CloseHandle      (      pi.hProcess);

      SetDebugErrorLevel      (      SLE_WARNING);

      while      (      WaitForDebugEvent      (      &dbgev,      INFINITE))
                  {
                        dwStatus      =      DBG_CONTINUE;


                        switch      (      dbgev.dwDebugEventCode)
                                    {
                                          case      EXCEPTION_DEBUG_EVENT            :

                                                      // the _initial_ breakpoit set by the system is to be avoided!!
                                                      if      (      EXCEPTION_BREAKPOINT !=      dbgev.u.Exception.ExceptionRecord.ExceptionCode)
                                                                  dwStatus      =      DBG_EXCEPTION_NOT_HANDLED;

                                                      if      (      STATUS_DLL_NOT_FOUND =      dbgev.u.Exception.ExceptionRecord.ExceptionCode)
                                                            {
                                                                  dwStatus      =      DBG_EXCEPTION_NOT_HANDLED;

                                                                  // a DLL couldn't be found....
                                                            }


                                                      break;

                                          case      CREATE_THREAD_DEBUG_EVENT   :


                                                      break;

                                          case      CREATE_PROCESS_DEBUG_EVENT  :


                                                      break;

                                          case      EXIT_THREAD_DEBUG_EVENT     :

                                                      break;

                                          case      EXIT_PROCESS_DEBUG_EVENT    :

                                                      break;

                                          case      LOAD_DLL_DEBUG_EVENT        :

                                                      break;

                                          case      UNLOAD_DLL_DEBUG_EVENT      :

                                                      break;

                                          case      OUTPUT_DEBUG_STRING_EVENT   :
                                                {
                                                      ULONG      ulBytes;
                                                      void*      pOutput      =      malloc      (      dbgev.u.DebugString.nDebugStringLength + 1);

                                                      ZeroMemory      (      pOutput, dbgev.u.DebugString.nDebugStringLength + 1);

                                                      ReadProcessMemory      (      hProcess,
                                                                                          dbgev.u.DebugString.lpDebugStringData,
                                                                                          pOutput,
                                                                                          dbgev.u.DebugString.nDebugStringLength,
                                                                                          &ulBytes
                                                                                    );

                                                      wsprintf      (      acBuf,
                                                                              "OUTPUT_DEBUG_STRING_EVENT, enc.: '%s', length == %d, data == '%s'",
                                                                                    dbgev.u.DebugString.fUnicode
                                                                              ?      "UNICODE"
                                                                              :      "ANSI",
                                                                              dbgev.u.DebugString.nDebugStringLength,
                                                                                    dbgev.u.DebugString.fUnicode
                                                                              ?      GetAnsiString      (      ( wchar_t*) pOutput)
                                                                              :      ( char*) pOutput
                                                                        );

                                                      free      (      pOutput);

                                                      break;
                                                }

                                          case      RIP_EVENT                   :

                                                      wsprintf      (      acBuf,
                                                                              "RIP_EVENT, error code == 0x%08x",
                                                                              dbgev.u.RipInfo.dwError
                                                                        );

                                                      break;

                                          default:      break;
                                    }


                        ContinueDebugEvent      (      dbgev.dwProcessId,      
                                                            dbgev.dwThreadId,
                                                            dwStatus
                                                      );
                  }

      return      (      0);
}

0
 
snoeglerCommented:
Check out www.sysinternals.com, head for the source of
"DLLView". This is a tools which monitors the loaded DLL's of
each process. As far as i remember, they use a dynamic VXD
to do this.
This could be useful to you.
0
 
jkrCommented:
To check the failure of process creation, simply try the following:
Use the 'hProcess' member of the 'PROCESS_INFORMATION' structure filled in by the 'CreateProcess()' call and use 'WaitForSingleObject()' with a short timeout - the object's state is signaled when it terminates, so 'WAIT_TIMEOUT' guarantees that it's still running, and 'WAIT_ABANDONED' indicates a failure...

0
Cloud Class® Course: CompTIA Healthcare IT Tech

This course will help prep you to earn the CompTIA Healthcare IT Technician certification showing that you have the knowledge and skills needed to succeed in installing, managing, and troubleshooting IT systems in medical and clinical settings.

 
traottAuthor Commented:
I am using WaitForSingleObject but it does not tell me why the process terminated.  This WAIT_ABANDONED signal telles me that something happened but not what.  I need to know specifically if a DLL is missing.
0
 
jkrCommented:
The problem is that there's no specific error code in Win32 that indicates to a 'parent process' that a process creation failed due to a missing DLL - even worse, child processes have no (documented) relation to their parents... Well, NT signals a 'STATUS_DLL_NOT_FOUND' (C0000135) to the started process, but to receive this exception, you'd have to be a debugger ;-)
0
 
traottAuthor Commented:
How do debuggers handle this exception?  If debuggers can handle this there has to be a way for my application to handle it.
0
 
jkrCommented:
A debugger gets notified by a 'EXCEPTION_DEBUG_EVENT'. It calls 'WaitForDebugEvent()' after the process has been started and receives a 'DEBUG_EVENT' union, in which (in this case) 'dbgev.u.Exception.ExceptionRecord.ExceptionCode' holds 'STATUS_DLL_NOT_FOUND' (0xC0000135) when a DLL can't be located. BUT: You'll have to use 'CreateProcess( ..., DEBUG_ONLY_THIS_PROCESS,...)' to become a debugger... (and i don't know whether this status code is dispatched on Win9x...
0
 
snoeglerCommented:
It is ...
0
 
jkrCommented:
Thanx, snoegler. This status code is only #defined in 'ntstatus.h' (and in no VC++ header), so i wansn't sure...

traott - do you need some code to illustrate how to do it?
0
 
traottAuthor Commented:
I do not need any code, the implementation is fairly straight forward for this Idea.  This is just an after thought, but will this also tell me what DLL the process cannot find.  I do not really need it but it would be nice.
0
 
jkrCommented:
Do you thik i may lock the Q along with the code?
0
 
traottAuthor Commented:
Yes, do so. I appreciate the help on this question.
0
 
jkrCommented:
yeek, excuse the formatting - as i pasted it into the edit box, it was nice, so i think EE is to blame ;-)
0
 
jkrCommented:
And, of course - feel free to ask if you have further questions on this issue...
0
 
jkrCommented:
Oops, i just saw that 'GetAnsiString()' in the 'OUTPUT_DEBUG_STRING_EVENT' branch isn't defined, it's simply

char*   GetAnsiString   (   wchar_t*    pwstr)
{
    static  char    s_acBuf [   1024];

    int             nLen;

    if  (   !pwstr)
            return  (   "");

    if  (   nLen    =   wcslen  (   pwstr  + 1) >   1024)  
            return  (   "");

    if  (   !WideCharToMultiByte    (   CP_ACP,
                                        0,
                                        pwstr,
                                        -1,
                                        s_acBuf,
                                        nLen    *   2,
                                        NULL,
                                        NULL
                                    )
        )   return  (   "");

    return  (   s_acBuf);
}


0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.