I have an Electron app that spawns a C# app. The C# app wants to fetch the Electron BrowserWindow's MainWindowHandle, but it always returns IntPtr.Zero, and I don't know why.
The docs say:
You must use the Refresh method to refresh the Process object to get the current main window handle if it has changed.
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.
My C# app runs Refresh just in case, and my Electron window is definitely visible, and I see the icon in the taskbar:
My Electron code launches my C# app and sends it the renderer process' pid (you can download the electron-quick-start app and make the following changes to reproduce):
const mainWindow = new BrowserWindow({width: 800, height: 600, show: false});
mainWindow.once("ready-to-show", () => {
mainWindow.show();
});
mainWindow.once("show", () => {
// by now, our window should have launched, and we should have a pid for it
const windowPid = mainWindow.webContents.getOSProcessId();
const proc = cp.spawn("my/exeFile.exe");
// send the pid to the C# process
const buff = Buffer.allocUnsafe(4);
buff.writeIntLE(windowPid, 0, 4);
proc.stdin.write(buff);
});
And the C# process starts and joins a thread with an infinite loop that reads that pid and tries to get its main window handle:
byte[] buffer = new byte[4];
inStream.Read(buffer, 0, 4);
int pid = BitConverter.ToInt32(buffer, 0); // I've verified that the pid I'm sending is the pid I'm getting
Process proc = Process.GetProcessById(pid);
proc.Refresh(); // just in case
IntPtr windowHandler = proc.MainWindowHandle; // 0x00000000
IntPtr handle = proc.Handle; // 0x000004b8
Am I sending the right electron pid over? I don't see which other pid I can use. The main process pid doesn't seem right, so all I'm left with is the renderer pid, which is what I'm using.
Should I expect MainWindowHandle to be set when the window is an Electron/Chromium window, or does this only work for C# windows?
There's a BrowserWindow API for this:
win.getNativeWindowHandle()
which return the HWND you can use in any native windows code
In your case I guess you can use it like this:
byte[] bytes = new byte[8];
for (int i = 0; i < data.Length; i++) {
object item = data[i];
bytes[i] = (byte)(int)item;
}
return BitConverter.ToUInt64(bytes, 0);
Related
So I have a C# application using Mono and Gtk+2 running on Mac
Is there some way to get active application window title?
https://stackoverflow.com/a/37368813/3794943 says that I need CGWindowListCopyWindowInfo (I already have the rest of their recipe, like process identifier, front most application, etc.).
Where can I get CGWindowListCopyWindowInfo or something similar from? Or is there some other way to get active window title when using mono on mac os?
Ok, finally found it here: https://forums.xamarin.com/discussion/comment/95429/#Comment_95429
At first you import that function like this:
[DllImport(#"/System/Library/Frameworks/QuartzCore.framework/QuartzCore")]
static extern IntPtr CGWindowListCopyWindowInfo(CGWindowListOption option, uint relativeToWindow);
Then you call it like this:
string result = null;
IntPtr windowInfo = CGWindowListCopyWindowInfo(CGWindowListOption.OnScreenOnly, 0);
NSArray values = (MonoMac.Foundation.NSArray)Runtime.GetNSObject(windowInfo);
for (ulong i = 0, len = values.Count; i < len; i++)
{
NSObject window = Runtime.GetNSObject(values.ValueAt(i));
NSString key = new NSString("kCGWindowOwnerPID");
NSNumber value = (MonoMac.Foundation.NSNumber)window.ValueForKey(key);
// and so on
}
P.S. MonoMac package must be added using NuGet
Question: What is the best way to programmatically disconnect and reconnect displays programmatically?
The Goal: Kill the video output (black screen with no backlight) on a display and later turn it back on. Imagine unplugging the video cord from the monitor, then plugging it back in.
My Attempt:
// Get the monitor to disable
uint iDevNum = 0;
DISPLAY_DEVICE displayDevice = new DISPLAY_DEVICE();
displayDevice.cb = Marshal.SizeOf(displayDevice);
EnumDisplayDevices(null, iDevNum, ref displayDevice, 0))
DEVMODE devMode = new DEVMODE();
EnumDisplaySettings(displayDevice.DeviceName, 0, ref devMode);
//
// Do something here to disable this display device!
//
// Save the display settings
ChangeDisplaySettingsEx(displayDevice.DeviceName, ref devMode,
IntPtr.Zero, ChangeDisplaySettingsFlags.CDS_NONE, IntPtr.Zero);
I can interact with each display, but I can't figure out how to disconnect one.
It is similar to "Disconnect this display" in the Screen Resolution properties in Windows 7:
Notes:
Turning off video output on all displays won't work because I need the other monitors to stay on.
The desktop area on the "dead" display does NOT need to be usable when it is off. Also, it is fine if windows move around.
References:
SO: Enabling a Second Monitor
How to Turn Off a Monitor
1) Get MultiMonitorHelper from here:
https://github.com/ChrisEelmaa/MultiMonitorHelper/tree/master
2) Extend Win7Display to disconnect the display:
using MultiMonitorHelper.DisplayModels.Win7.Enum;
using MultiMonitorHelper.DisplayModels.Win7.Struct;
/// <summary>
/// Disconnect a display.
/// </summary>
public void DisconnectDisplay(int displayNumber)
{
// Get the necessary display information
int numPathArrayElements = -1;
int numModeInfoArrayElements = -1;
StatusCode error = CCDWrapper.GetDisplayConfigBufferSizes(
QueryDisplayFlags.OnlyActivePaths,
out numPathArrayElements,
out numModeInfoArrayElements);
DisplayConfigPathInfo[] pathInfoArray = new DisplayConfigPathInfo[numPathArrayElements];
DisplayConfigModeInfo[] modeInfoArray = new DisplayConfigModeInfo[numModeInfoArrayElements];
error = CCDWrapper.QueryDisplayConfig(
QueryDisplayFlags.OnlyActivePaths,
ref numPathArrayElements,
pathInfoArray,
ref numModeInfoArrayElements,
modeInfoArray,
IntPtr.Zero);
if (error != StatusCode.Success)
{
// QueryDisplayConfig failed
}
// Check the index
if (pathInfoArray[displayNumber].sourceInfo.modeInfoIdx < modeInfoArray.Length)
{
// Disable and reset the display configuration
pathInfoArray[displayNumber].flags = DisplayConfigFlags.Zero;
error = CCDWrapper.SetDisplayConfig(
pathInfoArray.Length,
pathInfoArray,
modeInfoArray.Length,
modeInfoArray,
(SdcFlags.Apply | SdcFlags.AllowChanges | SdcFlags.UseSuppliedDisplayConfig));
if (error != StatusCode.Success)
{
// SetDisplayConfig failed
}
}
}
3) Extend Win7Display to reconnect the display using an answer from this post:
using System.Diagnostics;
/// <summary>
/// Reconnect all displays.
/// </summary>
public void ReconnectDisplays()
{
DisplayChanger.Start();
}
private static Process DisplayChanger = new Process
{
StartInfo =
{
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
FileName = "DisplaySwitch.exe",
Arguments = "/extend"
}
};
4) Update the methods in IDisplay.
5) Implement the methods:
IDisplayModel displayModel = DisplayFactory.GetDisplayModel();
List<IDisplay> displayList = displayModel.GetActiveDisplays().ToList();
displayList[0].DisconnectDisplay(0);
displayList[0].ReconnectDisplays();
There's a github project that I STILL haven't got around, but it's a starting point. You need to use Win7 specific API in order to change settings. ChangeDisplaySettings won't work.
Have a look: https://github.com/ChrisEelmaa/MultiMonitorHelper
This is what you need to do:
update the IDisplay interface to support TurnOff() method,
and then call it:
var displayModel = DisplayFactory.GetDisplayModel();
var displayList = displayModel.GetActiveDisplays().ToList();
var firstDisplay = displayList[0].TurnOff();
How to implement TurnOff()? I WOULD imagine this is how(I might be wrong here now):
You need to break the connection between GPU & monitor through breaking the "paths". You can break path between source and target like this:
Call SetDisplayConfig() and pass inside specific paths and make sure you map out the DISPLAYCONFIG_PATH_ACTIVE from the DISPLAY_PATH_INFO structure flags integer.
http://msdn.microsoft.com/en-us/library/windows/hardware/ff553945(v=vs.85).aspx
Sorry for not being more helpful, but this is pretty hardcore stuff, it took me quite a while to even understand the basics of that API. It's a starting point :-),
take a look at the example how to rotate specific monitor in Win7: How do I set the monitor orientation in Windows 7?
In all honesty, just wrap the DisplaySwitch.exe for Win7, and pass /internal or /external(depending if you want to disable/enable first/second monitor), this might or might not work for >2 monitors.
I'll try and simplify the problem I'm having. Firstly, I'd like to do this is C# but I'd be perfectly fine with any solution.
There's a console application running in a CMD window on a machine that spits out a new line of text every so often. This application is extremely unstable and sometimes freezes, but the window is still responsive so Windows has no idea.
I want to be able to read the contents of this screen so I can restart the process if there hasn't been an update within a certain threshold. I can do the logic no problem, but how can I tell if the screen has any new data?
I was thinking of screen shotting the window and comparing A to B. I know how to do the screen shots but I was looking for something a bit more elegant and to be honest, something that's not as resource intensive as taking a screen shot (The system is quite taxed whilst this application is running).
The application is launched via a .bat script which is why it resides in a CMD window (though obviously the process itself is accessible).
Thank you for any help and ideas.
Another way to receive console output is attach to the cmd process:
var poList= Process.GetProcesses().Where(process => process.ProcessName.Contains("cmd"));
var a = poList.First();
//FreeConsole();//if you use console application you must free self console
AttachConsole((uint) a.Id);
var err=Marshal.GetLastWin32Error();
var ptr=GetStdHandle(-11);
SMALL_RECT srctReadRect= new SMALL_RECT
{
Top = 0,Left = 0,Bottom = 1,Right = 79
};
CHAR_INFO[,] chiBuffer = new CHAR_INFO[2,80]; // [2][80];
COORD coordBufSize= new COORD
{
X = 2,Y = 80
};
COORD coordBufCoord = new COORD
{
X = 0,
Y = 0
};
bool fSuccess;
fSuccess = ReadConsoleOutput( ptr,chiBuffer,coordBufSize,chiBuffer coordBufCoord,ref srctReadRect);
all pinvoke functions can be copied form PInvike ConsoleFunctions
Let me know if you need mode detailed information
If you able to start batch file from C# wrapper application, create process via C# app:
Process proc = new Process();
proc.StartInfo = new ProcessStartInfo("d:\\batch.bat")
{
RedirectStandardOutput = true,
UseShellExecute = false
};
bool updated = false;
Timer waitTimer= new Timer(state =>
{
if (!updated)
{
proc.Kill();
return;
}
updated = false;
});
waitTimer.Change(1000, 1000);
proc.Start();
while (! proc.StandardOutput.EndOfStream)
{
var line = proc.StandardOutput.ReadLine();
updated = true;
Console.WriteLine(line);
}
I have the following code snippet
List<String> sensitiveApps = testConnection.SelectSensitive();
foreach (string sensitiveApp in sensitiveApps)
{
Console.Write(sensitiveApp);
// retrieve applications to minimize handle (connect to database and systematically minimize all applications?)
IntPtr hwnd = UnsafeNativeMethods.FindWindow(sensitiveApp, null);
StringBuilder stringBuilder = new StringBuilder(256);
UnsafeNativeMethods.GetWindowText(hwnd, stringBuilder, stringBuilder.Capacity);
Console.WriteLine(stringBuilder.ToString());
if (!hwnd.Equals(IntPtr.Zero))
{
// SW_SHOWMAXIMIZED to maximize the window
// SW_SHOWMINIMIZED to minimize the window
// SW_SHOWNORMAL to make the window be normal size
ShowWindowAsync(hwnd, SW_SHOWMINIMIZED);
}
}
Where sensitiveApps is a list containing the strings "Notepad", "Recuva" and "VLC media player 2.0.3".
However, the only application that can be minimized using this code is Notepad. Debugging the program finds that
Console.WriteLine(stringBuilder.ToString());
would not return any value for the last 2 programs, but would return a Untitled - Notepad.
Is there anything I'm doing wrong?
Try using Spy++ and check the FindWindow names are correct.
MS Word is OpusApp and VLC is QWidget.
I have a C# .Net Winforms application, which uses LibUsbDotNet to program firmware into an USB-device (Atmel AVR32) using "DFU_DNLOAD" transfers, which is a special kind of control-transfers. This all works, BUT: A specific kind of transfer, which causes the device to erase its internal flash, fails to send an ACK within the correct timing.
When this happens, my LibUsbDotNet connection becomes irreparably broken, which causes everything to fail.
My code does the following:
int TransferToDevice(byte request, short value, byte[] data)
{
var setup = new UsbSetupPacket(
(byte)(UsbCtrlFlags.Direction_Out | UsbCtrlFlags.RequestType_Class | UsbCtrlFlags.Recipient_Interface),
request,
value,
0,
(short)data.Length);
int n;
IntPtr unmanagedPointer = System.Runtime.InteropServices.Marshal.AllocHGlobal(data.Length);
System.Runtime.InteropServices.Marshal.Copy(data, 0, unmanagedPointer, data.Length);
// UsbDevice obtained else-where
if (!UsbDevice.ControlTransfer(ref setup, unmanagedPointer, data.Length, out n))
{
n = 0;
}
System.Runtime.InteropServices.Marshal.FreeHGlobal(unmanagedPointer);
return n;
}
// In order to do a "DFU_DNLOAD", the method above is used as follows:
TransferToDevice(DFU_DNLOAD, Transactions++, data); // "data" is the payload
// where DFU_DNLOAD is:
private const byte DFU_DNLOAD = 1;
// Transactions is
short Transaction = 0;
The above code works (the device correctly receives the "DFU_DNLOAD" message), but the missing ACK is the problem. Once the error occurs, every attempt to communicate with the device (even if I try to re-initialize everything) fails, untill the device is disconnected and re-inserted...
I would like to be able to reset or re-initialize the USB-connection somehow, when this error occurs. Currently I am only able to re-establish communications with the device by exiting my application and re-starting it manually.
This was never solved to my satisfaction, ended up implementing my own "DFU" protocol ontop of LibUSB using plain C, and P/Invoke to that, avoiding LibUsbDotNet entirely... This solution seems to work.
Just guessing, but in case if data is array of short, than size of the buffer should be adjusted
int numberOfValues = data.Length;
int size = Marshal.SizeOf(typeof(short));
IntPtr unmanagedPointer = Marshal.AllocHGlobal(numberOfValues*size);
if (unmanagedPointer == IntPtr.Zero)
throw new OutOfMemoryException("Unable allocate memory");