In attempting to upgrade some C++ software to run in Windows 7 I have experienced some problems in being able to create registry keys from scratch.
This is my code:
//
// Create a brand new registry key
//
LONG Registry::CreateRegister( std::string path )
{
HKEY hKey;
DWORD dwDisposition;
LONG openRes = RegCreateKeyEx( HKEY_CLASSES_ROOT,
path.c_str(),
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
&dwDisposition );
RegCloseKey( hKey );
return openRes;
}
In Windows XP, the RegCreateKeyEx function successfully creates the registry key, returning a success (0) value. In Windows 7 I am getting a return value of 5 (access denied) from the same function.
I used the regedit tool to ensure that my account has the necessary full permissions, but without success. Can anyone see where I might be going wrong, or if there are other gotchas and known issues I need to be aware of when using Visual Studio within Windows 7?
The software is currently written in Visual Studio 2003.
Thanks in anticipation.
Since Vista, access to certain areas of the registry has been locked down. The user must have "elevated" permissions. Try running your program with "Run as administrator" (right click it in Explorer).
See Registry Key Security and Access Rights and
RegCreateKeyEx
For parameter "hKey [in] A handle to an open registry key. The calling process must have KEY_CREATE_SUB_KEY access to the key."
This tuto help you Teach Your Apps To Play Nicely With Windows Vista User Account Control
and The Windows Vista and Windows Server 2008 Developer Story: Windows Vista Application Development Requirements for User Account Control (UAC)
Related
I have a 32 bit program which is running in two lanes in the same store. The program tries top open a registry key for query. The operating system is Windows 8.1 64 bit.
On one lane it succeeds, and on the other it fails and regopenkeyex returns 2. GetLastError returns 0.
The key it tries to open is under WOW6432Node.
The program is running under the same Windows user on both machines, the key exists on both. the UAC is set to "Never notify" (lowest), windows version is the same. Everything is supposed to be the same...
I am deliberately not specifying KEY_WOW64_64KEY because the code is supposed to work without it. But even when I do use it I get the same result.
What could be causing this?
The code:
rc = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
szKey,
0,
KEY_QUERY_VALUE,
&m_hKey);
Thank you.
You actually need to read about the functions you are using on MSDN. The registry functions return the error code directly, they do not use GetLastError!
2 is ERROR_FILE_NOT_FOUND so whatever you are hiding in szKey is not a valid subkey path.
WOW6432Node is a reserved key name that you really should not be using, use KEY_WOW64_32KEY if you need to access the 32-bit registry view in a 64-bit application. A 32-bit application reads keys under the WOW6432Node key by default.
Use Process Monitor to make sure you are accessing the correct key.
I have a problem with RegOpenKeyEx() function.
lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpSubKeyName, 0, KEY_READ, &hkMon);
Function return error code 2 (file not found), but this key exist in registry.
I try this on Windows 7 64-bit and Windows Xp 32bit.
When working with the registry you have to be aware of UAC registry virtualization (VirtualStore redirection for compatibility) and WOW64 (32/64 bit separation and registry redirection/reflection). Because of these features you sometimes end up in a different place in the registry and it will not match up with what you see in Regedit.
In cases like these the best thing to do is to use Process Monitor so you can see which key you are really accessing...
I have an old c++ application that needs to be modified to work with windows 7.
Problem is in creating a new folder and saving a file in that folder. This folder should be created in
c:\program files\myApp\data\newFolder.
This is function I use to create new folder and get errors:
if(!CreateDirectory(pathSamples,NULL)) //Throw Error
{
DWORD errorcode = GetLastError();
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorcode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL );
MessageBox(NULL, (LPCTSTR)lpMsgBuf, TEXT("Error"), MB_OK);
}
In XP this works, but in Windows 7 it doesn't. If I run application as administrator than the folder is created, otherwise "Access is denied" error is thrown.
My question is following:
Is there an option to make changes to the code so that the folder can be created in "program files" nad that files can be saved in this folder?
PS I saw this thread already but it doesn't answer my question.
Thanks,
Ilija
You have answered your own question. You need to be an administrator to write under Program Files in Windows 7.
Application data goes in a different area under Users//AppData etc...
You can always use the registry to select the location to write, so you can use the old area on XP and the new area on Vista and Windows 7.
With limited user access under Vista and later you don't want to be trying to put files in "Program Files" or any other non-standard place. You should really be using SHGetFolderPath to obtain the correct location from the system.
As others already wrote, %ProgramFiles% is not the right place to store user data. The correct solution obviously is to redesign the application so that it uses a different storage location.
As an alternative there exists a quick and dirty (!) fix: If the application does not have a manifest, User Account Control Data Redirection kicks in, transparently redirecting write requests to system areas to a safe place in the user profile. The redirection target is %LocalAppData%\VirtualStore\Program Files. Details about this kind of built-in virtualization can be found here.
So you could be done by simply removing the manifest from your application.
As #CashCow writes:
You need to be an administrator to
write under Program Files in Windows
7.
Best way to do this is to elevate your process (using ShellExecute "runas" or similar), and then create the folder.
Some ShellExecute examples:
http://en.wikipedia.org/wiki/User_Account_Control#cite_ref-kennykerr_4-2
http://blogs.msdn.com/b/vistacompatteam/archive/2006/09/25/771232.aspx
http://weblogs.asp.net/kennykerr/archive/2006/09/29/Windows-Vista-for-Developers-1320-Part-4-1320-User-Account-Control.aspx
It looks like it was enough to set permissions for this folder in installer and now it works normally.
Thanks everyone for your answers!
I have a CComModule that is calling RegisterServer (TRUE) on DllRegisterServer and UnregisterServer (TRUE) on DllUnregisterServer. The UnregisterServer is getting a 0x8002801C (Error accessing the OLE registry.) error and leaving around registery keys. I am using a Windows Server 2k8 R2 machine with UAC enabled. The components are x86 and I am using the 32 bit regsrv32.
Does anyone know why I would be getting this error?
You must run Regsvr32.exe from a command prompt that's elevated to administrator (i.e. UAC disabled). Make a shortcut on your desktop to "cmd.exe", right-click it and choose "Run as Administrator".
If you're using ATL and VS2008 then you can register your COM object per-user which writes the necessary registry keys to HKEY_CURRENT_USER instead of HKEY_LOCAL_MACHINE. You register your object by passing a special user switch to regsvr32, e.g.:
regsvr32.exe /i:user /n yourobject.dll
COM servers can be registered with the /RegServerPerUser switch.
I asked a similar question.
Environment - VS2008, Vista SP1.
I have written a process management service which can launch applications either in session 0 or the interactive console (usually 1). Please note this is NOT the normal mode of operation, it's for in-house debug purposes only. In the field, these processes will be safely hidden away in session 0. Security concerns do not apply.
Clearly people aren't reading this: security concerns do not apply. We have dozens of existing server apps (NOT services) written like this. We're not about to completely revamp these applications, we just need to be able to get at their inbuilt debug dialogs when running release versions in-house. I already know all about the canonical solution and pipes etc. If it was acceptable to add remote interfaces into all these apps, that's what we'd be doing.
I use the following code to do this:
ZeroMemory (&sui, sizeof(STARTUPINFO));
sui.cb = sizeof (STARTUPINFO);
sui.wShowWindow = pTask->GetWinStartState() ;
sui.dwFlags = STARTF_USESHOWWINDOW ;
ZeroMemory (&pi,sizeof(pi));
if (bInteractive)
{
HANDLE hToken = NULL;
DWORD dwSessionId = WTSGetActiveConsoleSessionId();
WTSQueryUserToken (dwSessionId, &hToken);
sui.lpDesktop = TEXT("winsta0\\default");
LPVOID pEnv = NULL;
DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
HMODULE hModu = LoadLibrary(TEXT("Userenv.dll"));
if (hModu )
{
if (CreateEnvironmentBlock (&pEnv, hToken, FALSE))
dwCreationFlag |= CREATE_UNICODE_ENVIRONMENT;
else
pEnv = NULL;
}
bCreatedOk = CreateProcessAsUser (hToken,
NULL,
(LPTSTR)(pTask->GetExeName()),
NULL,
NULL,
FALSE,
dwCreationFlag,
pEnv,
NULL,
&sui,
&pi);
}
else
{
bCreatedOk = CreateProcess (NULL, ... blah...);
}
This all works fine and I can run and monitor native processes both in the Vista service session and the console. Great. Cakes and ale for everyone.
So here's the problem. If I try to run a winforms (C#) app interactively like this, it appears to run, shows up in Process Explorer as running in session 1, but on the desktop... nada. No window appears at all. The process runs up and shuts down all fine, but no window ever appears. The exact same winform exe run from explorer also shows up in session 1, but this time appears on the desktop just fine.
Any ideas ?
Despite the evident hysteria there is nothing wrong with launching an application from a service into an interactive session provided it is done with the same privileges as the interactive user or lower. Since you are launching as the interactive user there can be no privilege escalation.
What you are doing does work. I suspect that the issue has something to do with your STARTUPINFO struct. You appear to be creating your sui on the stack but you don't show what you are doing with it. Are you initializing it to all 0s, if not you may be getting some garbage from the stack that is causing the window not to show or to show at some co-ordinates off the screen.
It looks as if you're trying to break Session Isolation. Like Mystere Man said, I don't think it's the right thing to do.
http://support.microsoft.com/?scid=kb%3Ben-us%3B165194&x=5&y=3 might be useful if you really want to do this. In the code sample over there, they seem to initialize more thing than you do.
EDIT If it's for debugging purposes, i-e VS 2008 step-in, just add a wait loop at the beginning, and launch VS with administrator rights. You can attach to your service process without encountering system isolation.
In a word, "don't".
Services typically run with reduced privileges and NOT as the current user. As such, in Vista+ they're not allowed to interact with the users desktop. On top of all that, services get a null Window Station.
You used to be able to check a box that said something like "Allow to interact with the desktop" but not anymore. It's bad practice.
Your best bet is to create a helper app that runs in the users context and communicates with the service via a named pipe, LRPC or a socket, then have your helper app launch the program for the user. This is the way most anti-virus now works.
Also, read this whitepaper from Microsoft on the subject. Services can't run in anything other than session 0.
NOTE: a little research seems to indicate that you need to duplicate the token using something like this:
DuplicateTokenEx(hTokenNew,MAXIMUM_ALLOWED,NULL,
SecurityIdentification,TokenPrimary,&hTokenDup);