4

Sometimes when I uninstall the setup (made with WIX) the service remain marked for deletion, and the user must restart the machine to install again. How could I verify that the service is marked for deletion and tell to the user to restart the computer before making other installation?

Yan Sklyarenko
  • 31,557
  • 24
  • 104
  • 139
ctescu
  • 350
  • 4
  • 16

6 Answers6

5

Generally speaking, this scenario occurs when something remains latched onto that service, preventing Windows from removing its configuration in the registry. (In most cases, it's simply the Services applet -- services.msc -- left open by accident in the background.)

For detection, I suggest you read up on CreateService and other Service API. For example, you'll receive ERROR_SERVICE_MARKED_FOR_DELETE upon calling CreateService if the service is marked for deletion.

Regarding your proposed reboot solution... Windows has advanced far enough to not require a reboot for nearly any reason. Unless you're installing specialized kernel drivers, you do not need to reboot. Don't be lazy! Keep the user in mind! I recommend altering your installer logic to detect potentially conflicting running programs, like the Services applet, and suggest closure.

Rafael Rivera
  • 871
  • 8
  • 22
2

Here is an SO post that may help you. Although the original question is for service installation, the answer also covers uninstalls and statuses.

How to install a windows service programmatically in C#?

Here is an article that explains why you may receive the "marked for deletion" message in the first place and how to get around it.

http://weblogs.asp.net/avnerk/archive/2007/09/05/windows-services-services-msc-and-the-quot-this-service-is-marked-for-deletion-quot-error.aspx

EDIT

Per Christopher Painter's comment, I'm updating this answer for intentions to promote best practices. While receiving the "marked for deletion" message has more often been (in my experience) the result of having the services.msc console than unreleased resources, writing a Custom Action to reboot is not the best way.

To schedule reboot after WiX processing, use WiX XML (explained how to with Wix# here) as follows:

<?xml version='1.0' encoding='windows-1252'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>
    ...
    <InstallExecuteSequence>
        <ScheduleReboot After="InstallFinalize"/>
    <InstallExecuteSequence>
</Wix>
Community
  • 1
  • 1
bitxwise
  • 3,534
  • 2
  • 17
  • 22
  • It would be nice if the -1 was explained – bitxwise Mar 17 '11 at 00:04
  • WiX uses Windows Installer which has support for installing/uninstalling services. Any attempt to write custom actions in the installer is considered a bad practice. See: http://robmensching.com/blog/posts/2007/8/17/Zataoca-Custom-actions-are-generally-an-admission-of-failure – Christopher Painter Mar 04 '12 at 21:16
  • Thanks for the explanation. I agree with many points made in the blog post you linked, however I'm not entirely in agreement with your interpretation of the post. Not trying to start a war, but the author of the post emphasized that custom actions are GENERALLY discouraged for the listed reasons of why set up developers would write custom actions. That said, the OP should probably use in the tag via WiX XML instead of writing a custom action for better practice. I'll update the answer to reflect this. – bitxwise Mar 05 '12 at 20:17
  • See his reason #a ( reinventing the wheel ). Any discussion of programatically installing a service in c# is by definition a violation of this principal. Windows Installer has the ServiceInstall table to do this work. AFAIK Windows Installer's builtin service standard actions handles services that are in a state of marked for deletion. – Christopher Painter Mar 05 '12 at 20:55
  • I think in most shops it comes down to 1. the amount of effort/time/skill a developer and 2. priorities. A case could be made either way. I've written things that may have already existed, but written them with improvement. Just because an interface already exists doesn't mean innovation should die to it. I'm not saying the OP is trying to be innovative. I'm simply saying I didn't entirely agree with the hard, fast rule of your interpretation. Thanks again for the insight though. – bitxwise Mar 06 '12 at 01:24
  • I think the context that might help you understand Rob/my philosphy is that Windows Installer is a declarative not imperative programming language. While I understand what you are saying, installers aren't a general purpose programming language where we are trying to innovate. The focus is on the robustness of the installation and injecting custom actions almost always decreases that outcome. – Christopher Painter Mar 06 '12 at 02:06
  • In other words, the assumption is that the Windows Installer team at Microsoft has had the last 13 years to get Service Installation correct and that any setup developer who tries to do it himself will do an inferior job. From a matter of experience ( 16 years writing installers ) I accept this assumption as almost fact. – Christopher Painter Mar 06 '12 at 02:12
  • 1
    So rather than solve a problem in the available time you think one should spend that time attempting to learn an new technology and fail to deliver? Development is often idealism vs pragmatism. – John Nicholas Nov 05 '12 at 14:43
2

I can't find an API way to do it (which doesn't involve calling either CreateService or DeleteService, both having undesired side effects), but HKLM\SYSTEM\CurrentControlSet\Services\ServiceName containing a DeleteFlag=1 (REG_DWORD) value seems to be pretty indicative of this unfortunate state.

Ilya
  • 5,533
  • 2
  • 29
  • 57
1

I also searched for the solution that to determine whether a service is existent or marked for deletion even though OpenService succeeds. I also found that the error code ERROR_SERVICE_MARKED_FOR_DELETE returned by ChangeServiceConfig. So, here is the way to check whether a service is marked for deletion through ChangeServiceConfig with C/C++:

BOOL SetDisplayName(SC_HANDLE schService, LPCTSTR lpDisplayName)
{
    return ChangeServiceConfig(schService, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, lpDisplayName);
}

//
// You need to obtain the hSCManager through OpenSCManager first
//
SC_HANDLE schService = OpenService(hSCManager, pszServiceName, SERVICE_CHANGE_CONFIG);
if (schService != nullptr)
{
    //
    // obtain the display name of a service
    //
    TCHAR szDisplayName[0x1000] = { 0 };
    DWORD cchDisplayName = ARRAYSIZE(szDisplayName);
    bResult = GetServiceDisplayName(schService, pszServiceName, szDisplayName, &cchDisplayName);
    if (bResult)
    {
        //
        // change the display name to the original display name
        //
        bResult = SetDisplayName(schService, szDisplayName);
        if (!bResult)
        {
            DWORD dwError = GetLastError();
            switch (dwError) {
            case ERROR_ACCESS_DENIED:
            case ERROR_CIRCULAR_DEPENDENCY:
            case ERROR_DUPLICATE_SERVICE_NAME:
            case ERROR_INVALID_SERVICE_ACCOUNT:
                break;
            case ERROR_INVALID_HANDLE:
                break;
            case ERROR_INVALID_PARAMETER:
                break;
            //
            // the service is marked for deletion
            //
            case ERROR_SERVICE_MARKED_FOR_DELETE:
                break;
            default:
                break;
            }
        }
    }
}
else
{
    DWORD dwError = GetLastError();
    switch (dwError) {
    case ERROR_ACCESS_DENIED:
        break;
    case ERROR_INVALID_HANDLE:
        break;
    case ERROR_INVALID_NAME:
        break;
    case ERROR_SERVICE_DOES_NOT_EXIST:
        break;
    default:
        break;
    }
}
xenophōn
  • 211
  • 2
  • 7
0

In my case the service was marked for deletion after uninstalling it, because I didn't dispose an object properly (rabbitmq-connection in my case).

This is not a direct answer on the question but may help to solve the root-issue of it.

Peter Widmer
  • 122
  • 10
0

Are you using the ServiceControl element/table to stop the service during the uninstall? If so, Does your service successfully stop? If not, look into what's going on inside your service to make sure it releases all of it's resources and shuts down gracefully when requested.

You shouldn't need to be writing any custom actions to programatically call the SCM API. Windows Installer should be able to handle this for you.

Christopher Painter
  • 54,556
  • 6
  • 63
  • 100
  • This doesn't answer the question. Some services just cannot be stopped, e.g. when a service belongs to a device driver that cannot be safely unloaded. – Ilya Mar 04 '12 at 12:07
  • The point that you missed was that this isn't an installer issue and to look inside your service code. If a service cannot be stopped then don't ask the installer to stop it. – Christopher Painter Mar 04 '12 at 14:06
  • Your uninstaller's goal is to delete the service. The uninstaller should stop it first, but if it's unstoppable, then it shall delete it anyway (which will mark it for deletion at system shutdown). If the user elects not to restart at the end of your uninstaller, and then goes to reinstall, he'd potentially face a problem when the installer will try creating the service again. – Ilya Mar 05 '12 at 15:46