Setting global environment variables programmatically - c++

I need to set an environment variable programmatically.
Microsoft provides documentation for that here. You just need to create a new value in the registry under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment. This part works fine.
The problem is that these changes only come into effect after logging out and logging in again.
To circument this they propose to execute this little piece of code:
if (!SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
(LPARAM) "Environment", SMTO_ABORTIFHUNG,
5000, &dwReturnValue))
{
... take action in case of failure
}
I did exactly this, SendMessageTimeout returns TRUE, but at least under Windows 10 it has no effect. A newly opened command prompt window still won't show the newly created variable.
I also tried to run this piece of code in an elevated process, but the result is still the same.
But when I use system applet for changing environment variables, my newly created variable shows up and when I click OK on the applet and when I open another command prompt, then the variable is there.
Any thoughts?

The problem was solved by calling explicitly the wide version of SendMessageTimeout and sending the "Environment" as wide string:
SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
(LPARAM)L"Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);

As Michael found out, the string width needs to match the A/W function type. WM_SETTINGCHANGE is in the < WM_USER range and will be marshaled by the window manager.
You can use the TEXT macro to create code that works for everyone everywhere if you don't want to hardcode the function name suffix:
SendMessageTimeout(
HWND_BROADCAST,
WM_SETTINGCHANGE,
0,
(LPARAM) TEXT("Environment"),
SMTO_ABORTIFHUNG,
5000,
&dwReturnValue
);

Related

c++ enable accessibility inspection in Google Chrome

I'm trying to access text of Google Chrome's webpage to read it and offer some actions (for example, remind). Everything works good, but I need to enable accessibility inspection programmatically. I use this code:
wchar_t className[100];
GetClassName(hwnd, className, 100) == 0 || wcscmp(className, L"Chrome_WidgetWin_1");
CComPtr<IAccessible> pAccMain;
HRESULT hr = ::AccessibleObjectFromWindow(hWndChrome, 1, IID_IAccessible, (void**)(&pAccMain));
CComPtr<IAccessible> pAccMain2;
::AccessibleObjectFromWindow(hWndChrome, OBJID_CLIENT, IID_IAccessible, (void**)(&pAccMain2));
And nothing happens until I run browser with --force-renderer-accessibility parameter or manually change accessibility settings located in chrome://accessibility.
What am i doing wrong?
Found this info: "Chrome calls NotifyWinEvent with EVENT_SYSTEM_ALERT and the custom object id of 1. If it subsequently receives a WM_GETOBJECT call for that custom object id, it assumes that assistive technology is running". Does anybody know how to implement this?
Use SetWinEventHook, e.g.
HWINEVENTHOOK hook = SetWinEventHook(EVENT_SYSTEM_ALERT, EVENT_SYSTEM_ALERT,NULL, WinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT)
Then in your WinEventProc, you'll be given a hWnd, idObject, and idChild when Chrome sends the EVENT_SYSTEM_ALERT.
If idObject == 1 then call AccessibleObjectFromEvent(), passing it those hWnd, idObject, and idChild arguments.
AccessibleObjectFromEvent will then send the WM_GETOBJECT. From the docs::
Applications never send this message directly. Microsoft Active Accessibility sends this message in response to calls to AccessibleObjectFromPoint, AccessibleObjectFromEvent, or AccessibleObjectFromWindow.
By using AccEvent I can see that Google Chrome only appears to send the EVENT_SYSTEM_ALERT when it starts, e.g. opening a new tab doesn't trigger it, so you'd need to have done the SetWinEventHook() before Chrome is launched.

I want to restart my application on restart of the system

I use GetModuleFileName to get the absolute path to my application, open the RunOnce registry key using RegOpenKeyEx and set a value using RegSetValueEx.
if (RegOpenKeyEx (HKEY_CURRENT_USER,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce",0, KEY_SET_VALUE, &hk1) == ERROR_SUCCESS)
{
RegSetValueEx(hk1, // subkey handle
"", // value name
0, // must be zero
REG_SZ, // value type
(LPBYTE) &path, sizeof(DWORD)); // length of value data
RegCloseKey(hk1);
}
However my application does not start after a system restart.
There are a few methods:
Place your application in your start-up folder. This is a very easy method. When your system (PC) will be restarted, the application will get started (You need to login for this);
Use windows task planner;
Make the application an service.
I prefer the last option if it always needs to run. But you will need to add service handling.
You can create a task using Task Scheduler to run your application when the computer starts.
Open Task Scheduler by clicking the Start button , clicking Control Panel, clicking System and Security, clicking Administrative Tools, and then double-clicking Task Scheduler.‌ If you're prompted for an administrator password or confirmation, type the password or provide confirmation.
Click the Action menu, and then click Create Basic Task.
Type a name for the task and an optional description, and then click Next.
Click When the computer starts, and then click Next.
To schedule a program to start automatically, click Start a program, and then click Next.
Click Browse to find the program you want to start, and then click Next.
Select the Open the Properties dialog for this task when I click Finish check box and click Finish.
In the Properties dialog box, select Run whether user is logged on or not, and then click OK
Source:
Windows 7 - Schedule a task
PS: You must be logged on as an administrator to perform these steps
There are a number of things to keep in mind when using the solution you opted for:
The application does not start when the system starts but rather when the current user logs on.
If you write to the RunOnce key the operation will be performed only once. If you want your application to always start when the user logs on you should instead use the Run key.
In addition to the above, if you want to create a value you will have to give it a name. From the documentation of the lpValueName parameter for RegSetValueEx:
If lpValueName is NULL or an empty string, "", the function sets the type and data for the key's unnamed or default value.
The default (unnamed) value is the one that shows up as (Default) when using regedit. To get this to work you will have to provide a name for the value. This should be unique so that it does not conflict with other values under that key.
On a less technical note, implementing an auto-start feature for an application should only be done after thorough consideration.
You are passing the wrong parameter values to RegSetValueEx(). You need to use it like this instead:
TCHAR path[MAX_PATH+1] = {0}
GetModuleFileName(NULL, path, MAX_PATH);
if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce"), 0, KEY_SET_VALUE, &hk1) == ERROR_SUCCESS)
{
RegSetValueEx(hk1, // subkey handle
TEXT("MyApp"), // value name
0, // must be zero
REG_SZ, // value type
(LPBYTE) path,
(lstrlen(path)+1) * sizeof(TCHAR)); // length of value data, in bytes
RegCloseKey(hk1);
}

Setting wxTE_PASSWORD later

I created two wxTextCtrl. One for log in (loginTxt) and another for password (pwdTxt) and both have readable default message.
I also installed wxEVT_LEFT_DOWN event so that when user click on either loginTxt or pwdTxt the default message will be set to empty string
Is it possible to set wxTE_PASSWORD style to the pwdTxt later? If it's possible, how can I do that?
I read wx.chm and it say,
"Note that alignment styles (wxTE_LEFT, wxTE_CENTRE and wxTE_RIGHT) can be changed dynamically after control creation on wxMSW and wxGTK. wxTE_READONLY, wxTE_PASSWORD and wrapping styles can be dynamically changed under wxGTK but not wxMSW. The other styles can be only set during control creation.".
I am writing my application on MS Windows with wxWidgets 2.9.3
You cannot change it later on Windows, since Microsoft's control does not support that. If you really need to, I suggest creating 2 different controls and show/hide the appropriate one.
Windows-only solution, probably will be useful:
void Sample::OnBUTTONClick( wxCommandEvent& event )
{
#if defined(__WXMSW__)
HWND hWnd = (HWND)m_Text->GetHandle();
SendMessage(hWnd, EM_SETPASSWORDCHAR, 0x25cf, 0); // 0x25cf is ● character
m_Text->Update();
#endif
}

GetWindowThreadProcessId can not find my window handle, even though its process ID exists on Window

I have a setup application that if user clicks the same setup twice, they would get the pop up message "Another setup instance already running". Upon clicking OK on that message I want to put focus back on the existing installation window which has been running. I currently have the following codes:
if("setup.exe" == CString(buffer))
EnumWindows(EnumWindowsProc,(LPARAM)processID);
BOOL CALLBACK EnumWindowsProc(HWND windowHandle,LPARAM lParam)
{
DWORD searchedProcessId = (DWORD)lParam;
DWORD windowProcessId = 0;
GetWindowThreadProcessId(windowHandle,&windowProcessId);
if(searchedProcessId == windowProcessId)
{
//Set focus when detects the right window.
SetForegroundWindow(windowHandle);
return FALSE;
}
return TRUE;
}
The above code works if I stay on the first screen/step on the installation wizard (install shield).
When I move to the next screen on the installation wizard, this focus logic no longer works. Upon debugging I found that the function GetWindowThreadProcessId could not find any windowProcessID that match searchedProcessId. I also confirmed the following:
the searchProcessId value remains the same whether I am on the 1st or 2nd installation screen.
I confirmed I see the searchProcessID value exists when I view in in the window task manager.
I read up on GetWindowThreadProcessId and see the following note: "The return value is the identifier of the thread that created the window." In my own interpretation, was this because the 2nd step/screen on the wizard is generated by its own thread?
I tried to play around with EnumChildWindow() function but that did not help. I'd be very appricated if anyone able to point me in the right direction of trying to get this to work?
I would use Spy++ which comes with Visual Studio or a similar system monitoring tool to show you all the windows/threads so you can try to confirm exactly what the install wizard is doing. Chances are EnumWindows() is working just fine and it is a problem with your code or your assumptions of how things work.

How to autodetect urls in RichEdit 2.0?

When we have a RichEdit control and send it an EM_AUTOURLDETECT message with WPARAM set to TRUE, it nicely hightlights the detected URLs and sends the EN_LINK notifications.
But it does this only for text that is entered into the control. I haven't found the way to do it for text that's loaded into the control with SetWindowText or EM_STREAMIN.
Please help! Thanks
Upd:
I've created a test application from scratch and it works fine there. I think the problem might be that I have superclassed the control, that is, created a new window class and just use the window procedure of the original class. I'm gonna try subclassing the control instead..
I just knocked up a basic WTL dialog based app containing a riched20 control and the following works fine:
CRichEditCtrl richedit = GetDlgItem(IDC_RICHEDIT);
richedit.SetAutoURLDetect(TRUE);
richedit.SetWindowText(_T("http://www.stackoverflow.com"));
I have some old MFC code that does something similar, albeit with ES_STREAM, and it works OK too.
FWIW the WTL CRichEditCtrl wrapper is pretty thin. SetAutoURLDetect simply calls SendMessage passing it EM_AUTOURLDETECT.
I am compiling with _RICHEDIT_VER set to 0x0200 FWIW.
Without knowing the format of the text you are trying to add to the control with SetWindowText and EM_STREAMIN I'm going to take a guess and say this might have something to do with the control's text mode. After setting the contents of the control try sending it a EM_GETTEXTMODE message and see if the TM_PLAINTEXT bit is set. If this is the case then try sending a EM_SETTEXTMODE message followed by EM_AUTOURLDETECT. Your code should look something like this:
UINT textmode = (UINT)::SendMessage(handle_to_control, EM_GETTEXTMODE, 0, 0);
if(textmode & TM_PLAINTEXT) {
textmode &= ~TM_PLAINTEXT; // Clear the TM_PLAINTEXT bit
textmode |= TM_RICHTEXT; // Set the TM_RICHTEXT bit
if(::SendMessage(handle_to_control, EM_SETTEXTMODE, textmode, 0) != 0) {
// Failed to set the text mode
}
}
::SendMessage(handle_to_control, EM_AUTOURLDETECT, TRUE, 0);
You might just have to rewrite the text to the control to get it to re-parse.