Archive: RegDLL and ExecWait with /RegServer


RegDLL and ExecWait with /RegServer
Hi all,

My installer needs to register several COM objects. Some are DLLs and some are EXEs. I register the DLLs with RegDLL and I register the EXEs with ExecWait "myobject.exe /RegServer".

But some EXEs depends on other DLLs and if the DLLs are not in the PATH ExecWait fails and shows an error message like "DLL <dllname> not found in path". That's fine. I like this message to appear to let the user know that something has gone wrong.

But I have noticed that after the first call to RegDLL, the calls to ExecWait don't show any message (even when they are supposed to do so).

And If I use "ExecWait regsvr32" instead of RegDLL, the next wrong calls to ExecWait DO show the error message. So there is something strange with RegDLL. Any ideas?

Thanks
Marc


RegDLL does exactly the same thing. Check the log to see whether the DLL registration was successful.

Maybe RegDLL fails because the current path is wrong (you can set it using SetOutPath).


RegDLL works fine. What is wrong are the next calls to ExecWait. Here is another example:

Section "MySection"
RegDLL "myObject.dll"
ExecWait "a:\myapp.exe"
SectionEnd

When this script executes (with NO disk in drive A) it shows no error. And that's wrong: it must show an error.

If I comment the call to RegDLL or replace it with ExecWait "regsvr32 myObject.dll" then the call to ExecWait "a:..." shows an error ("There is no disk in A: Retry/Cancel/Ignore?")

Why I don't get this error if RegDLL is used?


If ExecWait/RegDLL is unable to exectue an application, the error flag is set (see documentation). It's up to the user to display a message or do something else.


If I execute ExecWait "a:\app.exe" it sets the error flag, you are right, but it also displays an error message. I see no need to check the error flag in order to show an error message because ExecWait already shows an error message.

I think I am not explaining myself clear enough. My question is not why do ExecWait fails. I know why it fails and I want it to show an error message for me. And it does. Just try ExecWait "a:\app.exe" and you will see an error.

Then place any (succesful) call to RegDLL in the line before ExecWait and execute. You will not see any error and I expected ExecWait "a:\app.exe" to show an error (like it did the first time). Why ExecWait doesn't show an error now?

(Maybe you think my question is "Why RegDLL doesn't show an error?" but that's not my question. I hope this time I have been clearer)

Thanks.


As I said before, ExecWait does not show an error message. If you execute regsvr32 and it fails it is regsvr32 that displays a message, not ExecWait.

NSIS commands like RegDLL and ExecWait never show error messages. That's because many users want to add their own error handling and messages. Instead of showing a message they set the error flag so the user can decide what to do.


What you say is not completely true. Try ExecWait "a:\app.exe" and you will see that it displays an error message. Isn't it?

I know that ExecWait doesn't show always an error message when an error occurs, but sometimes it does, like the previous example or when ou call an EXE/DLL that depends on another DLL that is not in the PATH.

Am I right?


No. ExecWait "a:\app.exe" sets the error flag, it does not show an error message.

It is indeed true that Windows shows an error message when a binary is executed and a dependency is missing. However, this is not related to ExecWait.


Ok, so we agree that an error message appears when a binary is executed and a dependency is missing (it doesn't matter for me if Windows is the one showing it or ExecWait).

What I am trying to tell you is that if your script performs a call to RegDLL then any other call to ExecWait that executes a binary and a depencency is missing doesn't show up any error message. I just want to know why.


Different tricks to solve dependency problems with RegDLL have been used over different versions of NSIS. One of them might have an affect on the program you're trying to execute. Which version of NSIS are you using?


I am using NSIS20b3.

I want you to notice that the call that I made to RegDLL has noting to do with the call that I made to ExecWait. ExecWait has dependency problems but the reason has nothing to do with the call that I made to RegDLL. My question is not why ExecWait is failing or why RegDLL is failing. RegDLL is not failing, and ExecWait is failing because I make it on purpose.


Some of the old tricks in RegDLL could have made the difference, but you're using b3 which doesn't have any of them.

Maybe the code in the DLL registration server does this. Using RegSvr32 it runs in another process, but using RegDLL it runs in the installer's process which means it can change things like the working directory. Try forcing the working directory to something else using SetOutPath after using RegDLL.


I have tried SetOutPath "c:\" just after RegDLL and there is no difference. All the subsequent calls to ExecWait that should show a message (or should force Windows to show an error message) doesn't show it.


Well, then it must do something else. Without the source of the DLLs you are registering, I can't know for sure. But that should be the only difference between calling regsvr32 and using RegDLL.


I have seen that it only happens when using RegDLL with a COM DLL that uses MFC. I have sent you a project like that: it's a simple VC++ 6.0 ATL COM AppWizard project with just one COM object. I have modified the settings to use MFC: "Use MFC in a Shared DLL" in "General" tab and I have added a couple of includes in StdAfx.h.

Compile it in Debug mode and try:

RegDLL "C:\SimpleCom.dll"
ExecWait "a:\test.exe"

You will not see any error message like "There is no disk in drive A:". If you remove the RegDLL line, you will see the message.


Ah, COM... I am pretty sure I've read some where about COM DllRegisterServer using SetErrorMode, but I can't find that page now. If I remember correctly, that could be the source of the problem. SetErrorMode can control both of the messages you have described, so it must be it. You can call SetErrorMode using the System plug-in or use the latest CVS version. In the latest CVS version SetErrorMode is set back to 0 after RegDLL because it's set to something else before it.


Solved! Thanks!

It was indeed related to SetErrorMode. I placed the next line after RegDLL:

System::Call 'kernel32::SetErrorMode(i) i (0) .r1'

and then the next calls to ExecWait displayed the error I wanted. I also checked (through the return value in $1) that RegDLL switched the error mode to

SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS

That's way no error was displayed.
Thanks again.

BTW: Is there a way to tell System::Call that I want to ignore the return value? I don't want it to modify any of my registers. I know I can save the value of a register in the stack, but it would be better if I could just tell System:Call that I want no return...


You can simply not write the return value part. Use:

System::Call "kernel32::SetErrorMode(i 0)"