I've placed a custom entry in the win.ini file in the windows directory , called LoginCount under a custom [Login] section and increment it each time the main view is loaded. In the Form Create event I access the win.ini and check for its value and if it's reached 1000 I show a message. But a very weird thing has happened. After reaching 1000 I manually set its value back to 0. But it still shows 1000. I checked the path, in case the application refers to some other win.ini file somewhere in the system, but it's C:\Windows\win.ini. Here's the code:
procedure TfMain.FormCreate(Sender: TObject);
var
winIni: TIniFile;
windir_buf: array [0 .. 144] of Char;
WINDIR: string;
loginCount: integer;
begin
GetWindowsDirectory(windir_buf, sizeof(windir_buf));
WINDIR := StrPas(windir_buf) + '\';
ShowMessage(WINDIR+'win.ini');//Shows C:\Windows\win.ini
winIni := TIniFile.Create(WINDIR + 'Win.ini');
loginCount := winIni.ReadInteger('Login', 'LoginCount', 1);
ShowMessage(IntToStr(loginCount));//Shows 1000 although it's 0 in the actual file.
end;
Any idea why?
This is probably due to file system virtualization. You are running a virtualized process under Vista or later and don't have write access to the Windows directory.
There's no point debugging this. The Win.ini file has been deprecated for nearly 20 years. You should:
Add a manifest to suppress virtualization.
Ensure that you run with UAC enabled, and as standard user.
Store your file to an appropriate folder. Under the user profile is the obvious place. That makes it a per-user setting. For system-wide settings you store to the ProgramData folder.
If you simply cannot bring yourself to stop using Win.ini then you'll still need to add a manifest with the requireAdministrator option.
If you are going to use Win.ini (and I cannot express how much I abhor the fact you contemplate doing so) then you should use GetProfileString and SetProfileString. Since the file is shared you need to access it with functions that synchronize that access.
Related
I have the two following methods and I am using them to store a special value locally and be able to access it on application restart:
(Store value locally:)
private void SaveSet(string key, string value)
{
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
ISharedPreferencesEditor prefEditor = prefs.Edit();
prefEditor.PutString(key, value);
// editor.Commit(); // applies changes synchronously on older APIs
prefEditor.Apply(); // applies changes asynchronously on newer APIs
}
(Read it again:)
private string RetrieveSet(string key)
{
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
return prefs.GetString(key, null);
}
This works perfectly. Now is it possible to access and edit this Shared Preferences externally? Unfortunately, I cannot find any file when searching in folder
Phone\Android\data\com.<company_name>.<application_name>\files
nor anywhere else. I want / try to edit this value from my computer, after connecting the phone to it. Is this possible?
Alternatively: Can anyone maybe show me how to create a new file in the given path above, write/read it programmatically and how it stays there, even if application is closed / started again? So I can then edit this file with my computer anyhow?
I tried it with the following code, but unfortunately it doesn't work / no file is created or at least i cannot see it in the given path above:
//"This code snippet is one example of writing an integer to a UTF-8 text file to the internal storage directory of an application:"
public void SaveValueIntoNewFile(int value)
{
var backingFile = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "newFile.txt");
using (var writer = System.IO.File.CreateText(backingFile))
{
writer.WriteLine(value.ToString());
}
}
Would be very happy about every answer, thanks in advance and best regards
What you're looking for is where Android stores the Shared Preference file for applications that make use of it's default PreferenceManager.
I'd refer to this SO post which answers your question pretty well
SharedPreferences are stored in an xml file in the app data folder,
i.e.
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PREFS_NAME.xml
or the default preferences at:
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PACKAGE_NAME_preferences.xml
SharedPreferences added during runtime are not stored in the Eclipse
project.
Note: Accessing /data/data/ requires superuser
privileges
A simple method is to use Android Device Monotor,you can open it by clicking Tools--> android-->Android Device Monotor...
For example:
The path in my device is as follows:
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PACKAGE_NAME_preferences.xml
And we notice three buttons in the upper right corner of the picture.
The first one is used toPull a file from the device,the second one is used to Push a file onto the device,and the last one is used to delete the preferences.xml file.
So we can pull the preferences.xml file from current device to our computer and edit it as we want, and then push the updated preferences.xml to the folder again.Then we will get the value of preferences.xml file .
I'm trying to encapsulate a number of repetitive installation tasks into a private setup program. The program is for in-house use setting up custom, single purpose systems for industrial users. I need Administrator privileges to tweak a number of Windows settings for our environment and then I need to set some current user settings for the application software packages to use.
Is it possible for a Windows program (in plain C, created with Visual Studio 2017) that uses requireAdministrator in its manifest to revert to the user that started the program when the admin privilege is no longer needed? I've seen this done in linux, but have been unable to find any examples (or even mentions) of this being done in Windows. Help? Please?
You don't.
A way given awhile back is to track down the running instance of explorer, and spawn a remote thread in it (with CreateRemoteThread) that does what you want. However CreateRemoteTherad is touchy and what if explorer isn't running?
If you know the username you can use CreateService() to create a service that runs as that user and ServiceStart() to start it but that's its own pain and now the code has to deal with no access to the desktop. Getting code running as the user on the desktop involves digging into the undocumented.
The correct design is to use two executables, the first with asInvoker that starts the second with requireAdministrator using the API call ShellExecuteEx, waits for it to finish, checks the exit code, and on success does the individual user steps.
There is no way to de-elevate a running process. You might be able to lower your rights a little bit but not all the way. Even if it was possible, you would have to hardcode the list of groups and privileges to remove/disable in your token because I don't believe there is a API to restrict a token just like UAC does.
There are many half-assed solutions out there to start a un-elevated child process:
Using the Task Scheduler
IShellDispatch2::ShellExecute in the "main" Explorer.exe instance
CreateProcessAsUser with the token from the "main" Explorer.exe instance
Bootstrapper instance elevates another instance and communicates back to parent when it needs to perform un-elevated actions
Take advantage of Explorer bug and simply execute "%windir\Explorer.exe" "c:\path\to\myapp.exe"
All of those solutions have issues related to:
Explorer might not be running (custom shell or Explorer crash)
Explorer is running elevated
Non-admin users elevate with a different administrator account with a different SID
RunAs.exe has been used and your parent process is not the same as the "main" logon session nor Explorer.exe
Final Answer
When I was finally able to install a clean Windows 10 on a spare system for testing and create a pure non-admin account to test with, the new, improved answer did not work. I added a GetUserName() call after the impersonate and wrote it to the debug log to learn that the user name was the same before and after the impersonate, even though all of the functions returned success. I can only assume that the GetShellWindow() [I also tried GetDesktopWindow(), just in case] returned a handle to an explorer/shell under the Admin context. So, now I'm using a cleaned up (to make it plain C) version of the GetConsoleUserToken() function I found here: https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/17db92be-0ebd-4b54-9e88-a122c7bc351d/strange-problem-with-wtsqueryusertoken-and-impersonateloggedonuser?forum=windowsgeneraldevelopmentissues This is working on both my development system AND on the clean Windows 10 non-admin user. The function actually searches all running processes for the explorer.exe that belongs (is attached?) to the console session and while debugging it, I did see that it find more than one explorer.exe process, but only ONE is the right one.
Thanks for all of the suggestions and comments! They gave me good ideas and put me on a path that allowed me to use better search terms to find what I needed.
To Microsoft: This seems quite unnecessarily complex to do something that should not be difficult for an administrator-level process to do.
New, Improved Answer:
Following the suggestion made by eryksun, I created the following example function that shows all of the steps my program needed to get the current user key opened. My program has requireAdministrator in the manifest, if yours does not, you might need to make changes or additions to my sample. Here is what is working perfectly for me (but apparently not on a clean machine under a non-admin account):
BOOL ChangeHkcuSettings( void )
{
BOOL bResult = FALSE; // HKCU was not accessed
HWND hwndShell;
DWORD dwThreadId;
DWORD dwProcessId;
HKEY hKeyUserHive;
HANDLE hToken;
HANDLE hProcess;
hwndShell = GetShellWindow();
dwThreadId = GetWindowThreadProcessId( hwndShell, &dwProcessId );
hProcess = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, dwProcessId );
if( NULL != hProcess )
{
if( OpenProcessToken( hProcess,
TOKEN_QUERY
| TOKEN_DUPLICATE
| TOKEN_IMPERSONATE,
&hToken ) )
{
if( ImpersonateLoggedOnUser( hToken ) )
{
if( ERROR_SUCCESS == RegOpenCurrentUser( KEY_ALL_ACCESS, &hKeyUserHive ) )
{
// ... use the user hive key to access necessary HKCU items ...
RegCloseKey( hKeyUserHive );
bResult = TRUE; // HKCU was accessed
}
RevertToSelf();
}
CloseHandle( hToken );
}
CloseHandle( hProcess );
}
return bResult;
}
Thanks again to eryksun for the suggestion!
ORIGINAL ANSWER: I think I found another way that will work for me... Instead of using HKEY_CURRENT_USER (which will be the Administrator) the admin account can open the specific user's registry key(s) under HKEY_USERS instead. I will need to find the appropriate user's SID, but my setup knows the user's name (and password, for setting auto logon), so I think this is do-able. For me, this is much easier, since all of the code already exists in a single program that formerly write EVERYTHING to HKEY_LOCAL_MACHINE, which was easy and worked great. Trying to be "correct" is much more work, perhaps more than it should be! :-(
I have an Access database was created, it's working perfectly. After successfully splitting the database to back and front end I shared the _be file in shared drive and front end file in my local, I tried to run the front end and I am getting error when macro is executing:
I even put the back and front end in my local machine but still I am getting the error, I tried to remove the macros but still getting errors since I am not the one developed this application.
Dim fdb As Database
Dim utab As Table, otab As Table
DBEngine.SystemDB = "System.mdw"
Set fdb = CurrentDb()
Set utab = fdb.OpenTable("Users")
Set otab = fdb.OpenTable("Organization")
After tracing the function findorg() I found this error
Item not found in the collection
For this line
Set utab = fdb.OpenTable("Users")
I tried to change the code to be
Set utab = fdb.Rescordsets("Users")
But still I am getting the same error.
Note that I am using back and front end, I have all my tables linked.
"Users" table is not linked in the database in which you are running this code. Link it again and check, check for all tables that should also be linked.
i was able to solve the issue by using DAO 3.6 object library, as i found that the current database application is using DAO 2.5, so i recompiled the application and fixed all errors. also i managed to rename the functions that's been called by macro, i found that in macro when i write calling function as findorg() will not work, but in function name i specify "findorg" between double quote, which run successfully.
I'm trying to create a connection to an Interbase ToGo database, at runtime, in a unit that does not have a form.
MyConnection := TSQLConnection.Create(nil);
strTestPath := TPath.Combine(TPath.GetDocumentsPath, 'ISHMAEL.GDB');
MyConnection.Params.Add('Database=' + strTestPath);
MyConnection.DriverName := 'IBToGo';
MyConnection.Params.Add('User_Name=SYSDBA');
MyConnection.Params.Add('Password=masterkey');
MyConnection.Open();
Everything seems fine as I step through, until I try and open the connection. The open raises an I/O error, saying that database.gdb isn't found. I called the database ISHMAEL, so is something else happening, or is the error message misleading?
Ideally, I'd like to get all of my database calls (and really, most if not all of the business logic) out of the forms and into some data access and business object units, but am having difficulty finding examples.
All of the examples and tutorials I have found focus on dropping controls on a form, and using visual live bindings.
Can this be done inside a non-form unit?
Thanks!
I'm using IsloatedStorage in a Silverlight app to log information on the client, and I added a function to clear the log file. However, I have had problems with the two approaches I tried:
Approach one: use
IsolatedStorageFile.DeleteFile("log.log");
Result: This fails and returns an "[IsolatedStorage_DeleteFile]" error (No other info). The function works fine on test files, e.g. DeleteFile("test.txt"), but refuses to delete the log. I though that perhaps the log is being used, and tried to close it with
IsolatedStorageFileStream.close()
But this returns a different error "[IsolatedStorage_StoreNotOpen]". I know it is open as the previous line of code successfully logs a message.
Approach Two: Reopen the log file using the Truncate file mode,
_storageFileStream = new IsolatedStorageFileStream(logfilename, FileMode.Truncate, FileAccess.Write, FileShare.ReadWrite, _storageFile);
According to MSDN, Truncate "Specifies that the operating system should open an existing file. Once opened, the file should be truncated so that its size is zero bytes." However, it opens my log file and fills it with blank space! The filesize is left identical, the next log message is appended to the end of all of the space.
I've found a way to do this, not by closing but by disposing:
IsolatedStreamWriter.Dispose();
IsolatedStorageFile.DeleteFile("log.log");
IsolatedStorageFileStream = new IsolatedStorageFileStream(logfilename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite, _storageFile);
Didn't get the 'truncate' approach to work, but no need now.