Question

I'm writing a C++ DLL to connect to a MySQL database. Among other things, it tries to add an exception to the Windows firewall list so it can connect to the remote MySQL server via TCP (port 3306). When I try to add the port, I get an HRESULT of E_ACCESSDENIED, unless I run the program as an administrator.

I would like to prompt the user for an administrator password, but only when the port is not already in the exception list. This means that I can't just create a UAC manifest, since setting level="requireAdministrator" would always prompt for an administrator password, as far as I can tell. Can I conditionally bring up an administrator prompt?

Was it helpful?

Solution

As per your request, I'm making an answer out of my comment.


The correct way to do this is to let your DLL's users handle the situation themselves. As an API developer it is definitely not your job to bother about things like a firewall: either the API user and/or end-user made sure it works, or you fail gracefully. As others have said, there are many reasons why the end-user couldn't be able to answer an UAC request (eg. on a headless server) so you mustn't rely on your DLL being used in an interactive context. It's simply not your responsibility.


If you really must go on with your original idea (which, I stress again, is a bad idea IMHO) my best bet would be to split your DLL in two:

  • The DLL itself, without any manifest so it can run under any user,
  • and a separate .exe with a manifest that requires admin privileges, that will be run by your DLL only when need arises.

Just make it a requirement that both are stored in the same directory so you can easily find your DLL's (and thus the .exe's) directory with GetModuleFileName.

Others have pointed out runas or RunDll (which are equally valid answers IMO) but I'm more of a Unix-y type hence my suggestion of a separate binary altogether. I find it much easier to maintain in the long run.


A solution "in-between" would be that your DLL doesn't bother with the firewall at all (as it should) but you provide a completely separate tool (.exe with manifest) that helps your users setup the firewall correctly when they need to. This may be the best solution: clean design (separation of responsibility) and still you provide all the required tools to the users.

OTHER TIPS

There is no way to elevate the privileges of an application once it is started. In your case, one work around is to use ShellExecuteEx to launch a small external application with elevated privileges and add the firewall exception there. Once the exception has been added, do whatever cleanup is necessary and exit. You can then wait for the application to finish before you continue execution.


Much Kudos to Cody Gray for pointing out why RunDll should not be used and the additional blog entry by Raymond Chen containing more information about issues with RunDll

You can relaunch your program using ShellExecuteEx with "runas" verb whenever you get E_ACCESSDENIED. Something like this

        wchar_t szPath[MAX_PATH];
        if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)))
        {`enter code here`
            // Launch itself as administrator.
            SHELLEXECUTEINFO sei = { sizeof(sei) };
            sei.lpVerb = L"runas";
            sei.lpFile = szPath;
            sei.lpParameters=L"admin";
            sei.nShow = SW_NORMAL;

            if (!ShellExecuteEx(&sei))
            {
                DWORD dwError = GetLastError();
                if (dwError == ERROR_CANCELLED)
                {
                    ExitProcess(0);
                }
            }
            else
            {
                ExitProcess(0);
            }
        }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top