Windows scripting: is it possible to capture missing-DLL errors in a log file? - winapi

When you try to run a program on Windows, and the loader can't find all of the required DLLs, the default behavior is to pop up a dialog box that describes the problem, including both the name of the program and the name of (one of the) missing DLLs. The process then hangs until someone clicks OK, and then exits with an error code. Here's an example of this dialog box:
Now suppose you're scripting some automated process that might fail for this reason, e.g. running CI tests after installation, where part of the point is to make sure the installer installs all the DLLs. You don't want your build workers to hang waiting for someone to click on a dialog box that's being displayed on a monitor that the computer physically does not have because it's in a server rack somewhere. You want the test cycle to stop immediately and the details to get written to the log.
Your build driver can disable this dialog box for itself and all its child processes (assuming nobody uses CREATE_DEFAULT_ERROR_MODE) by calling SetErrorMode:
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
hProc = CreateProcess(...);
However, this only solves half of the problem. The offending process will terminate with an exit status of 0xC0000135 (STATUS_DLL_NOT_FOUND), but the name of the problem executable and the name of the missing DLL are not reported anywhere that I can find it.
So here's the actual question: From code running in the build driver, how do I get the name of the problem executable and the name of the missing DLL, so that I can write them to my CI build logs? Anything goes, except that the code of the problem executable itself and its DLLs cannot be modified (because this is supposed to be a general solution) and an approach that does not require elevated privileges is strongly preferred.
(This is a follow-up question for Suppress "The program can't start because X.dll is missing" error popup . I've been vaguely meaning to write it for years now.)

Even though it's a little overkill, you could write a program/script that parses the PE format of each executable, walks its import table to enumerate imported modules, and checks to see if each module exists on the system (i.e. is it in \Windows\System32, the current directory, etc).
Then, if the executable in question is missing an import, log the executable and the missing import somewhere (and don't run the executable, of course).
If you want to do this in Python, the pefile module is excellent (https://pypi.org/project/pefile/).
(Update: This won't work for DLLs that are imported via LoadLibrary)

Related

Find out path of output file in VB6

Okay, I've been struggling with this for full last couple of hours. I have a Windows 7 32-bit installation, on which I'm trying to compile some old code. I do not have any working knowledge of VB, but I'm trying to build a VB6 project which is part of the code base. The code compiles and when I run it after full-compile, it runs. But I can't seem to find where this file is. Task manager shows VB6 as process. It is not present in %TEMP%, and not in my project directory. Running an exhaustive search on my single drive configuration, I can find only one entry by this name, and this file has a timestamp older than current time. I attempted delete on this one while the debugger was still on breakpoint and it succeeded. As last resort in prefetch directory, I deleted that entry also, but magically this file still runs when on VB6. What must have been going on?
When you run an application within the VB6 IDE, it entirely runs within the VB6 IDE. Unlike some other languages, it isn't creating an executable file and then running it separately while hooking a debugger up, it just starts running your code using the VB interpreter.
To create a standalone .exe file, you need to create it separately. In the "File" menu, choose "Make projectname.exe". For full details, see "Making and Running an Executable File" in the Visual Basic 6 Concepts guide.

Passing parameters/values from Emacs to external application

I'm working on a plugin for a very niche CAD application that our company uses. The development environment is entirely based on Emacs and everything should be done through it.
In this case, code should be edited, navigated, compiled and debugged using Emacs. There is no separation from Emacs and the actual language compiler (from what we know so far - there is no compiler.exe).
Since my team is not a fan of Emacs we are trying to get rid of this dependency so we can use whatever editor we want an compile/interact with the application without Emacs, but looks like this is very hard because everything is built inside it, even go-to definition, find all references all and sorts of commands.
These are all defined in hundreds of .el files (LISP) that are called using custom commands from Emacs.
However, when calling "Run this file" for example, said file is ran inside the application and correctly executed so my question is: what mechanisms could be used used for this to happen? I don't expect precise answers as I can't share the name of the actual CAD application or anything else, but it would be really helpful to understand what options are possible so I can investigate them.
In summary, whenever I "Run this file" the actual application is started by Emacs by calling the command line and the actual file gets executed inside the application. Looks like the contents of the Emacs buffer are passed to the application somehow. This "how" is what I'm trying to find.
Thanks for any help.

Windows 8.1 Pro MinGW Gfortran Command Prompt 'not recognized command' error

C:\Users\redacted\Documents\redacted>gfortran hibrac.f -o hibrac.exe
'gfortran' is not recognized as an internal or external command,
operable program or batch file.
https://gcc.gnu.org/wiki/GFortranBinariesWindows seems similar to my problem: gfortran.exe is in C:\Windows\MinGW\bin -- except the solution appears not to apply to Windows 8.1 Pro:
Right click on My Computer, Properties, Advanced Tab, Environment Variables.
Instead I tried: Right click on 'This PC' within File Explorer, Properties, Advanced system settings, Advanced Tab -- and I cannot find an equivalent-looking section that allows me to proceed with the advised solution.
As background information:
I had installed MinGW Installation Manager which installed mingw32-gcc-fortran (together with mingw32-base, -gcc-g++, and -gcc-objc) in the recommended C:\Windows\MinGW folder, without any apparent error message.
Isn't it acceptable -- standard practice -- to have one's code in a folder separate from this MinGW folder? i.e. a subdirectory of my Users\account rather than a subdirectory of MinGW. This isn't the problem, is it? What do I need to do to get it to recognize the gfortran.exe, or call it correctly?
Please tell me what to do to get it working. If it's relevant, I have a Japanese computer with an English language pack installed (it seems to have some gaps, such as some text in the Settings charm or startup/shutdown text being in Japanese).
This looks very much like you have neglected to add C:\MinGW\bin to the effective PATH for the command window, in which you are attempting to run the gfortran command.
Your question isn't entirely specific on this point, (i.e. you could improve the question), but you hint that you were unable to add the appropriate PATH entry to the global environment variables, because you couldn't find the appropriate control panel applet? I know that this is often recommended as part of a MinGW setup, but the installer will not do it for you, because I, as the maintainer of mingw-get, don't consider that to be best practice; much better, IMO, to add it for each specific command window in which you need it to take effect, by running (once, at the start of each command prompt session) the command:
path %PATH%;C:\MinGW\bin
If you prefer, you may create a batch file to do this for you, along with any other initializations you wish to perform, (or better still, use MSYS as the working shell environment, in which case the PATH initialization is taken care of by the shell's own initialization scripts).

VB6 Service debugging with WINDBG

First and foremost, I'm an absolute newbie at debugging compiled code (until recently, I didn't even know it was possible!). I've successfully used the VC6 IDE to attach to a process but I can't get WinDbg to work...
The problem is that I have a VB6 service that I need to debug in a production server. I really don't understand much of how the .EXE is compiled because it is generated by an external tool called "NT Service Toolkit". The code I have is compiled into an ActiveX DLL that is used by this .EXE.
I've done everything I found on the internet to make the service debuggable with the VC6 IDE: compile without optimizations and generate symbols. When I use the VC6 IDE to attach to it, it works as expected... I can set breakpoints and everything works fine except for the annoying bug that kills the service when the debugging session stops.
When I try to use WinDbg however, I cannot set breakpoints; it fails with an error:
Unable to insert breakpoint 0 at 00000000`0046f715, Win32 error 0n299
"Only part of a ReadProcessMemory or WriteProcessMemory request was completed."
bp0 at 00000000`0046f715 failed
WaitForEvent failed
I've already downloaded symbols for windows' components from the symbol servers, as instructed in the documentation I could find, and I also included the PDB file for my service in the symbols folder...
One thing I noted is that I can get symbolic information when I check the "Noninvasive" box when selecting the process to attach to because I see things like wow64win!ZwUserGetMessage instead of just the address`offset syntax... but when I do, I also get this warning:
WARNING: Process <PID> is not attached as a debuggee
The process can be examined but debug events will not be received
In this case, I can set breakpoints, but when I try to run the service, it fails with:
0:000> g
^ No runnable debuggees error in 'g'
UPDATE
When I set a breakpoint in "Noninvasive" mode, detach, and reattach with "Noninvasive" un-checked, the breakpoints I set while in "Noninvasive" mode remain, and are hit successfully, but I can't set new breakpoints unless I repeat the whole process (detach, attach in "Noninvasive" mode and reattach). Does this make sense? What could I be doing wrong?
I found the problem. It was a conflict in the module names.
When WinDbg loads the modules for a process, it names them as the file where they live, just WITHOUT extension. Therefore, if, as in my case, two modules that must be loaded share the same name, say MyService.exe and MyService.dll, there will be two modules named MyService; in this case, WinDbg breaks the ambiguity by appending _<start-address> to the name, resulting in, for example:
...
MyService
MyService_11000000
...
(The following is a rationalization of what happened, since like I said, I'm pretty new to "attach to process"-type debugging and I lack knowledge of WinDbg internals...)
My problem was that the PDB file for the DLL (MyService.pdb), which was the module I was interested in debugging, was being matched to the EXE file's module (MyService), so the DLL's symbols were not loaded before trying to "restore" the breakpoints, resulting in the Win32 error 0n299 I was seeing earlier... It appears that the g command also tries to "set" the saved breakpoints, and since the symbols were not yet loaded, it would not let me start the thing after the default breakpoint either...
...
Or something like that... Obviously, if someone has a more accurate explanation, I'd very much like to be enlightened. :)
For the moment, I just changed the service EXE's name, and now everything works fine.

What are the differences between running an executable from a Windows Command Prompt versus from Windows Explorer?

EDIT: This is due to stupidity. It is a multiple monitor issue. It's just that from cmd.exe we always opened in the primary monitor, whilst from explorer, we always opened in the secondary. Thanks all for the help!
We hit a weird bug recently. We have a Qt + osg app that behaves differently if we run it from explorer than if we run it from a command line. Running from explorer is unusable, while running from command line (or by running from the explorer a simple batch file that calls the .exe) works as expected.
We suspect environment variables, because that's all we can think of. But the fact that it runs fine with a one line batch file seems to refute this. I'm not familiar enough with windows to know of any subtle differences in how it loads executables, nor where to look to find out.
Are there any other differences that could explain this? Does windows load different sets of user environment variables in each case? OS is Windows XP Service Pack 3.
The behavior experienced when running from explorer (double click program.exe) is consistent with a driver issue or improper OSG scene setup: image artifacts, flashing, and weird colors.
The behavior experienced when running the same executable from cmd.exe (or by double clicking a .bat file next to the .exe containing only a line to run the .exe) is the correct, expected behavior: the scene is correct, no flashing, etc.
To rule out potential library load path issues, try using dot-local DLL redirection.
Towards that end, create an (empty) file in the same directory as your executable and give it the same name as your binary, except with .local appended. I.e., if your binary is named yourbinary.exe, name that file yourbinary.exe.local. That will force the PE loader to first look in that directory to resolve LoadLibrary calls (and that includes DLLs loaded indirectly via system DLLs or via COM, no matter how many indirection levels are involved.) Place as many supporting DLLs (including Qt DLLs) in that directory. If you're using Qt plugins, also place the plugins directory there (or use a custom trolltech.conf.)
More details on dot-local redirection here, for example.
This thread looks like it might have the answer to your question:
http://forum.soft32.com/windows/Start-Run-Command-Prompt-ftopict353085.html
In short, I think it might be looking for your executable in different places depending on which method you attempt to use to run it. Perhaps you have 2 different versions hiding somewhere that explorer uses instead of the one you want?
You have not given enough details so I will give you a general answer. In order to use QT and its tools you need 2 environment variables. *QTDIR, and PATH * Make sure you have these variables set instructions are below. I have taken them from this site. See also this link for deployment on windows.
Setup the QTDIR environmental
variable.
1) Create a new System variable
called: QTDIR
a. Right click on My Computer -> Properties -> Advanced Tab ->
Environment Variables button
b. Find System variables -> New -> Type in "QTDIR" 2) Set the value to: C:\your\Qt\directory (NOTICE: No
trailing '\' character!!!)
Now, add the QTDIR on to your PATH
variable.
1) Edit your PATH variable, add onto
the end of it a ';' if one isn't
already on the end. 2) Now add on:
%QTDIR%\bin;
Example:
Before
PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;
After,
PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;%QTDIR%\bin;
That will make sure that our Qt
application(s) will be able to find
the Qt DLL files when you go to run
it.
I hope this helps.
Perhaps there is a difference caused by the way Explorer launches an executable vs directly running it from the console. I think that Explorer uses ShellExecute or ShellExecuteEx and I doubt that executing an application from a console or batch file does the same.
I would create a test app that tries some of the ShellExecute variants and use different parameters to see if the Explorer behavior can be reproduced in order to try to diagnose what parameters passed to ShellExecute might be causing the problem.
There is an interesting community note on the ShellExecuteEx page that may or may not be applicable:
ShellExecuteEx ignores the current input desktop. It always uses winsta0\default. Instead use ShellExecute or CreateProcess.
I would also investigate whether or not AppCompatFlags affect console executed applications (or see if any AppCompatFlags have been set for your application).

Resources