Solved

Notification of Missing DLL's from a child process

Posted on 1999-01-04
15
525 Views
Last Modified: 2013-11-20
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.
0
Comment
Question by:traott
  • 9
  • 4
  • 2
15 Comments
 
LVL 6

Expert Comment

by:snoegler
Comment Utility
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
 
LVL 86

Expert Comment

by:jkr
Comment Utility
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
 

Author Comment

by:traott
Comment Utility
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
 
LVL 86

Expert Comment

by:jkr
Comment Utility
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
 

Author Comment

by:traott
Comment Utility
How do debuggers handle this exception?  If debuggers can handle this there has to be a way for my application to handle it.
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
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
 
LVL 6

Expert Comment

by:snoegler
Comment Utility
It is ...
0
Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 86

Expert Comment

by:jkr
Comment Utility
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
 

Author Comment

by:traott
Comment Utility
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
 
LVL 86

Expert Comment

by:jkr
Comment Utility
Do you thik i may lock the Q along with the code?
0
 

Author Comment

by:traott
Comment Utility
Yes, do so. I appreciate the help on this question.
0
 
LVL 86

Accepted Solution

by:
jkr earned 100 total points
Comment Utility
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
 
LVL 86

Expert Comment

by:jkr
Comment Utility
yeek, excuse the formatting - as i pasted it into the edit box, it was nice, so i think EE is to blame ;-)
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
And, of course - feel free to ask if you have further questions on this issue...
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
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

Featured Post

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Whole sheet autoscrub still needed 19 44
matchUp  challenge 6 48
Window placement 17 64
Use of condition with 'serial' in ansible 2 36
Here is how to use MFC's automatic Radio Button handling in your dialog boxes and forms.  Beginner programmers usually start with a OnClick handler for each radio button and that's just not the right way to go.  MFC has a very cool system for handli…
Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

8 Experts available now in Live!

Get 1:1 Help Now