I have a simple windows form that has a COM user control. This COM user control has method "GetNode" COM methods.
I want to test this function call using autohotkey.
Run "C:\AHC_Exploring\Project1.exe"
WinWaitActive, Form1, , 2
if ErrorLevel
{ MsgBox, WinWait timed out.
return
}
ObjUerControl := ; // Need to know correct get COM object method
lResult = Object.GetNodeId(2)
MsgBox %lResult%
Return
You can use ComObjActive to get a handle to a COM object that is already running.
Related
In our application that we've had for 15+ years, we have a type of progress bar.
This progress bar is for long lasting C++ operations and there is also the case for when we make a COM call and it takes a long time for the COM call to return.
In general, we want the user to know that something is taking a long time to complete and give him a chance to cancel if he thinks it is taking too much time.
For COM operations, many years ago we implemented a custom IMessageFilter for COM calls that take too long. We would like the user to be able to cancel but also for the prompt to cancel go away on its own when the operation completes. It has been working fine for years. Most of our customers are conservative and are still running Windows 7. Recently a customer using Windows 10 has found an issue where a COM call on Windows 10 never seems to never finish.
Our progress bar comes up and the progress control cycles and recycles, but the operation never seems to finish. After investigating it, it seems that the ICancelMethodCalls::TestCancel() method always returns RPC_S_CALLPENDING, it never returns RPC_E_CALL_COMPLETE. On Windows 7, previous versions of Windows, and Windows 8.1, it works fine, but not on Windows 10.
I created a minimal test solution in Visual Studio 2013 that demonstrates the problem. One project is an ATL server, and the other project is an MFC client application. A link to a zip file of a sample solution is here: https://www.dropbox.com/s/1dkchplsi7d6tda/MyTimeOutServer.zip?dl=0
Basically the ATL server has a property that sets a delay, and a method that just waits the delay length. The purpose is to simulate a COM operation that is taking too long.
interface IMyTimoutServer : IDispatch{
[propget, id(1), helpstring("Timeout value in milliseconds")] HRESULT TimeOut([out, retval] LONG* pVal);
[propput, id(1), helpstring("Timeout value in milliseconds")] HRESULT TimeOut([in] LONG newVal);
[id(2)] HRESULT DoTimeOut([out, retval] VARIANT_BOOL* Result);
};
The next important thing is the IMessageFilter in the client application. At some point, someone decided it was good to derive from COleMessageFilter, the default MFC implementation. (Let's not argue whether that is a good idea.)
The important methods of the the class are the MessagePending() and MyNotResponding().
DWORD CMyMessageFilter::XMyMessageFilter::MessagePending(HTASK htaskCallee, DWORD dwTickCount, DWORD dwPendingType)
{
METHOD_PROLOGUE_EX(CMyMessageFilter, MyMessageFilter);
ASSERT_VALID(pThis);
MSG msg;
if (dwTickCount > pThis->m_nTimeout && !pThis->m_bUnblocking && pThis->IsSignificantMessage(&msg))
{
if (pThis->m_bEnableNotResponding)
{
pThis->m_bUnblocking = TRUE; // avoid reentrant calls
// eat all mouse and keyboard messages in our queue
while (PeekMessage(&msg, NULL, WM_MOUSEFIRST, AFX_WM_MOUSELAST, PM_REMOVE | PM_NOYIELD));
while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE | PM_NOYIELD));
// show not responding dialog
pThis->m_dwTickCount = dwTickCount;
bool bCancel = pThis->MyNotResponding(htaskCallee) == RPC_E_CALL_CANCELED;
pThis->m_bUnblocking = FALSE;
return bCancel ? PENDINGMSG_CANCELCALL : PENDINGMSG_WAITNOPROCESS; // if canceled, the COM call will return RPC_E_CALL_CANCELED
}
}
// don't process re-entrant messages
if (pThis->m_bUnblocking)
return PENDINGMSG_WAITDEFPROCESS;
// allow application to process pending message
if (::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE | PM_NOYIELD))
pThis->OnMessagePending(&msg); // IK: could also return a value from extended OnMessagePending() to cancel the call
// by default we return pending MSG wait
return PENDINGMSG_WAITNOPROCESS;
}
After a timeout, we display a status type window that updates in the NotMyResponding() method.
int CMyMessageFilter::MyNotResponding(HTASK hTaskBusy)
{
TRY
{
CComPtr<ICancelMethodCalls> pCancel;
HRESULT hRes = ::CoGetCancelObject(0, IID_ICancelMethodCalls, (void**)&pCancel);
ATLASSERT(SUCCEEDED(hRes)); // COM should automatically create Cancel objects for pending calls [both on client and server]
if (pCancel == NULL)
{
return COleBusyDialog::retry;
}
m_pFrame->EnableWindow(FALSE);
CMyCancelDlg dlg;
dlg.Create(CMyCancelDlg::IDD);
dlg.ShowWindow(SW_SHOWNORMAL);
HWND hWndDlg = dlg.GetSafeHwnd();
do
{
MSG msg;
for (int i = 0; i < 100 && PeekMessage(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD); ++i)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (dlg.StepAndCheckCancel())
{
dlg.DestroyWindow();
m_pFrame->EnableWindow(TRUE);
return RPC_E_CALL_CANCELED; // signals to MessagePending() that the COM call should be cancelled
}
Sleep(250); // this call has been pending for many seconds now... sleep for some time to avoid CPU utilization by this loop and yield if needed
hRes = pCancel->TestCancel();
} while (hRes == RPC_S_CALLPENDING);
ATLASSERT(hRes == RPC_E_CALL_COMPLETE);
dlg.DestroyWindow();
m_pFrame->EnableWindow(TRUE);
}
END_TRY
return RPC_E_CALL_COMPLETE;
}
Basically, in MyNotResponding(), we create a Cancel object, create a window with a cancel button, pump messsages, and look for either a press on the cancel button or if TestCancel() returns something other than RPC_S_CALLPENDING.
Well, on Windows 10, it stays in the loop and RPC_S_CALLPENDING is always returned from TestCancel().
Has anyone seen anything like this on Windows 10? Are we doing something wrong that we are really only getting lucky on Windows 7?
The default implementation of the MFC puts up an OLEUIBusy dialog which pumps messages. It just never tests the cancel object.
It is many years since I did anything with FoxPro, but I have a legacy system that needs work. Okay, I can call a COM-based application such as MapPoint or Excel from FoxPro. I've done that before. However, how do I pass a function or object method as an event callback?
Is it even possible? (I can't find anything online or the FoxPro books I've managed to track down)
The following is a VB6 example of what I mean, taken from the MapPoint documentation. As it happens OnConnection() is itself a callback; but the call to moaApp.AddCommand() passes a reference to a callback function ( SayHello() ) to MapPoint (moApp), as a menu callback.
Not that it matters for the question, but I'm probably going to need to trap the Save, Quit, and Menu callback events.
Dim moaApp As MapPoint.Application
Public Sub SayHello()
MsgBox "Hello"
End Sub
Private Sub AddinInstance_OnConnection(ByVal Application As Object, ByVal ConnectMode As
AddInDesignerObjects.ext_ConnectMode, ByVal AddInInst As Object, custom() As Variant)
On Error GoTo error_handler
Set moaApp = Application
'Add this command to the menu (HOW DO I DO THIS IN FOXPRO?)
moaApp.AddCommand "Saying Hello", "SayHello", Me
Exit Sub
error_handler:
MsgBox Err.Description
End Sub
Thanks to the lead from #Alan B, I've managed to get it working...
Events are caught by creating a COM class that implements the required event interface. All events in the interface have to be implemented, although they can be empty implementations.
E.g.
&& Create an event handler
oHandler = CREATEOBJECT("MapPointEventHandler")
&& Connect our _ApplicationEvents implementation
EVENTHANDLER( oMyMapPointApp, oHandler)
&& Here is our event handler
DEFINE CLASS MapPointEventHHandler AS Session OLEPUBLIC
IMPLEMENTS _ApplicationEvents IN "MapPoint.Application"
&& Call back when MapPoint Quits
PROCEDURE _ApplicationEvents_Quit()
MESSAGEBOX("QuitHandler called")
ENDPROC
&& Event indicates MapPoint is about to close
PROCEDURE _ApplicationEvents_BeforeClose( bcancel as logical) AS VOID
MESSAGEBOX("before close called")
ENDPROC
&& These events are not used here, but must be defined for COM/class compatibility
PROCEDURE _ApplicationEvents_BeforeSave( SaveAsUI AS logical #, bcancel as logical) AS VOID
*? PROGRAM()
ENDPROC
PROCEDURE _ApplicationEvents_New() AS VOID
*? PROGRAM()
ENDPROC
PROCEDURE _ApplicationEvents_Open() AS VOID
*? PROGRAM()
ENDPROC
ENDDEFINE
Methods can also be passed (eg. for menu items), but these cannot be to the same class. You need to implement one class for each event handler interface you wish to implement, and a separate class to handle the menu callbacks.
Here is an example with a menu item:
&& Create a menu handler
oMyMenu = CREATEOBJECT("MapPointMenuHandler")
&& Add our Tools menu entries and hook them up
oMyMapPointApp.AddCommand("Custom Menu Item", "MyMenuCallBack", oMyMenu)
&& This class implements the Tools menu callbacks
&& *** NOTE: MessageBox will appear UNDER MapPoint
DEFINE CLASS MapPointMenuHandler AS Session OLEPUBLIC
PROCEDURE MyMenuCallback()
MESSAGEBOX("Main Menu callback")
ENDPROC
ENDDEFINE
Assuming I have a class that implements the DShellWindowsEvents interface and registers it with shell windows; The OnWindowRegistered method is called whenever a new IE or Explorer window is opened with a cookie value as the first parameter. Using this cookie value, I should be able to call FindWindowSW and retrieve the window that was just registered. However, it always returns S_FALSE with no values returned.
Here is the relevant code:
HRESULT MyShellWindows::OnWindowRegistered(LONG cookie)
{
CComPtr<IDispatch> p_browser_disp;
CComVariant v_cookie(cookie), v_empty;
long window_handle = 0;
HRESULT res = m_shell->FindWindowSW(
&v_cookie,
&v_empty,
SWC_BROWSER,
&window_handle,
SWFO_COOKIEPASSED|SWFO_NEEDDISPATCH,
&p_browser_disp
);
assert(res == S_OK); // returns S_FALSE.
}
The result is always S_FALSE and neither an IDispatch pointer nor a window handle is returned. I tried several combinations for the flags and I tried Apartment and Free Threading. I even tried to save the cookie and defer the call to avoid re-entrance into the IShellWindows object. Edit: I also tried numerous combinations of variant types directly and by reference as the cookie value, IIRC all VT_I* and VT_U* types plus some more directly and by reference.
So, here is the question:
How can I get a valid result (S_OK and a window handle or an IDispatch pointer) from a FindWindowSW call from a cookie?
Please don't post if you never tried. Thanks.
hello
i'm using Visual Basic 2008
here is part of my code:
Private Const SW_HIDE As Integer = 0
Private Const SW_SHOW As Integer = 5
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function ShowWindowAsync(ByVal hwnd As IntPtr, ByVal nCmdShow As Integer) As Boolean
End Function
button1 code:
Try
Dim p() As Process = Process.GetProcessesByName("notepad")
Dim mwh = p(0).MainWindowHandle
ShowWindowAsync(mwh, SW_HIDE)
Catch ex As Exception
MsgBox("error")
End Try
button2 code:
Try
Dim p() As Process = Process.GetProcessesByName("notepad")
Dim mwh = p(0).MainWindowHandle
ShowWindowAsync(mwh, SW_SHOW)
Catch ex As Exception
MsgBox("error")
End Try
when i click button 1 ( to hide the window ) it works good, but then when i want to display window back ( by clicking button 2 ) the function returns FALSE, who can tell me why? and how to get it to work, to hide window and then to show it again?
Because ShowWindowAsync() returns zero when the window was previously hidden according to the MSDN documentation, and zero is interpreted as FALSE. The return value does not indicate whether the function succeeded or not.
So the first time you call ShowWindowAsync(mwh, SW_HIDE) on a visible window the function returns TRUE because ShowWindowAsync() returns a non-zero value indicating the window (that is now/will be hidden) used to be visible.
Then the second call ShowWindowAsync(mwh, SW_SHOW) on a hidden window returns FALSE because ShowWindowAsync() returns a zero value indicating the window (that is now/will be visible) used to be hidden.
In other words, this is by design and the function should work as intended (unless the mwh window is not responding to messages or is invalid).
But the real problem here is that once you hide a window, Process::MainWindowHandle property doesn't have the handle anymore.
A process has a main window associated
with it only if the process has a
graphical interface. If the associated
process does not have a main window,
the MainWindowHandle value is zero.
The value is also zero for processes
that have been hidden, that is,
processes that are not visible in the
taskbar. This can be the case for
processes that appear as icons in the
notification area, at the far right of
the taskbar.
What you should do is to obtain the window handle via p(0).MainWindowHandle once and save the returned handle somewhere, so that you can show/hide the window. Of course, you should make sure that you zero out the saved handle when the window gets destroyed by the target process. In Notepad's case, you can use the Process::Exited event.
Sounds like "by design". Don't treat the return value of "false" as an error.
As per MSDN:
If the window was previously visible, the return value is nonzero.
If the window was previously hidden, the return value is zero.
http://msdn.microsoft.com/en-us/library/ms633549%28VS.85%29.aspx
You could likely block the exception from occurring by declaring the interop import of the function as a 32-bit integer type instead of a Boolean.
Private Shared Function ShowWindowAsync(ByVal hwnd As IntPtr, ByVal nCmdShow As Integer) As Integer
When hiding I did :
WindowHandle = Proc(0).MainWindowHandle
ShowWindowAsync(Proc(0).MainWindowHandle, ShowWindowConstants.SW_HIDE)
Then when showing I used the WindowHandle variable:
ShowWindowAsync(WindowHandle, ShowWindowConstants.SW_SHOW)
To Maximize a hidden window of another process you must do this 3 steps (very easy):
1- Get the MainWindowHandle
Because the application is hidden the MainWindowHandle is 0 (false). To overcome this problem you can use the FindWindow
//Get the MainWindowHandle
IntPtr hwnd = FindWindow(null, <WINDOW TITLE>);
2- Use ShowWindowAsync()
bool result = ShowWindowAsync(hwnd, 3); //3= Maximize application
3- Implement one of this application events: "GotFocus", "Activated" or "IsVisibleChanged"
(I only tested the "Activated" event)
private void WindowApplication_Activated(object sender, EventArgs e)
{
Application.Current.MainWindow.Show();
}
You can find more information on this url:
http://isolvable.blogspot.pt/2009/09/restoring-wpf-window-of-another-process.html
Hope this information can help someone!
I'd like to be able to query some function and give it a processID or processName - It then should return true or false on wether that process is in the foreground or not.
So i.e. the query for Firefox would return true (because right now I'm in FireFox, typing this) and everything else should return false.
Is that even possible for every type of application (.net, java/swing, pure c++/win32-ui)?
This question is for Windows only.
GetForegroundWindow and GetWindowThreadProcessId should let you get this information.
i.e., if you know the pid just check it against a function like this:
bool IsForegroundProcess(DWORD pid)
{
HWND hwnd = GetForegroundWindow();
if (hwnd == NULL) return false;
DWORD foregroundPid;
if (GetWindowThreadProcessId(hwnd, &foregroundPid) == 0) return false;
return (foregroundPid == pid);
}
This will work for any application that uses the core Win32 library at some level - this'll include Windows Forms, WPF, native Win32 applications, etc. Note this'll only work for applications running on the calling desktop and session - you can't use this to determine if another user's application is in the foreground, for instance.