i'm using the Win32 progress dialog. The damnest thing is that when i call:
progressDialog.StopProgressDialog();
it doesn't disappear. It stays on screen until the user moves her mouse over it - then it suddenly disappers.
The call to StopProgressDialog returns right away (i.e. it's not a synchronous call). i can prove this by doing things after the call has returned:
private void button1_Click(object sender, EventArgs e)
{
//Force red background to prove we've started
this.BackColor = Color.Red;
this.Refresh();
//Start a progress dialog
IProgressDialog pd = (IProgressDialog)new ProgressDialog();
pd.StartProgressDialog(this.Handle, null, PROGDLG.Normal, IntPtr.Zero);
//The long running operation
System.Threading.Thread.Sleep(10000);
//Stop the progress dialog
pd.SetLine(1, "Stopping Progress Dialog", false, IntPtr.Zero);
pd.StopProgressDialog();
pd = null;
//Return form to normal color to prove we've stopped.
this.BackColor = SystemColors.Control;
this.Refresh();
}
The form:
starts gray
turns red to show we've stared
turns back to gray color to show we've called stop
So the call to StopProgressDialog has returned, except the progress dialog is still sitting there, mocking me, showing the message:
Stopping Progress Dialog
Doesn't Appear for 10 seconds
Additionally, the progress dialog does not appear on screen until the
System.Threading.Thread.Sleep(10000);
ten second sleep is over.
Not limited to .NET WinForms
The same code also fails in Delphi, which is also an object wrapper around Window's windows:
procedure TForm1.Button1Click(Sender: TObject);
var
pd: IProgressDialog;
begin
Self.Color := clRed;
Self.Repaint;
pd := CoProgressDialog.Create;
pd.StartProgressDialog(Self.Handle, nil, PROGDLG_NORMAL, nil);
Sleep(10000);
pd.SetLine(1, StringToOleStr('Stopping Progress Dialog'), False, nil);
pd.StopProgressDialog;
pd := nil;
Self.Color := clBtnFace;
Self.Repaint;
end;
PreserveSig
An exception would be thrown if StopProgressDialog was failing.
Most of the methods in IProgressDialog, when translated into C# (or into Delphi), use the compiler's automatic mechanism of converting failed COM HRESULTS into a native language exception.
In other words the following two signatures will throw an exception if the COM call returned an error HRESULT (i.e. a value less than zero):
//C#
void StopProgressDialog();
//Delphi
procedure StopProgressDialog; safecall;
Whereas the following lets you see the HRESULT's and react yourself:
//C#
[PreserveSig]
int StopProgressDialog();
//Delphi
function StopProgressDialog: HRESULT; stdcall;
HRESULT is a 32-bit value. If the high-bit is set (or the value is negative) it is an error.
i am using the former syntax. So if StopProgressDialog is returning an error it will be automatically converted to a language exception.
Note: Just for SaG i used the [PreserveSig] syntax, the returned HRESULT is zero;
MsgWait?
The symptom is similar to what Raymond Chen described once, which has to do with the incorrect use of PeekMessage followed by MsgWaitForMultipleObjects:
"Sometimes my program gets stuck and
reports one fewer record than it
should. I have to jiggle the mouse to
get the value to update. After a while
longer, it falls two behind, then
three..."
But that would mean that the failure is in IProgressDialog, since it fails equally well on CLR .NET WinForms and native Win32 code.
To really hide the dialog, I've added the following to my C++ wrapper class:
void CProgressDlg::Stop()
{
if ((m_isVisible)&&(m_bValid))
{
HWND hDlgWnd = NULL;
//Sometimes the progress dialog sticks around after stopping it,
//until the mouse pointer is moved over it or some other triggers.
//This process finds the hwnd of the progress dialog and hides it
//immediately.
IOleWindow *pOleWindow;
HRESULT hr=m_pIDlg->QueryInterface(IID_IOleWindow,(LPVOID *)&pOleWindow);
if(SUCCEEDED(hr))
{
hr=pOleWindow->GetWindow(&hDlgWnd);
if(FAILED(hr))
{
hDlgWnd = NULL;
}
pOleWindow->Release();
}
m_pIDlg->StopProgressDialog();
if (hDlgWnd)
ShowWindow(hDlgWnd, SW_HIDE);
m_isVisible = false;
m_pIDlg->Release();
m_bValid = false;
}
}
This is in C++, but you should be able to adapt this to C# without much problems.
Check the return value of the StopProgressDialog Method, maybe that will give you more information about what is going on:
HRESULT StopProgressDialog(VOID);
Returns S_OK if successful, or an error value otherwise.
The full P/Invoke signature is available, but here's the condensed version for easy reading:
[ComImport]
[Guid("EBBC7C04-315E-11d2-B62F-006097DF5BD4")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IProgressDialog
{
void StartProgressDialog(IntPtr hwndParent,
[MarshalAs(UnmanagedType.IUnknown)] object punkEnableModless, //IUnknown
PROGDLG dwFlags, //DWORD
IntPtr pvResevered //LPCVOID
);
void StopProgressDialog();
void SetTitle(
[MarshalAs(UnmanagedType.LPWStr)] string pwzTitle //LPCWSTR
);
void SetAnimation(
IntPtr hInstAnimation, //HINSTANCE
ushort idAnimation //UINT
);
[PreserveSig]
[return: MarshalAs(UnmanagedType.Bool)]
bool HasUserCancelled();
void SetProgress(
uint dwCompleted, //DWORD
uint dwTotal //DWORD
);
void SetProgress64(
ulong ullCompleted, //ULONGLONG
ulong ullTotal //ULONGLONG
);
void SetLine(
uint dwLineNum, //DWORD
[MarshalAs(UnmanagedType.LPWStr)] string pwzString, //LPCWSTR
[MarshalAs(UnmanagedType.VariantBool)] bool fCompactPath, //BOOL
IntPtr pvResevered //LPCVOID
);
void SetCancelMsg(
[MarshalAs(UnmanagedType.LPWStr)] string pwzCancelMsg,
object pvResevered
);
void Timer(PDTIMER dwTimerAction, object pvResevered);
}
Note: That almost all of the methods follow proper COM rules for their signatures. Except HasUserCancelled. It is does not follow the rules for the signature of a method in a COM class. All methods are supposed to return an HRESULT, and return values are supposed to be in an out retval paramater. HasUserCancelled actually returns a Boolean value.
Note: That almost all these worlds are yours. Except Europa. Attempt no landing there.
Note: That almost all your base are belong to us. Except WhatYouSay. Main light turn on.
Related
I'm writing an application in C# to remote control another application. I don't have access to that application yet, but it's most likely written in either Visual C++ or VB6. I'm currently remote controlling a simple C# winforms program I created instead. So far, I can get all of the controls in a manner similar to Spy++ using PInvoke. I can also do things like setting the text in a textbox. The problem I have is that I can't uniquely identify a specific text box between executions of the application I'm controlling. I can't use hWnd for example because it's different every time you run the application. GetWindowLong returns whatever the hWnd is so that's no help. The internet speaks of this message called WM_GETCONTROLNAME. The scary code below attempts to use that message to get the name the developer used to uniquely identify the controls. The SendMessage seems like it's returning the number of bytes in the name. But the buffer containing that name comes back all zeros.
So here's the question. How can I fix the code below so that it returns that name correctly? (Hopefully it's a boneheaded mistake that's easy to fix) Or, just as good, is there some other ID that will be guaranteed to be the same every time I run the program? Without something to uniquely identify the textbox controls, my code can't tell them apart.
I do have a hack in mind. It seems to me that using the position of the textboxes to tell them apart would work. But I'd much prefer to have the code below working.
public static string GetWindowText(IntPtr Handle)
{
int BufferSize = 256;
uint Message = RegisterWindowMessage("WM_GETCONTROLNAME");
StringBuilder sb = new StringBuilder(BufferSize);
byte[] ControlName = new byte[BufferSize];
long BytesRead = 0;
IntPtr BytesReadPointer = new IntPtr(BytesRead);
IntPtr OtherMem = IntPtr.Zero;
try
{
OtherMem = VirtualAllocEx(Handle, IntPtr.Zero, new IntPtr(sb.Capacity), AllocationType.Commit, MemoryProtection.ExecuteReadWrite);
var Result = SendMessage(Handle, Message, new IntPtr(sb.Capacity), OtherMem);
//Result contains different numbers which seem like the correct lengths
var Result2 = ReadProcessMemory(Handle, OtherMem, ControlName, BufferSize, out BytesReadPointer);
//ControlName always comes back blank.
}
finally
{
var Result3 = VirtualFreeEx(Handle, OtherMem, BufferSize, FreeType.Release);
}
return ""; // Convert ControlName to a string
}
So after a good night's sleep and a little tinkering, I managed to fix the problem myself. There were two problems. First, I was passing in the handle to the control to VirtualAllocEx. But in the documentation for that function, it says that it takes a handle to the process. So started supplying that. Then VirtualAllocEx was telling me that I had an invalid handle. So it turns out the handle you pass to VirualAllocEx has to have PROCESS_VM_OPERATION set. So I made a call to OpenProcess to get that handle, then passed that to VirtualAllocEx. After that, everything worked. I just had to convert the byte array coming back into a string, and trim a bunch of nulls. This returns the Name property on the controls. I'll post the code here in case anyone else needs something like this.
[Flags]
public enum ProcessAccessFlags : uint
{
All = 0x001F0FFF,
Terminate = 0x00000001,
CreateThread = 0x00000002,
VirtualMemoryOperation = 0x00000008,
VirtualMemoryRead = 0x00000010,
VirtualMemoryWrite = 0x00000020,
DuplicateHandle = 0x00000040,
CreateProcess = 0x000000080,
SetQuota = 0x00000100,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
QueryLimitedInformation = 0x00001000,
Synchronize = 0x00100000
}
public static string GetWindowText(IntPtr ControlHandle, IntPtr ProcessHandle)
{
int BufferSize = 256;
StringBuilder sb = new StringBuilder(BufferSize);
byte[] ControlNameByteArray = new byte[BufferSize];
long BytesRead = 0;
IntPtr BytesReadPointer = new IntPtr(BytesRead);
IntPtr RemoteProcessMemoryAddress = IntPtr.Zero;
IntPtr RemoteProcessHandle = IntPtr.Zero;
try
{
uint Message = RegisterWindowMessage("WM_GETCONTROLNAME");
RemoteProcessHandle = OpenProcess(ProcessAccessFlags.CreateThread |
ProcessAccessFlags.QueryInformation |
ProcessAccessFlags.VirtualMemoryOperation |
ProcessAccessFlags.VirtualMemoryWrite |
ProcessAccessFlags.VirtualMemoryRead, false, Process.Id);
RemoteProcessMemoryAddress = VirtualAllocEx(RemoteProcessHandle, IntPtr.Zero, new IntPtr(sb.Capacity), AllocationType.Commit, MemoryProtection.ExecuteReadWrite);
if (RemoteProcessMemoryAddress == IntPtr.Zero)
{
throw new Exception("GetWindowText: " + GetLastWin32Error());
}
SendMessage(ControlHandle, Message, new IntPtr(sb.Capacity), RemoteProcessMemoryAddress);
ReadProcessMemory(RemoteProcessHandle, RemoteProcessMemoryAddress, ControlNameByteArray, BufferSize, out BytesReadPointer);
string ControlName = Encoding.Unicode.GetString(ControlNameByteArray).TrimEnd('\0');
return ControlName;
}
finally
{
VirtualFreeEx(RemoteProcessHandle, RemoteProcessMemoryAddress, BufferSize, FreeType.Release);
}
}
I am working on a Pebble watch face and I ran into a problem, the function app_message_outbox_send seems to throw an error (which then crashes my app). The error is "[INFO ] E call_internal.c:36 syscall failure! 0..0x8 is not in app space."
The relevant code:
static void askPhoneForCharge(){
if(bluetooth_connection_service_peek()){
DictionaryIterator *iter;
app_message_outbox_begin(&iter);
dict_write_uint8(iter, KEY_PHONE_ASK, 0);
app_message_outbox_send();
}else{
phoneCharging = 0;
phoneCharge = 0;
updatePhoneBattery();
}
}
Here is how I set up the handlers and open the channel:
app_message_register_inbox_received(inboxReceivedCallback);
app_message_register_inbox_dropped(inboxDroppedCallback);
app_message_register_outbox_failed(outboxFailedCallback);
app_message_register_outbox_sent(outboxSentCallback);
app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum());
As it turns out, you can not use the message functions while in the initialization phase, so I started a timer that only executes once to take care of the initial messaging.
I was having the same problem while sending the app_message_outbox_send() command which was getting called by to the Up_button_click handler.
The following initialization code in the Init() method fixed it for me.
Look at the "Register message handlers" & "Init buffers"
void out_sent_handler(DictionaryIterator *sent, void *context){}
static void out_fail_handler(DictionaryIterator *failed, AppMessageResult reason, void* context){}
static void in_received_handler(DictionaryIterator *iter, void* context){}
void in_drop_handler(AppMessageResult reason, void *context){}
static void init() {
// Register message handlers
app_message_register_outbox_sent(out_sent_handler);
app_message_register_inbox_received(in_received_handler);
app_message_register_inbox_dropped(in_drop_handler);
app_message_register_outbox_failed(out_fail_handler);
// Init buffers
app_message_open(64, 64);
// Create main Window element and assign to pointer
s_main_window = window_create();
// Set handlers to manage the elements inside the Window
window_set_window_handlers(s_main_window, (WindowHandlers) {
.load = main_window_load,
.unload = main_window_unload
});
// Show the Window on the watch, with animated=true
window_stack_push(s_main_window, true);
}
I use Direct2D to render the animation on VS2010. But when I create over 100 animation objects (the limitation amount is variable),
the output window shows the error message:
"First-chance exception at 0x7607C42D in StarrySky.exe: Microsoft C++
exception: _com_error at memory location 0x001C9580."
However, the error does not interupt the program, and the animation object's seems correct.
I trace the code and find out the error message print after calling
hr = m_pD2DFactory->CreateHwndRenderTarget(renderTargetProperties,
D2D1::HwndRenderTargetProperties(this->m_hWnd, size), &m_pRT);
But the return hr is S_OK. Does anyone know the reason or the solution?
In Direct2D, make sure you are instantiating the device independent resources (In Constructor or something - Init_D2D() in this code example) before device dependent resources. For instance,
// In your header files
ID2D1Factory* pD2DFactory; // device independent
ID2D1HwndRenderTarget* pRenderTarget;
// In the source files
void Init_D2D() // probably in constructor
{
HRESULT hr = D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory);
}
void Create_D2D_Device_Dep() //before drawing
{
if(pRenderTarget == NULL) //if it exists, do not create again
{
HRESULT hr = pD2DFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(m_hWnd,
D2D1::SizeU((UINT32)m_pRect.right, (UINT32)m_pRectWindow.bottom)),
&pRenderTarget);
}
}
OnPaint() method should be something like this,
void CPlotterView::OnPaint_D2D()
{
HRESULT hr;
hr = Create_D2D_Device_Dep();
if (SUCCEEDED(hr))
{
//pRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
pRenderTarget->BeginDraw();
D2D1_SIZE_F renderTargetSize = pRenderTarget->GetSize();
pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::LightYellow));
// Drawlines or ellipses using pRenderTarget->DrawEllipse() or something
hr = pRenderTarget->EndDraw();
}
if (hr == D2DERR_RECREATE_TARGET)
{
Free_D2D_Device_Dep();
}
else
{
::ValidateRect(m_hWnd, NULL);
}
}
At the end, do not forget to close/release the resources, in the same order you opened.
I'm developing an application which will display information derived from the EDID blocks (monitor model, ID, S/N, etc.) on a dialog on the corresponding monitor.
This code works for finding the EDID information for displays. It extracts the EDID information by enumerating the DISPLAY keys under HKLM\SYSTEM\CurrentControlSet\Enum\DISPLAY\[Monitor]\[PnPID]\Device Parameters\EDID.
Update: The above code is relying on "side effects" of PnP use of the registry. I am now using the SetupAPI to enumerate monitors, which correctly handles monitors being attached/removed (unlike the code from the link above.)
I am trying to correlate each Screen in Windows.Forms.Screen.AllScreens[] (\\.\DISPLAY1, \\.\DISPLAY2, etc.) with the entries returned from the above registry inspection.
Note: In the code block below, DisplayDetails.GetMonitorDetails() has now been replaced with more robust registry enumeration code using the SetupAPI, but the data returned is the same.
e.g.
private void Form1_Load(object sender, EventArgs e)
{
Console.WriteLine("Polling displays on {0}:", System.Environment.MachineName);
int i = 0;
foreach ( DisplayDetails dd in DisplayDetails.GetMonitorDetails())
{
Console.WriteLine( "Info: Model: {0}, MonitorID: {1}, PnPID: {2}, Serial#:{3}", dd.Model, dd.MonitorID, dd.PnPID, dd.SerialNumber );
Console.WriteLine( "Does this correlate to Screen: {0}?", Screen.AllScreens[i++].DeviceName );
}
}
Output:
Info: Model: DELL P2411H, MonitorID: DELA06E, PnPID: 5&2e2fefea&0&UID1078018, Serial#:F8NDP0C...PU
Does this correlate to Screen: \\.\DISPLAY1?
Info: Model: DELL P2411H, MonitorID: DELA06E, PnPID: 5&2e2fefea&0&UID1078019, Serial#:F8NDP0C...AU
Does this correlate to Screen: \\.\DISPLAY2?
Answer: NO
In testing, I've found these don't reliably correlate (I have a system in which the first display enumerated is \\.\DISPLAY2).
My Question:
Is there a way to reliably get the EDID information for a given Forms.Screen? I can get the EDID block, but have found no path to correlate this up to the UI top-level Form. Prompting the user is undesirable, as in my use case the two (or more) monitors will likely be the same model and resolution, and only differ by a few digits in the S/N.
I've looked for paths following the Forms.Screen API, Win32 EnumDisplay, other registry GUIDs (PnP and driver-related), but haven't found any promising paths.
I have also investigated the WMI Win32_DesktopMonitor API (Windows 7), however it doesn't appear to have any more information that would help me correlate it to the Windows.Forms.Screen.AllScreens[] entries.
I suspect if there is a way to do this, it's through the SetupAPI, however I haven't found it yet.
A method to resolve the GDI to SetupAPI is available in the EnumDisplayDevices API. If you pass in the EDD_GET_DEVICE_INTERFACE_NAME in for dwFlags, the monitor enumeration will return DeviceID information of the form:
Monitor 0 info:
DeviceName: \\.\DISPLAY1
MonitorInfo: Dell P2411H(Digital)
DeviceID: \\?\DISPLAY#DELA06E#5&2e2fefea&0&UID1078018#{e6f07b5f-ee97-4a90-b076-3
3f57bf4eaa7}
Monitor 1 info:
DeviceName: \\.\DISPLAY2
MonitorInfo: Dell P2411H(Digital)
DeviceID: \\?\DISPLAY#DELA06E#5&2e2fefea&0&UID1078019#{e6f07b5f-ee97-4a90-b076-3
3f57bf4eaa7}
The DeviceID fields now match the results from the didd.DevicePath, as retrieved in the C# fragment below:
Guid MonitorGUID = new Guid(Win32.GUID_DEVINTERFACE_MONITOR);
// We start at the "root" of the device tree and look for all
// devices that match the interface GUID of a monitor
IntPtr h = Win32.SetupDiGetClassDevs(ref MonitorGUID, IntPtr.Zero, IntPtr.Zero, (uint)(Win32.DIGCF_PRESENT | Win32.DIGCF_DEVICEINTERFACE));
if (h.ToInt64() != Win32.INVALID_HANDLE_VALUE)
{
bool Success = true;
uint i = 0;
while (Success)
{
// create a Device Interface Data structure
Win32.SP_DEVICE_INTERFACE_DATA dia = new Win32.SP_DEVICE_INTERFACE_DATA();
dia.cbSize = (uint)Marshal.SizeOf(dia);
// start the enumeration
Success = Win32.SetupDiEnumDeviceInterfaces(h, IntPtr.Zero, ref MonitorGUID, i, ref dia);
if (Success)
{
// build a DevInfo Data structure
Win32.SP_DEVINFO_DATA da = new Win32.SP_DEVINFO_DATA();
da.cbSize = (uint)Marshal.SizeOf(da);
// build a Device Interface Detail Data structure
Win32.SP_DEVICE_INTERFACE_DETAIL_DATA didd = new Win32.SP_DEVICE_INTERFACE_DETAIL_DATA();
didd.cbSize = (uint)(4 + Marshal.SystemDefaultCharSize); // trust me :)
// now we can get some more detailed information
uint nRequiredSize = 0;
uint nBytes = Win32.BUFFER_SIZE;
if (Win32.SetupDiGetDeviceInterfaceDetail(h, ref dia, ref didd, nBytes, out nRequiredSize, ref da))
{
// Now we get the InstanceID
IntPtr ptrInstanceBuf = Marshal.AllocHGlobal((int)nBytes);
Win32.CM_Get_Device_ID(da.DevInst, ptrInstanceBuf, (int)nBytes, 0);
string InstanceID = Marshal.PtrToStringAuto(ptrInstanceBuf);
Console.WriteLine("InstanceID: {0}", InstanceID );
Marshal.FreeHGlobal(ptrInstanceBuf);
Console.WriteLine("DevicePath: {0}", didd.DevicePath );
}
i++;
}
}
}
Win32.SetupDiDestroyDeviceInfoList(h);
}
Sample Output:
InstanceID: DISPLAY\DELA06E\5&2E2FEFEA&0&UID1078018
DevicePath: \\?\display#dela06e#5&2e2fefea&0&uid1078018#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
The DeviceName from the original EnumDisplayDevices matches the Forms.Screen.DeviceName property.
With these two pieces of information, it is now possible to read the EDID block during the SetupDIEnumDeviceInterface traversal using a fragment like the below:
private static byte[] GetMonitorEDID(IntPtr pDevInfoSet, SP_DEVINFO_DATA deviceInfoData)
{
IntPtr hDeviceRegistryKey = SetupDiOpenDevRegKey(pDevInfoSet, ref deviceInfoData,
DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_QUERY_VALUE);
if (hDeviceRegistryKey == IntPtr.Zero)
{
throw new Exception("Failed to open a registry key for device-specific configuration information");
}
IntPtr ptrBuff = Marshal.AllocHGlobal((int)256);
try
{
RegistryValueKind lpRegKeyType = RegistryValueKind.Binary;
int length = 256;
uint result = RegQueryValueEx(hDeviceRegistryKey, "EDID", 0, ref lpRegKeyType, ptrBuff, ref length);
if (result != 0)
{
throw new Exception("Can not read registry value EDID for device " + deviceInfoData.ClassGuid);
}
}
finally
{
RegCloseKey(hDeviceRegistryKey);
}
byte[] edidBlock = new byte[256];
Marshal.Copy(ptrBuff, edidBlock, 0, 256);
Marshal.FreeHGlobal(ptrBuff);
return edidBlock;
}
Which, finally, can be parsed for the VESA descriptor blocks, as shown in the DisplayDetails.GetMonitorDetails() method in this code.
I'm attempting to emulate user clicking and mouse-moving, specifically in Mozilla Firefox, in a Windows 7 environment. The solution I frankensteined together from various tutorials, forum posts, and MSDN documents works for 99% of Windows Applications out there, but it won't work for Firefox 8.0.
From my preliminary research, the most accurate (low-level) way of emulating keyboard and mouse input in Windows is to use the SendInput function from the User32.dll Windows Library. To test this, I wrote a short C# program to loop through and make a call to SendInput that creates a programmatic mouse-click every 5 seconds, no matter where the mouse cursor is on screen.
Once running, the program perfectly emulates a mouseclick for almost every application window I switch focus to, even including the Windows interface itself (Start Button, Taskbar, Windows Explorer, etc.), but no programmatic mouse-clicks occur when I bring the cursor into a Mozilla Firefox Window.
In order to get a better handle on what was happening under the hood, I booted up Microsoft Spy++ and began inspecting what messages were actually getting passed to the Firefox window's message queue. Sure enough, the Firefox window would receive no messages even though my cursor would be situated directly above it while it had focus. When I manually clicked my mouse, the Firefox Spy++ listener would then go nuts and display the full "nHittest:HTCLIENT wMouseMsg:WM_LBUTTONDOWN" that I saw when observing other applications' correct responses to my emulation program.
Can anyone provide an explanation for how/why Mozilla Firefox is one of the only applications that does not even receive any messages at all from the SendInput function and perhaps a suggestion for how to overcome this?
Source Code
(Imports/external references removed for clarity):
static void Main(string[] args)
{
for (; ; )
{
System.Threading.Thread.Sleep(5000);
INPUT[] inp = new INPUT[2];
inp[0].type = INPUT_MOUSE;
inp[0].mi = createMouseInput(0, 0, 0, 0, MOUSEEVENTF_LEFTDOWN);
inp[1].type = INPUT_MOUSE;
inp[1].mi = createMouseInput(0, 0, 0, 0, MOUSEEVENTF_LEFTUP);
SendInput((uint)inp.Length, inp, Marshal.SizeOf(inp[0].GetType()));
}
}
private static MOUSEINPUT createMouseInput(int x, int y, uint data, uint t, uint flag)
{
MOUSEINPUT mi = new MOUSEINPUT();
mi.dx = x;
mi.dy = y;
mi.mouseData = data;
mi.time = t;
mi.dwFlags = flag;
return mi;
}
[StructLayout(LayoutKind.Sequential)]
struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Explicit)]
private struct INPUT
{
[FieldOffset(0)]
public int type;
[FieldOffset(sizeof(int))] //[FieldOffset(8)] for x64
public MOUSEINPUT mi;
[FieldOffset(sizeof(int))] //[FieldOffset(8)] for x64
public KEYBDINPUT ki;
[FieldOffset(sizeof(int))] //[FieldOffset(8)] for x64
public HARDWAREINPUT hi;
}
Did you run your program as an administrator? It could be an UIPI, i.e. integrity level issue.