How to send a link to an application, like Spotify does - c++

When we save a level in our editor, we create a log file of any errors it contains. These consist basically of an error message and a path that allows the user to find the erronous item in a tree view.
What I want is to make that path a link, something like
< a href="editor://path/to/gameobject" > Click to see object in editor< /a >
The SO questions I've seen regarding this seems to point to this msdn page:
http://msdn.microsoft.com/en-us/library/aa767914.aspx
But from what I can tell, it will spawn a new instance of the application. What I want to do is to simply "call" our editor somehow. One way to do it, I guess, is to spawn it, and in the beginning check if there's already an instance running, and if so, send the commandline to it.
Is that the best way to do it? If so, any ideas on how to do it best? What are otherwise some ways that this could be done?
Also: does the msdn solution work across browsers? Our editor runs in Windows only, but people use IE, Fx, GC and Opera.

If you need the link to work in any viewer, yes, registering a protocol handler is the best way.
As for launching the editor, you could implement it as an out-of-process COM server, but if you've already got command line parsing sorted, you might as well use a window message or named pipe to pass that to the editor. If you're sending a window message, you could use FindWindow (with a unique class name) to check for a running instance.

Sounds like you solved it already, by checking for a previous instance.
I would be surprised if the OS takes upon it to somehow "stamp" the association with data telling it to separate programs that should run multiple times from programs that should not.

Here's how I solved it. Basically, there are two parts. Or three.
First, the app needs to register itself in the registry, like this. It took some googling to find out how to use the windows register functions, but they were pretty straightforward. By adding this to the registry, your application will launch when a link with your custom url protocol is clicked.
Secondly, the app needs to detect that it's been started from a browser. Obviously quite trivial, just check the command line for "/uri" or however you chose to customize it.
Third, you don't actually want to start your application - it should already be running! Instead, when you've detected that you got started from a hyperlink, you need to detect if another instance of the application is already running. After that, you need to pass the command line to it. Here's how I did it:
bool ShouldContinueStartEditor( const std::string& command_line )
{
// Check if this instance was spawned from a web browser
if ( command_line.find( "/uri" ) != std::string::npos )
{
// Try to find other instance of JustEdit
HWND wnd = FindWindow( "AV_MainFrame", NULL );
if ( wnd )
{
COPYDATASTRUCT cds;
NEditorCopyData::SCommandLine data_to_copy;
strncpy( data_to_copy.m_CommandLine, command_line.c_str(), sizeof(data_to_copy.m_CommandLine) - 2 );
cds.dwData = NEditorCopyData::ECommandLine; // function identifier
cds.cbData = sizeof( data_to_copy ); // size of data
cds.lpData = &data_to_copy; // data structure
SendMessage( wnd, WM_COPYDATA, NULL, (LPARAM) (LPVOID) &cds );
}
return false;
}
return true;
}
"AV_Mainframe" is the name of the hwnd. If you happen to be using WTL, you can declare it like this.
DECLARE_FRAME_WND_CLASS("AV_MainFrame", IDR_MAINFRAME)
Now, in your window class, you need to handle the WM_COPYDATA message like this:
MESSAGE_HANDLER(WM_COPYDATA, OnCopyData);
LRESULT OnCopyData(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/);
LRESULT CMainFrame::OnCopyData(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
{
PCOPYDATASTRUCT cds = (PCOPYDATASTRUCT) lParam;
if ( cds->dwData == NEditorCopyData::ECommandLine )
{
NEditorCopyData::SCommandLine* command_line = static_cast( cds->lpData );
const char* internal_path = strstr( command_line->m_CommandLine, "/uri" );
if ( internal_path != NULL )
{
// Do your thang
}
}
return 0;
}
And that's pretty much all there's to it. Oh, this is what the copy data namespace looks like:
namespace NEditorCopyData
{
enum ECopyDataMessages
{
ECommandLine = 0
};
struct SCommandLine
{
char m_CommandLine[512];
};
}

Related

Get WM_INPUT from Unity window

About
I am trying to build a custom mouse input for Unity that gets the data directly from the HID. I do this because I want to try if there is any difference (when using my own custom mouse input) to the Unity API that gives me raw mouse input.
Also I need to say that everything I am doing right now does not happen within Unity. I want to build an C++ application and then pass the data to Unity (that's not a part of this question).
This link (MSDN High-Definition Mouse Movement) shows that there are three different types of messages I can use. Due to I need so called "High-Definition Mouse Movement" I need to go with WM_INPUT.
This message can be caught with the WinProc handler as the documentation says. Within that callback the raw mouse data can be accessed. This is what I want to achieve and where I need help.
My current approach
The documentation (link above) gives me this example to register the mouse:
RAWINPUTDEVICE Rid[1];
Rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC;
Rid[0].usUsage = HID_USAGE_GENERIC_MOUSE;
Rid[0].dwFlags = RIDEV_INPUTSINK;
Rid[0].hwndTarget = gameWindowHandle;
regDeviceDone = RegisterRawInputDevices(Rid, 1, sizeof(Rid[0]));
The following two lines were modified by me:
Rid[0].hwndTarget = gameWindowHandle;
There I define the Unity window as target. gameWindowHandle is set by EnumWindows.
The other line I changed is the last one due to there is a syntax error (missing parenthesis).
As far as I understood the documentation right this should be it. Now the following callback should be called when there are WM_INPUT messages sent to the Unity window.
LRESULT CALLBACK WindowProc(
_In_ HWND hwnd,
_In_ UINT uMsg,
_In_ WPARAM wParam,
_In_ LPARAM lParam
) {
printf("%d", uMsg);
switch (uMsg) {
case WM_INPUT:
UINT dwSize = 40;
static BYTE lpb[40];
GetRawInputData((HRAWINPUT)lParam, RID_INPUT,
lpb, &dwSize, sizeof(RAWINPUTHEADER));
RAWINPUT* raw = (RAWINPUT*)lpb;
if (raw->header.dwType == RIM_TYPEMOUSE)
{
int xPosRelative = raw->data.mouse.lLastX;
int yPosRelative = raw->data.mouse.lLastY;
printf("X: %d, Y: %d", xPosRelative, yPosRelative);
}
break;
}
return NULL;
}
My problems
The first problem I have is that calling this
regDeviceDone = RegisterRawInputDevices(Rid, 1, sizeof(Rid[0]));
does not return true as it should. Instead it returns false and GetLastError gives me error 87 (after googling this I found out it has to do with wrong parameters).
The documentation says to do so but unfortunately it does not work the way I do it.
Another problem is how to keep the application alive. After registering the device I need to wait for the callbacks to trigger (if they would work). How can I achieve that the application does nothing than waiting for the callbacks?
Is my approach even reasonable or am I doing completely wrong and have to use different APIs?
Your approach is wrong. First, RawInput requires Window. A Window under your control with your own WndProc. Hence in your C++ library, you should define a window procedure. Start a thread. In this thread register window class with that procedure. After you succeed in registering your class, create HWND_MESSAGE window, register your devices and enter a while GetMessage... DispatchMessage loop. This should be done in a separate thread. In your window procedure you must now catch WM_INPUT messages. Enjoy.

force SHBrowseForFolder() to show desired directory

I've been searching online and fighting this thing for over an hour and still can't seem to get it to work. Most people seem to be satisfied when they get it this far on forums etc. but mine still doesn't work.
I'm trying to force the SHBrowseForFolder() function to start in a folder of my choosing.
char current[MAX_PATH];
strcpy(current,"C:\\Users");
char outbuf[MAX_PATH];
BROWSEINFO bis;
bis.hwndOwner = NULL;
bis.pidlRoot = NULL;
bis.pszDisplayName = outbuf;
bis.lpszTitle = (LPCSTR)"HERE";
bis.ulFlags = BIF_NEWDIALOGSTYLE|BIF_RETURNONLYFSDIRS;
bis.lpfn = NULL;
bis.lParam = (LPARAM)current;
SHBrowseForFolder(
&bis
);
It seems like this should be a relatively simple task. :/
At the moment, the above code is still showing the default: the Desktop folder.
Beyond starting in a specific folder, if possible, I'd also like it to ONLY show that folder and below, with no access to parent directories.
What am I missing here?
You can also send a BFFM_SETSELECTION message from your BrowseCallbackProc, like:
int FAR PASCAL BrowseNotify(HWND hWnd, UINT iMessage, long wParam, LPARAM lParam)
{ if (iMessage == BFFM_INITIALIZED)
{ SendMessage(hWnd, BFFM_SETSELECTION, 1, (LPARAM) szInitialPathName); // Set initial folder
return 1;
}
return 0;
}
Set the BFFCALLBACK (lpfn) to a BrowseCallbackProc. From there you can call SendMessage with BFFM_SETEXPANDED to specify the path of a folder to expand in the Browse dialog box.
See:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773205(v=vs.85).aspx and
http://msdn.microsoft.com/en-us/library/windows/desktop/bb762598(v=vs.85).aspx
From my experience, that folder dialog is a bit flaky - it often scrolls the desired directory out of view and looks suboptimal. Just one of the joys of Windows...
Also, there is no way I have discovered to get it to show only that directory and its subs. The parent directories always seem to be there.
Set BIF.PidlRoot to the PIDL you don't want the user to browse below, select and expand the folder you want initially focused and selected - do as above - and it should work.
Jens. :)

"SendMessage" to 3 different processes in C++

I want to send keystrokes to multiple processes. For example, if I press “1”, then I want to send the “1” to 3 "Notepad windows". Frist I want to try to send a keystroke to notepad, but it fails on the HWND:
//HANDLE hWin;
HWND windowHandle = FindWindowA(NULL, "Notepad"); //Can’t find a proccess
//Send a key
if( windowHandle ) //This one fails
{
while(true)
{
if( GetAsyncKeyState(VK_F12) != 0 )
{
SendMessageA(windowHandle, WM_KEYDOWN, VK_NUMPAD1, 0);
Sleep(1000);
SendMessageA(windowHandle, WM_KEYUP, VK_NUMPAD1, 0);
}
Sleep(100);
}
}
But the "FindWindow" method is not good enough for my program. There is also no way to get 3 different processes with the same name. So how can I make 3 handles to 3 different processes with the same name? And how can I send key’s to the processes?
You can use EnumWindows for enumerating all the top level windows on the system. You then need to filter through these windows to get the ones you are interested in. Class name is probably a better choice for filtering rather than the window name though. Here is some example code (not tested) of what I have in mind:
BOOL CALLBACK BroadcastToNotepad(HWND hwnd, LPARAM lParam)
{
wchar_t lpClassName[16];
/*
* More reliable to filter by class name. We could additionally filter
* by caption name too if necessary.
*/
if(GetClassName(hwnd, lpClassName, _countof(lpClassName))) {
if(wcscmp(lpClassName, L"Notepad") == 0) {
SendMessage(hwnd, WM_KEYDOWN, (WPARAM)lParam, 0);
Sleep(1000);
SendMessage(hwnd, WM_KEYUP, (WPARAM)lParam, 0);
}
}
return TRUE;
}
// Some handler which gets invoked when your hotkey is hit.
void handlerKey1(...)
{
EnumWindows(BroadcastToNotepad, (lParam)VK_NUMPAD1)
}
Note the usage of BroadcastToNotepad and how you can have different handlers pass in a different lParam.
One final thing to note is that PostMessage/SendMessage is not a reliable way to simulate keyboard input. This is noted by Raymond Chen here. SendInput is the preferred way for injecting input. However, to use that you will need to ensure the window you want to send to has the keyboard focus.
I recall vaguely having played with something similar to what you are doing in the past. If I remember correctly, you need to send to Notepad's child window (class name = Edit). So the code above needs to be modified as so:
if(wcscmp(lpClassName, L"Notepad") == 0) {
HWND hwndChild = FindWindowEx(hwnd, NULL, L"Edit", NULL);
SendMessage(hwndChild, WM_KEYDOWN, (WPARAM)lParam, 0);
Sleep(1000);
SendMessage(hwndChild, WM_KEYUP, (WPARAM)lParam, 0);
}
Firstly install Spy++ from Visual Studio which lets you see all the HWND windows in hierarchy ( and which process owns them).
Then you'll see why your FindWindow is failing. You'll also know the exact hierarchy calls to make on FindWindow and GetWindow().
Be aware that since Vista some HWNDs are protected and you cant send to them - but notepad is probably fine.
For sending the key, you can probably just use PostMessage to fire and forget.
First of all, why is while(true) there? Wouldn't you rather want to activate your software on F12 key press than having an infinite loop? That handle is not valid forever, you know.
Second, you'd probably want to use EnumWindows to go through all the windows and find the one you're interested in. Then you'd implement a callback function that'll need to decide on some basis if it wants to act on some window or not (be it name or something else).
SendMessage/SendMessageA/SendMessageW should work just fine when you've found a proper handle for the window you want to target for (save for some special windows that are protected from this).

How does a CRichEditCtrl know a paste operation has been performed?

It has methods like CRichEditCtrl::Copy(), CRichEditCtrl::Paste() which you can call, but I can't spot any messages the control is sent by Windows telling it to perform a paste operation. Does anyone know if such a thing exists? Or does CRichEditCtrl do something lower-level like monitoring WM_CHAR events? If so can I reuse any internal methods or would I just have to roll my own in order to override the standard paste functionality?
What I actually want is for my custom subclass (CMyRichEditCtrl : CRichEditCtrl) to ignore any formatting on text pasted in to the control. Either by getting the clipboard data in a different clipboard format, or by pasting it in as normal and immediately removing formatting on inserted text.
What I tried so far:
Checking the message for WM_PASTE in CMyRichEditCtrl::PreTranslateMessage()
Creating a method virtual void CMyRichEditCtrl::Paste()
Putting a breakpoint on CRichEditCtrl::Paste() in afxcmn.inl
Dumping every message passing through CMyRichEditCtrl::PreTranslateMessage()
Results:
1: No WM_PASTE message seen
2: It's never called
3: It's never hit... how?
4: The control never receives any WM_COMMAND, WM_PASTE or focus-related messages. Basically only mouse-move and key-press messages.
It seems other people have actually done this successfully. I'm wondering if my MFC version or something could be screwing it up, at this point.
Handle EN_PROTECTED message.
ON_NOTIFY_REFLECT(EN_PROTECTED, &YourClass::OnProtected)
// call this from the parent class
void YourClass::Initialize()
{
CHARFORMAT format = { sizeof(CHARFORMAT) };
format.dwEffects = CFE_PROTECTED;
format.dwMask = CFM_PROTECTED;
SetDefaultCharFormat(format);
SetEventMask(ENM_PROTECTED);
}
void YourClass::OnProtected(NMHDR* pNMHDR, LRESULT* pResult)
{
*pResult = 0;
ENPROTECTED* pEP = (ENPROTECTED*)pNMHDR;
if (pEP->msg == WM_PASTE)
pResult = 1; // prevent paste
}
What happens when the user requests a paste action is usually that a WM_COMMAND message with the identifier ID_EDIT_PASTE is sent to the rich edit control. By default in MFC this is handled by CRichEditCtrl::OnEditPaste(), which calls Paste() on the edit control itself.
The way I'd go about this is to derive a class from CRichEditCtrl, add an OnEditPaste method and route the message to it with a
ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
declaration, which should work. Alternatively, in your PreTranslateMessage you could look for WM_COMMAND with a wParam of ID_EDIT_PASTE.
By the way, I've solved a very similar problem to yours (paste without formatting) by just having an implementation of OnEditPaste with
void MyRichEdit::OnEditPaste()
{
SendMessage(EM_PASTESPECIAL,CF_UNICODETEXT);
}
This responds to the paste request by sending a paste message to the control that insists that the data format is plain text.
Finally, I should point out that the above technique is sufficient to catch all pastes triggered from the user interface. However, it won't catch programmatically triggered pastes, when your code sends WM_PASTE to the edit control. In those cases it's easiest to just change your code. However, if you really want to intercept such cases, you have to get your hands dirty with COM and IRichEditOleCallback::QueryAcceptData. But you almost certainly don't want to go there :-)
Windows defines messages for cut/copy/and paste. see WM_CUT.
It's probably responding to those messages rather than to WM_CHAR messages to know when to do clipboard operations.
Use the ON_MESSAGE Macro on your derived class.
ON_MESSAGE(WM_PASTE, OnPaste)
LRESULT CMyRichEditCtrl::OnPaste(WPARAM, LPARAM)
If you open the RichEdit.h file, you will notice that some of the messages are on the range of WM_USER. Maybe this is how MFC handles the events for the Rich Edit Control.
i have to perform like below
void MyRichEcit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if( ( GetKeyState(VK_CONTROL)<0 && nChar==88 ) || (nChar==VK_DELETE && GetKeyState(VK_SHIFT) < 0) ) //cut
{
}
if( ( GetKeyState(VK_CONTROL)<0 && nChar==86 ) || (nChar==VK_INSERT && GetKeyState(VK_SHIFT) < 0) ) //paste
{
}
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
Did you try CRichEditCtrl::PasteSpecial(CF_TEXT)? I believe it should do what you are wanting to do.

Reading from a text field in another application's window

Is there a way for a Windows application to access another applications data, more specifically a text input field in the GUI, and grab the text there for processing in our own application?
If it is possible, is there a way to "shield" your application to prevent it?
EDIT: The three first answers seem to be about getting the another applications window title, not a specific text input field in that window.
I'm no Windows API expert, so could you be more exact how do I find a certain text field in that window, what are the prerequisites for it (seems like knowing a window handle something is required, does it require knowing the text field handle as well? How do I get that? etc...)
Code snippets in C++ really would be really appreciated. MSDN help is hard to browse since Win32-API has such horrible naming conventions.
Completed! See my answer below for a how-to in C++.
For reading text content from another application's text box you will need to get that text box control's window handle somehow. Depending on how your application UI is designed (if it has a UI that is) there are a couple of different ways that you can use to get this handle. You might use "FindWindow"/"FindWindowEx" to locate your control or use "WindowFromPoint" if that makes sense. Either way, once you have the handle to the text control you can send a "WM_GETTEXT" message to it to retrieve its contents (assuming it is a standard text box control). Here's a concocted sample (sans error checks):
HWND hwnd = (HWND)0x00310E3A;
char szBuf[2048];
LONG lResult;
lResult = SendMessage( hwnd, WM_GETTEXT, sizeof( szBuf ) / sizeof( szBuf[0] ), (LPARAM)szBuf );
printf( "Copied %d characters. Contents: %s\n", lResult, szBuf );
I used "Spy++" to get the handle to a text box window that happened to be lying around.
As for protecting your own text boxes from being inspected like this, you could always sub-class your text box (see "SetWindowLong" with "GWL_WNDPROC" for the "nIndex" parameter) and do some special processing of the "WM_GETTEXT" message to ensure that only requests from the same process are serviced.
OK, I have somewhat figured this out.
The starting point is now knowing the window handle exactly, we only know partial window title, so first thing to do is find that main window:
...
EnumWindows((WNDENUMPROC)on_enumwindow_cb, 0);
...
which enumerates through all the windows on desktop. It makes a callback with each of these window handles:
BOOL CALLBACK on_enumwindow_cb(HWND hwndWindow, LPARAM lParam) {
TCHAR wsTitle[2048];
LRESULT result;
result = SendMessage(hwndWindow, WM_GETTEXT, (WPARAM) 2048, (LPARAM) wsTitle);
...
and by using the wsTitle and little regex magic, we can find the window we want.
By using the before mentioned Spy++ I could figure out the text edit field class name and use it to find wanted field in the hwndWindow:
hwndEdit = FindWindowEx(hwndWindow, NULL, L"RichEdit20W", NULL);
and then we can read the text from that field:
result = SendMessage(hwndEdit, WM_GETTEXT, (WPARAM) 4096, (LPARAM) wsText);
I hope this helps anyone fighting with the same problem!
Look at AutoHotkey. If you need an API for your application, look at their sources.
To prevent it, use a custom widget instead of WinForms, MFC or Win32 API. That is not foolproof, but helps.
Yes it is possible in many ways (one way is to use WINAPI GetWindow and GetWindowText).
First, get a handle to the textbox you want to retrieve text from (using FindWindow, EnumChildWindows and other APIs), then:
Old VB6-codeexample, declaration of API:
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As Long) As Long
Code to extract text:
Dim MyStr As String
MyStr = String(GetWindowTextLength(TextBoxHandle) + 1, Chr$(0))
GetWindowText TextBoxHandle, MyStr, Len(MyStr)
MsgBox MyStr
About how to shield the application to prevent it, you could do many things.
One way would be to have a own control to handle text input that build up the text from lets say a couple of labels placed where the text would be, or that draws the text graphically.
You can also get text from a richedit control with EM_GETTEXTRANGE message, but it works only in the same process in which the control was created.