Disconnect and Reconnect Displays Programmatically - c#

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.

Related

Detecting the number of connected monitors always returns 1 or more instead of 0

I have a Windows application that needs to detect the correct number of connected monitors during runtime.
So far, all of the methods that I have been able to find on the internet and Stack Overflow have failed. They all return 1, whether or not a screen is actually connected. And when 2 screens are connected, some of them correctly return 2, however, none of them return 0 when no screens are connected. I need it to return a 0 when no screen is connected.
Even a method that detects whether or not a screen is connected would work.
Below is a list of the code of the 6 methods that I have tried.
int numberOfScreens = 0;
numberOfScreens = Screen.AllScreens.Length; //1 DOESN'T WORK
numberOfScreens = SystemInformation.MonitorCount; //2 DOESN'T WORK
numberOfScreens = GetSystemMetrics(80); //3 DOESN'T WORK
/*4 DOESN'T WORK
* //This code was outside of the function
* [DllImport("user32")]
* private static extern bool EnumDisplayMonitors(IntPtr hdc, IntPtr lpRect, MonitorEnumProc callback, int dwData);
* private delegate bool MonitorEnumProc(IntPtr hDesktop, IntPtr hdc, ref Rect pRect, int dwData);
* [StructLayout(LayoutKind.Sequential)]
* private struct Rect {
* public int left;
* public int top;
* public int right;
* public int bottom;
* }
*/
int monCount = 0;
Rect r = new Rect();
MonitorEnumProc callback = (IntPtr hDesktop, IntPtr hdc, ref Rect prect, int d) => ++monCount > 0;
if (EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, callback, 0)) {
Console.WriteLine("You have {0} monitors", monCount);
numberOfScreens = monCount;
} else {
Console.WriteLine("An error occured while enumerating monitors");
}
//5 DOESN'T WORK
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_PnPEntity where service =\"monitor\"");
numberOfScreens = searcher.Get().Count;
//6 DOESN'T WORK
var active = true;
var query = "select * from WmiMonitorBasicDisplayParams";
using (var wmiSearcher = new ManagementObjectSearcher("\\root\\wmi", query)) {
var results = wmiSearcher.Get();
foreach (ManagementObject wmiObj in results) {
// get the "Active" property and cast to a boolean, which should
// tell us if the display is active. I've interpreted this to mean "on"
active = (Boolean)wmiObj["Active"];
}
}
Console.WriteLine(active);
If there are any other reliable methods of detecting the correct number of displays, I would appreciate the help.
Thank you!
On a side note, the EDID_OVERRIDE in the registry for Default_Monitor is set so that no matter what happens, the EDID doesn't change. This isn't something I can change. But it is possible that this is the reason Windows says there is a monitor when there actually isn't. This would mean that it doesn't count monitors by how many are actually connected, but by the number of displays that it thinks it's rendering to.
Given this suspicion, is there a method by which I can detect the number of monitors actually connected? Like, how many HDMI/DVI/VGA cables are plugged in, as opposed to how many screens Windows thinks it's rendering to?
After a great deal of tinkering and head scratching, I managed to create a function that reliably returns the correct number of connected screens, even if that number changes while the program is running, and even if the number of connected screens is 0.
This means it can also be used to detect whether or not a screen / display / monitor is connected to the computer at all.
Function:
using System;
using System.Management;
/// <summary>
/// Returns the number of monitors currently connected to the computer.
/// </summary>
/// <returns>(int) number of monitors</returns>
public int getNumberOfConnectedMonitors() {
int numberOfMonitors = 1;
//Detect number of monitors. However, this does NOT work when no monitors are connected. It instead gives a 1.
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_PnPEntity where service =\"monitor\"");
numberOfMonitors = searcher.Get().Count;
//Get's the monitor's instance name. "Default_Monitor" is the "monitor" Windows defaults to when nothing is connected
string activeScreen = "";
using (var wmiSearcher = new ManagementObjectSearcher("\\root\\wmi", "select * from WmiMonitorBasicDisplayParams")) {
var results = wmiSearcher.Get();
foreach (ManagementObject wmiObj in results) {
// tell us if the display is active
var active = (Boolean)wmiObj["Active"];
//Get the instance name of the active monitor
if (active) {
activeScreen = (string)wmiObj["InstanceName"];
}
}
}
//If Windows says only one monitor is connected and that monitor is Default_Monitor, then that means that there are no monitors detected by Windows
if(numberOfMonitors == 1 && activeScreen.Contains("Default_Monitor")) {
numberOfMonitors = 0;
}
return numberOfMonitors;
}
Details and Explanation (if you want that)
The above function is a Frankenstein mixture of method 5 and method 6 (seen in the question).
First it gets the number of monitors from method 5, which reliably gave the correct number when the number of connected monitors was greater than 0 (at least with the monitors that I tested). However, it returns 1 when there are actually 0 monitors connected.
Second it gets the name of one of the active monitors through a modified version of method 6. Method 6 uses the WmiMonitorBasicDisplayParams class to get info about the connected monitors. First it checks if the monitor is active. If it is, then it will get the monitor's instance name. The instance name comes in a form that looks like DISPLAY\(monitorNameHere)\(seeminglyRandomValuesHere).
If there is only one monitor connected, and that monitor's instance name contains "Default_Monitor", then that means no valid monitors are currently connected, and the function returns a 0.
"Default_Monitor" is the name that Windows gives to the "monitor" that it displays to when no monitors are detected. (at least, no monitors that give a valid EDID)
Now, it may just be that the reason the original functions weren't working for me was because I have Default_Monitor set to have an EDID of its own. However, I think that this function will still be reliable regardless.
This function is also great for detecting whether or not a screen/display/monitor is connected (because if not, it returns 0), which is something that I was also unable to find anywhere else on the internet, leading me to believe that those functions wouldn't return 0 even if I didn't have an EDID set up for Default_Monitor.
Either way, this function works reliably in my testing.

BASS WASAPI BPMCounter

I want to analyse my default playback device and detect the beats. I've been using the BASS WASAPI to get the FFT data of the selected device with:
int ret = BassWasapi.BASS_WASAPI_GetData(_fft, (int)BASSData.BASS_DATA_FFT2048);
Now I was using the data to generate spectrum data and display this to the user. In addition I want to detect the Beats using the BPMCounter Class from BASS. However as far as I can tell the BPMCounter.ProcessAudio() function requires a stream (which I don't get with WASAPI) in order to work. Is there a ways I can use BPMCounter with WASAPI? Would be great if someone can point me to the right direction. Thanks
Edit:
Tried this to convert the data to a stream, but without success:
int ret = BassWasapi.BASS_WASAPI_GetData(_fft, (int)BASSData.BASS_DATA_FFT2048); //get channel fft data
var chan = Bass.BASS_StreamCreate(0, 44100, BASSFlag.BASS_DEFAULT, BASSStreamProc.STREAMPROC_PUSH);
Bass.BASS_ChannelPlay(chan, false);
Bass.BASS_StreamPutData(chan, _fft, _fft.Length);
bool beat = _count.ProcessAudio(chan, true);
Debug.Write(beat);
beat is always False, however I can see at the Spectrum that the capturing of the FFT Data is corrent.
I've just started playing with this lib a few hours ago and i am still going through the examples. So my answer maybe is not what you want. For my project i also want to transform WASAPI into a stream and use it for a displaying a spectrum. What i did was to create a StreamPush, right after BASS_WASAPI initialization.
To init your WASAPI use this call and this delegate:
private InitWasapi()
{
WASAPIPROC _process = new WASAPIPROC(Process); // Delegate
bool res = BassWasapi.BASS_WASAPI_Init(_YourDeviceNumber, 0, 0, BASSWASAPIInit.BASS_WASAPI_BUFFER, 1f, 0f, _process, IntPtr.Zero);
if (!res)
{
// Do error checking
}
// This is the part you are looking for (maybe!)
// Use these flags because Wasapi needs 32-bit sample data
var info = BassWasapi.BASS_WASAPI_GetInfo();
_stream = Bass.BASS_StreamCreatePush(info.freq, info.chans, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT, IntPtr.Zero);
BassWasapi.BASS_WASAPI_Start();
}
private int Process(IntPtr buffer, int length, IntPtr user)
{
Bass.BASS_StreamPutData(_stream, buffer, length);
return length;
}
Please note: This works, but i am still experimenting. For example i am not getting the same spectrum output as when i create the stream from the music file itself. There are some (small) differences. Maybe it's because i am using a custom EQ in Winamp for playing the same .mp3. So if anyone knows more on this subject, i would like also to hear it!

Can I use SetErrorMode in C# process?

I'm preparing for writing an online judge core,
A program that can compile user's code and run the program to check the answer like uva online judge.
And I'm having problem in catching the exception of submit program like below.
int main()
{
while(~scanf("%d %d",n,&m))
{
printf("%d\n",n+m);
}
return 0;
}
it's access denied at first line because it scan an integer to error position.
how can I catch runtime error for my process?
I used to use "try catch" to catch the exception,
but it didn't reply anything about runtime error.
so I only check the exit code of the submit program although it's not a good method to check except for a process..
and it shows like photo
I have to close the error message box manually,
and I find a solution that is to use a SEH Handler DLL for the process.
SetErrorMode(SEM_NOGPFAULTERRORBOX);
but I don't know how to use it in C# process.
and below is my code of judger
timer = new Stopwatch();
timer.Reset();
submitProg = new Process();
submitProg.StartInfo.FileName = outputFile;
submitProg.StartInfo.UseShellExecute = false;
submitProg.StartInfo.CreateNoWindow = true;
submitProg.StartInfo.RedirectStandardInput = true;
submitProg.StartInfo.RedirectStandardOutput = true;
submitProg.StartInfo.RedirectStandardError = true;
submitProg.StartInfo.ErrorDialog = false;
submitProg.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
submitProg.EnableRaisingEvents = true;
submitProg.Start();
timer.Start();
progInput = submitProg.StandardInput;
progOutput = submitProg.StandardOutput;
progInput.Write(inputStream.ReadToEnd());
submitProg.StandardInput.Close();
while (!submitProg.HasExited)
{
peakPagedMem = submitProg.PeakPagedMemorySize64;
peakVirtualMem = submitProg.PeakVirtualMemorySize64;
peakWorkingSet = submitProg.PeakWorkingSet64;
if (peakPagedMem > memLimit)
{
submitProg.Kill();
}
if (timer.ElapsedMilliseconds > timeLimit)
{
timeLimitExceed = true;
submitProg.Kill();
}
}
timeUsed = timer.ElapsedMilliseconds;
timer.Stop();
if(submitProg.ExitCode!=0)systemRuntimeError = true;
Thanks for helping, and so sorry for my poor English.
==================================
p.s.
the question is how can I set error mode for the child process in C#.
My English is not good enough to explain the problem, so sorry.<(_ _)>
If you mean the Win32 API function SetErrorMode then you'll need to use P/Invoke, which is easy with such a simple signature:
[DllImport("kernel32.dll")]
static extern ErrorModes SetErrorMode(ErrorModes uMode);
[Flags]
public enum ErrorModes : uint {
SYSTEM_DEFAULT = 0x0,
SEM_FAILCRITICALERRORS = 0x0001,
SEM_NOALIGNMENTFAULTEXCEPT = 0x0004,
SEM_NOGPFAULTERRORBOX = 0x0002,
SEM_NOOPENFILEERRORBOX = 0x8000
}
(The Site http://www.pinvoike.net/ is always a good place to start to get the right declaration.)
You cannot set the error mode in another process without injecting code into that process. Which is impossible to do easily in C#. It isn't going to work well in C/C++ either, these kind of programs don't live long enough to give you time to inject.
It doesn't solve your problem anyway, you also have to protect against programs that never exit after they got stuck in an endless loop. The simple solution is to give every program a limited amount of time to execute. Say 10 seconds. If it doesn't complete then Process.Kill() it and declare a failure. This will also take care of the programs that bomb with a message box.
Trivial to implement with the Process.WaitForExit(milliseconds) overload.

How can a C# Windows Console application tell if it is run interactively

How can a Windows console application written in C# determine whether it is invoked in a non-interactive environment (e.g. from a service or as a scheduled task) or from an environment capable of user-interaction (e.g. Command Prompt or PowerShell)?
[EDIT: 4/2021 - new answer...]
Due to a recent change in the Visual Studio debugger, my original answer stopped working correctly when debugging. To remedy this, I'm providing an entirely different approach. The text of the original answer is included at the bottom.
1. Just the code, please...
To determine if a .NET application is running in GUI mode:
[DllImport("kernel32.dll")] static extern IntPtr GetModuleHandleW(IntPtr _);
public static bool IsGui
{
get
{
var p = GetModuleHandleW(default);
return Marshal.ReadInt16(p, Marshal.ReadInt32(p, 0x3C) + 0x5C) == 2;
}
}
This checks the Subsystem value in the PE header. For a console application, the value will be 3 instead of 2.
2. Discussion
As noted in a related question, the most reliable indicator of GUI vs. console is the "Subsystem" field in the PE header of the executable image. The following C# enum lists the allowable (documented) values:
public enum Subsystem : ushort
{
Unknown /**/ = 0x0000,
Native /**/ = 0x0001,
WindowsGui /**/ = 0x0002,
WindowsCui /**/ = 0x0003,
OS2Cui /**/ = 0x0005,
PosixCui /**/ = 0x0007,
NativeWindows /**/ = 0x0008,
WindowsCEGui /**/ = 0x0009,
EfiApplication /**/ = 0x000A,
EfiBootServiceDriver /**/ = 0x000B,
EfiRuntimeDriver /**/ = 0x000C,
EfiRom /**/ = 0x000D,
Xbox /**/ = 0x000E,
WindowsBootApplication /**/ = 0x0010,
};
As easy as that code (in that other answer) is, our case here can be vastly simplified. Since we are only specifically interested in our own running process (which is necessarily loaded), you don't have to open any file or read from the disk to obtain the subsystem value. Our executable image is guaranteed to be already mapped into memory. And it is simple to retrieve the base address for any loaded file image by calling the GetModuleHandleW function:
[DllImport("kernel32.dll")]
static extern IntPtr GetModuleHandleW(IntPtr lpModuleName);
Although we might provide a filename to this function, again things are easier and we don't have to. Passing null, or in this case, default(IntPtr.Zero) (which is the same as IntPtr.Zero), returns the base address of the virtual memory image for the current process. This eliminates the extra steps (alluded to earlier) of having to fetch the entry assembly and its Location property, etc. Without further ado, here is the new and simplified code:
static Subsystem GetSubsystem()
{
var p = GetModuleHandleW(default); // VM base address of mapped PE image
p += Marshal.ReadInt32(p, 0x3C); // RVA of COFF/PE within DOS header
return (Subsystem)Marshal.ReadInt16(p + 0x5C); // PE offset to 'Subsystem' word
}
public static bool IsGui => GetSubsystem() == Subsystem.WindowsGui;
public static bool IsConsole => GetSubsystem() == Subsystem.WindowsCui;
[official end of the new answer]
3. Bonus Discussion
For the purposes of .NET, Subsystem is perhaps the most—or only—useful piece of information in the PE Header. But depending on your tolerance for minutiae, there could be other invaluable tidbits, and it's easy to use the technique just described to retrieve additional interesting data.
Obviously, by changing the final field offset (0x5C) used earlier, you can access other fields in the COFF or PE header. The next snippet illustrates this for Subsystem (as above) plus three additional fields with their respective offsets.
NOTE: To reduce clutter, the enum declarations used in the following can be found here
var p = GetModuleHandleW(default); // PE image VM mapped base address
p += Marshal.ReadInt32(p, 0x3C); // RVA of COFF/PE within DOS header
var subsys = (Subsystem)Marshal.ReadInt16(p + 0x005C); // (same as before)
var machine = (ImageFileMachine)Marshal.ReadInt16(p + 0x0004); // new
var imgType = (ImageFileCharacteristics)Marshal.ReadInt16(p + 0x0016); // new
var dllFlags = (DllCharacteristics)Marshal.ReadInt16(p + 0x005E); // new
// ... etc.
To improve things when accessing multiple fields in unmanaged memory, it's essential to define an overlaying struct. This allows for direct and natural managed access using C#. For the running example, I merged the adjacent COFF and PE headers together into the following C# struct definition, and only included the four fields we deemed interesting:
[StructLayout(LayoutKind.Explicit)]
struct COFF_PE
{
[FieldOffset(0x04)] public ImageFileMachine MachineType;
[FieldOffset(0x16)] public ImageFileCharacteristics Characteristics;
[FieldOffset(0x5C)] public Subsystem Subsystem;
[FieldOffset(0x5E)] public DllCharacteristics DllCharacteristics;
};
NOTE: A fuller version of this struct, without the omitted fields, can be found here
Any interop struct such as this has to be properly setup at runtime, and there are many options for doing so. Ideally, its generally better to impose the struct overlay "in-situ" directly on the unmanaged memory, so that no memory copying needs to occur. To avoid prolonging the discussion here even further however, I will instead show an easier method that does involve copying.
var p = GetModuleHandleW(default);
var _pe = Marshal.PtrToStructure<COFF_PE>(p + Marshal.ReadInt32(p, 0x3C));
Trace.WriteLine($#"
MachineType: {_pe.MachineType}
Characteristics: {_pe.Characteristics}
Subsystem: {_pe.Subsystem}
DllCharacteristics: {_pe.DllCharacteristics}");
4. Output of the demo code
Here is the output when a console program is running...
MachineType: Amd64
Characteristics: ExecutableImage, LargeAddressAware
Subsystem: WindowsCui (3)
DllCharacteristics: HighEntropyVA, DynamicBase, NxCompatible, NoSeh, TSAware
...compared to GUI (WPF) application:
MachineType: Amd64
Characteristics: ExecutableImage, LargeAddressAware
Subsystem: WindowsGui (2)
DllCharacteristics: HighEntropyVA, DynamicBase, NxCompatible, NoSeh, TSAware
[OLD: original answer from 2012...]
To determine if a .NET application is running in GUI mode:
bool is_console_app = Console.OpenStandardInput(1) != Stream.Null;
Environment.UserInteractive Property
If all you're trying to do is to determine whether the console will continue to exist after your program exits (so that you can, for example, prompt the user to hit Enter before the program exits), then all you have to do is to check if your process is the only one attached to the console. If it is, then the console will be destroyed when your process exits. If there are other processes attached to the console, then the console will continue to exist (because your program won't be the last one).
For example*:
using System;
using System.Runtime.InteropServices;
namespace CheckIfConsoleWillBeDestroyedAtTheEnd
{
internal class Program
{
private static void Main(string[] args)
{
// ...
if (ConsoleWillBeDestroyedAtTheEnd())
{
Console.WriteLine("Press any key to continue . . .");
Console.ReadKey();
}
}
private static bool ConsoleWillBeDestroyedAtTheEnd()
{
var processList = new uint[1];
var processCount = GetConsoleProcessList(processList, 1);
return processCount == 1;
}
[DllImport("kernel32.dll", SetLastError = true)]
static extern uint GetConsoleProcessList(uint[] processList, uint processCount);
}
}
(*) Adapted from code found here.
I haven't tested it, but Environment.UserInteractive looks promising.
A possible improvement of Glenn Slayden's solution:
bool isConsoleApplication = Console.In != StreamReader.Null;
To prompt for user input in an interactive console, but do nothing when run without a console or when input has been redirected:
if (Environment.UserInteractive && !Console.IsInputRedirected)
{
Console.ReadKey();
}

detecting presence of bluetooth printer

I'm working on a mobile application (C#/WPF on a tablet PC) that prints to a bluetooth connected printer. Right now I just fire off a print job, and if the printer is not present the printer subsystem reports an error to the user. I'm not doing anything programatically with Bluetooth, just using PrintDialog().
I'd like to modify this process to detect the printer first - if it is not available, then I'll just store the document without printing. Is there a way in code for me to detect if the bluetooth device is connected/active/available?
If I look at the device in the Bluetooth panel under Control Panel, it doesn't seem to have any sort of status that reflects whether or not the device is available, so maybe this isn't possible.
I'm assuming the printer has already been setup and configured in Windows - all I need to do is detect if it is actually present at a given point in time.
Perhaps use the 32feet.NET library (of which I am the maintainer) and check if the printer is present before submitting the job. You'd need to know the Bluetooth address of the printer; can one get that from the system, or maybe you always know it.
Discovery on the MSFT Bluetooth stack always returns all known devices in amongst those in range :-( but we can use other means to detect the device's presence/absence. Perhaps using BluetoothDeviceInfo.GetServiceRecords in its BeginGetServiceRecords form. e.g. something like (not tested/compiled):
bool IsPresent(BluetoothAddress addr) // address from config somehow
{
BluetoothDeviceInfo bdi = new BluetoothDeviceInfo(addr);
if (bdi.Connected) {
return true;
}
Guid arbitraryClass = BluetoothService.Headset;
AsyncResult<bool> ourAr = new AsyncResult<bool>(); // Jeffrey Richter's impl
IAsyncResult ar = bdi.BeginGetService(arbitraryClass, IsPresent_GsrCallback, ourAr);
bool signalled = ourAr.AsyncWaitHandle.WaitOne(Timeout);
if (!signalled) {
return false; // Taken too long, so not in range
} else {
return ourAr.Result;
}
}
void IsPresent_GsrCallback(IAsyncResult ar)
{
AsyncResult<bool> ourAr = (AsyncResult<bool>)ar.AsyncState;
const bool IsInRange = true;
const bool completedSyncFalse = true;
try {
bdi.EndGetServiceResult(ar);
ourAr.SetAsCompleted(IsInRange, completedSyncFalse);
} catch {
// If this returns quickly, then it is in range and
// if slowly then out of range but caller will have
// moved on by then... So set true in both cases...
// TODO check what error codes we get here. SocketException(10108) iirc
ourAr.SetAsCompleted(IsInrange, completedSyncFalse);
}
}

Categories

Resources