How to take the snapshot of a IE webpage through a BHO (C#) - bho

I am trying to build an IE BHO in C# for taking the snapshot of a webpage loaded in the IE browser. Here is what I'm trying to do:
public class ShowToolbarBHO : BandObjectLib.IObjectWithSite
{
IWebBrowser2 webBrowser = null;
public void SetSite (Object site)
{
.......
if (site != null)
{
......
webBrowser = (IWebBrowser2)site;
......
}
}
}
Also, I p/invoke the following COM methods:
[Guid("0000010D-0000-0000-C000-000000000046")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
[ComImportAttribute()]
public interface IViewObject
{
void Draw([MarshalAs(UnmanagedType.U4)] int dwDrawAspect, int lindex, IntPtr pvAspect, [In] IntPtr ptd, IntPtr hdcTargetDev, IntPtr hdcDraw, [MarshalAs(UnmanagedType.LPStruct)] ref COMRECT lprcBounds, [In] IntPtr lprcWBounds, IntPtr pfnContinue, int dwContinue);
int GetColorSet([MarshalAs(UnmanagedType.U4)] int dwDrawAspect, int lindex, IntPtr pvAspect, [In] IntPtr ptd, IntPtr hicTargetDev, [Out] IntPtr ppColorSet);
int Freeze([MarshalAs(UnmanagedType.U4)] int dwDrawAspect, int lindex, IntPtr pvAspect, out IntPtr pdwFreeze);
int Unfreeze([MarshalAs(UnmanagedType.U4)] int dwFreeze);
int SetAdvise([MarshalAs(UnmanagedType.U4)] int aspects, [MarshalAs(UnmanagedType.U4)] int advf, [MarshalAs(UnmanagedType.Interface)] IAdviseSink pAdvSink);
void GetAdvise([MarshalAs(UnmanagedType.LPArray)] out int[] paspects, [MarshalAs(UnmanagedType.LPArray)] out int[] advf, [MarshalAs(UnmanagedType.LPArray)] out IAdviseSink[] pAdvSink);
}
[StructLayoutAttribute(LayoutKind.Sequential)]
public class COMRECT
{
public int left;
public int top;
public int right;
public int bottom;
public COMRECT()
{
}
public COMRECT(int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
}
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisibleAttribute(true)]
[GuidAttribute("0000010F-0000-0000-C000-000000000046")]
[ComImportAttribute()]
public interface IAdviseSink
{
void OnDataChange([In]IntPtr pFormatetc, [In]IntPtr pStgmed);
void OnViewChange([MarshalAs(UnmanagedType.U4)] int dwAspect, [MarshalAs(UnmanagedType.I4)] int lindex);
void OnRename([MarshalAs(UnmanagedType.Interface)] object pmk);
void OnSave();
void OnClose();
}
Now When I take the snapshot:
I make a call CaptureWebScreenImage((IHTMLDocument2) webBrowser.document);
public static Image CaptureWebScreenImage(IHTMLDocument2 myDoc) {
int heightsize = (int)getDocumentAttribute(myDoc, "scrollHeight");
int widthsize = (int)getDocumentAttribute(myDoc, "scrollWidth");
Bitmap finalImage = new Bitmap(widthsize, heightsize);
Graphics gFinal = Graphics.FromImage(finalImage);
COMRECT rect = new COMRECT();
rect.left = 0;
rect.top = 0;
rect.right = widthsize;
rect.bottom = heightsize;
IntPtr hDC = gFinal.GetHdc();
IViewObject vO = myDoc as IViewObject;
vO.Draw(1, -1, (IntPtr)0, (IntPtr)0, (IntPtr)0, (IntPtr)hDC, ref rect, (IntPtr)0, (IntPtr)0, 0);
gFinal.ReleaseHdc();
gFinal.Dispose();
return finalImage;
}
I am not getting the image of the webpage. Rather I am getting an image with black background. I am not sure if this is the right way of doing it, but I found over the web that IViewObject::Draw method is used for taking the image of a webpage in IE.
I was earlier doing the image capture using the Native PrintWindow() method as mentioned in the following codeproject's page - http://www.codeproject.com/KB/graphics/IECapture.aspx
But the image size is humongous! I was trying to see if I can reduce the size by using other techniques. It would be great if someone can point out the mistakes (I am sure there would be many) in my code above.
Thanks,
Kapil

You can try to query IHTMLElementRender interface for the document element (which is available through IHTMLDocument3 interface) and use DrawToDC method to render page to a bitmap.

1 IHTMLElementRender::DrawToDC is deprecated, according to Microsoft's documentation. And it does not draw object and canvas, like flash, as far as I can see.
It's better to cal IViewObject::Draw() or ::OleDraw().
2 I dont know how to do it in C#, but I know how to do it in win32 program. In win32 program, you need to call IOleInplaceObject::SetObjectRect to set IWebBrowser's position and size.
Or SetClientSite(), I am sure one of them does the magic.
CComQIPtr oleObject = browser;
CComQIPtr<IOleInPlaceObject> inPlaceObject(mOleObject);
inPlaceObject->SetObjectRects(&rcClient, &rcClient);
oleObject ->SetClientSite(clientSite);
Hope it helps

Related

Removing horizontal scrollbar from list control in report mode [duplicate]

I've got a ListView control in Details mode with a single column. It's on a form that is meant to only be used with the keyboard, mostly with the up/down arrows for scrolling and enter to select. So I don't really need to have the scroll bars and would just like them to not show for a cleaner look. However, when I set the ListView.Scrollable property to false, I can still move the selected item up and down, but as soon as it moves to an item not currently in view, the list won't move to show that item. I've tried using EnsureVisible to programmatically scroll the list, but it does nothing when in this mode.
Is there any way to manually move the list up and down to scroll, but without having the scrollbar present?
It's not easy but it can be done. If you try to hide the scroll bar through ShowScrollBar, the ListView will simply put it back again. So you have to do something more devious.
You will have to intercept the WM_NCCALCSIZE message, and in there, turn off the vertical scroll style. Whenever the listview tries to turn it on again, you will turn it off again in this handler.
public class ListViewWithoutScrollBar : ListView
{
protected override void WndProc(ref Message m) {
switch (m.Msg) {
case 0x83: // WM_NCCALCSIZE
int style = (int)GetWindowLong(this.Handle, GWL_STYLE);
if ((style & WS_VSCROLL) == WS_VSCROLL)
SetWindowLong(this.Handle, GWL_STYLE, style & ~WS_VSCROLL);
base.WndProc(ref m);
break;
default:
base.WndProc(ref m);
break;
}
}
const int GWL_STYLE = -16;
const int WS_VSCROLL = 0x00200000;
public static int GetWindowLong(IntPtr hWnd, int nIndex) {
if (IntPtr.Size == 4)
return (int)GetWindowLong32(hWnd, nIndex);
else
return (int)(long)GetWindowLongPtr64(hWnd, nIndex);
}
public static int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong) {
if (IntPtr.Size == 4)
return (int)SetWindowLongPtr32(hWnd, nIndex, dwNewLong);
else
return (int)(long)SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
}
[DllImport("user32.dll", EntryPoint = "GetWindowLong", CharSet = CharSet.Auto)]
public static extern IntPtr GetWindowLong32(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", CharSet = CharSet.Auto)]
public static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowLongPtr32(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, int dwNewLong);
}
This will give you a ListView without scroll bars that still scrolls when you use the arrow keys to change selection.
i did something more easy. i left scrollable to true and used a custom slider(colorSlider) that i found on codeproject and i drawed the slider over the position where the vscroller would appear and then used the ensureVisible function.
Call the ShowScrollBar API method.
If ShowScrollBar doesn't work, I'm not sure how to do it.
You could put the ListView in a panel and make the ListView wider than the panel so that the scrollbar is cut off (check SystemInformation.VerticalScrollBarWidth), but that's a horrifyingly ugly hack.
Since ShowScrollBar didn't work, maybe this helps:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
private const int WM_VSCROLL = 0x115;
private const int SB_LINEDOWN = 1;
public Form1()
{
InitializeComponent();
for (int i = 0; i < 100; i++)
listView1.Items.Add("foo" + i);
listView1.Scrollable = false;
}
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
SendMessage(listView1.Handle, WM_VSCROLL, SB_LINEDOWN, 0);
}
You can use ListView.Scrollable Property. Set it to false and Scroll bars won't appear!

In Windows 10, how can we determine which virtual desktop a window belongs to?

Concerning Windows 10 and its new virtual desktop feature, is there a way to determine which virtual desktop a particular window belongs to? And, which virtual desktop is active?
The problem can be seen using the Snipping Tool. Open the tool and select a New / Window Snip. As you move the mouse around, the snipping tool highlights areas where there is no window, but there is a window at that location on another virtual desktop.
In this picture, the Snipping Tool is highlighting an empty spot.
Snipping Tool doesn't know which virtual desktop a particular window is on.
Here's the same question on MSDN Forums, unanswered, but with lots of additional detail.
Sorry, my status isn't high enough to insert images or include more links.
The Windows SDK Support Team Blog posted a C# demo to switch Desktops via IVirtualDesktopManager:
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("a5cd92ff-29be-454c-8d04-d82879fb3f1b")]
[System.Security.SuppressUnmanagedCodeSecurity]
public interface IVirtualDesktopManager
{
[PreserveSig]
int IsWindowOnCurrentVirtualDesktop(
[In] IntPtr TopLevelWindow,
[Out] out int OnCurrentDesktop
);
[PreserveSig]
int GetWindowDesktopId(
[In] IntPtr TopLevelWindow,
[Out] out Guid CurrentDesktop
);
[PreserveSig]
int MoveWindowToDesktop(
[In] IntPtr TopLevelWindow,
[MarshalAs(UnmanagedType.LPStruct)]
[In]Guid CurrentDesktop
);
}
[ComImport, Guid("aa509086-5ca9-4c25-8f95-589d3c07b48a")]
public class CVirtualDesktopManager
{
}
public class VirtualDesktopManager
{
public VirtualDesktopManager()
{
cmanager = new CVirtualDesktopManager();
manager = (IVirtualDesktopManager)cmanager;
}
~VirtualDesktopManager()
{
manager = null;
cmanager = null;
}
private CVirtualDesktopManager cmanager = null;
private IVirtualDesktopManager manager;
public bool IsWindowOnCurrentVirtualDesktop(IntPtr TopLevelWindow)
{
int result;
int hr;
if ((hr = manager.IsWindowOnCurrentVirtualDesktop(TopLevelWindow, out result)) != 0)
{
Marshal.ThrowExceptionForHR(hr);
}
return result != 0;
}
public Guid GetWindowDesktopId(IntPtr TopLevelWindow)
{
Guid result;
int hr;
if ((hr = manager.GetWindowDesktopId(TopLevelWindow, out result)) != 0)
{
Marshal.ThrowExceptionForHR(hr);
}
return result;
}
public void MoveWindowToDesktop(IntPtr TopLevelWindow, Guid CurrentDesktop)
{
int hr;
if ((hr = manager.MoveWindowToDesktop(TopLevelWindow, CurrentDesktop)) != 0)
{
Marshal.ThrowExceptionForHR(hr);
}
}
}
Call GetWindowDesktopId to get the Desktop GUID.

Powershell - Make Desktop Background changes take effect immediately

I am running a powershell script to change the background to a certain set of colors. I would like to do this without rebooting, but unfortunately cannot get the changes to take effect immediately on a windows 7/8 platform. I've found many solutions online but I cannot find one that works for me. I think it may have something to do with setting the SystemParametersInfo, but I don't know for sure. I've seen a few solutions and tried them out for myself, but I can't get them to work either. The registry keys update just find but the changes don't take effect until after I reboot. Below is what I have so far, if anyone seeing anything I could do different I would appreciate the help!
backgroundtest.ps1
Add-Type #"
using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace Background
{
public class Setter {
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SystemParametersInfo(int uAction, int uParm, string lpvParam, int fuWinIni);
public const int UpdateIniFile = 0x01;
public const int SendWinIniChange = 0x02;
public const int SetDesktopBackground = 20; <# following examples online to set parameters #>
public static void SetBackground() {
SystemParametersInfo(SetDesktopBackground, 0, "", UpdateIniFile | SendWinIniChange);
RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true);
key.SetValue(#"WallPaper", 0); <#remove wallpaper#>
RegistryKey key2 = Registry.CurrentUser.OpenSubKey("Control Panel\\Colors", true);
key2.SetValue(#"Background", "0 118 163"); <#set background to new color>
key.Close();
key2.Close();
}
}
}
"#
[Background.Setter]::SetBackground()
The documented way to change the system colours is the SetSysColors function.
This sends the WM_SYSCOLORCHANGE message to all top-level windows to notify them of the change.
I've updated your class to clear the background and set the colour to purple. It would need copying into your PowerShell stuff. Note that the way I've declared SetSysColors you can only change one colour at a time.
public class Setter {
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SystemParametersInfo(int uAction, int uParm, string lpvParam, int fuWinIni);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SetSysColors(int count, [In] ref int index, [In] ref int colour);
public const int UpdateIniFile = 0x01;
public const int SendWinIniChange = 0x02;
public const int SetDesktopBackground = 20;
public const int COLOR_BACKGROUND = 1;
public static void SetBackground() {
SystemParametersInfo(SetDesktopBackground, 0, "", UpdateIniFile | SendWinIniChange);
int index = COLOR_BACKGROUND;
int colour = 0xFF00FF;
SetSysColors(1, ref index, ref colour);
}
}
Yesterday was my first Powershell experience and I was pretty lost with what I needed to do. In order to change the desktop background to a solid color, you first need to remove the wall paper and then you can use the SetSysColors function to immediately change the desktop background. This link helped me out tremendously. http://gallery.technet.microsoft.com/scriptcenter/Change-window-borderdesktop-609a6fb2
Hopefully this helps someone the same way it helped me.
Updated Code
$code = #'
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace Background
{
public class Setter {
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SystemParametersInfo(int uAction, int uParm, string lpvParam, int fuWinIni);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError =true)]
private static extern int SetSysColors(int cElements, int[] lpaElements, int[] lpRgbValues);
public const int UpdateIniFile = 0x01;
public const int SendWinIniChange = 0x02;
public const int SetDesktopBackground = 0x0014;
public const int COLOR_DESKTOP = 1;
public int[] first = {COLOR_DESKTOP};
public static void RemoveWallPaper() {
SystemParametersInfo( SetDesktopBackground, 0, "", SendWinIniChange | UpdateIniFile );
RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true);
key.SetValue(#"WallPaper", 0);
key.Close();
}
public static void SetBackground(byte r, byte g, byte b) {
RemoveWallPaper();
System.Drawing.Color color= System.Drawing.Color.FromArgb(r,g,b);
int[] elements = {COLOR_DESKTOP};
int[] colors = { System.Drawing.ColorTranslator.ToWin32(color) };
SetSysColors(elements.Length, elements, colors);
RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel\\Colors", true);
key.SetValue(#"Background", string.Format("{0} {1} {2}", color.R, color.G, color.B));
key.Close();
}
}
}
'#
Add-Type -TypeDefinition $code -ReferencedAssemblies System.Drawing.dll -PassThru
Function Set-OSCDesktopColor
{
<# Powershell function to remove desktop background and set background to colors we want #>
Process
{
[Background.Setter]::SetBackground(0,118,163)
return
}
}
Set-OSCDesktopColor
I grabbed Wheatfairies code, updated it and published in (with Attribution) to the PowerShell Gallery at:
https://www.powershellgallery.com/packages/Set-DesktopBackGround/1.0.0.0/DisplayScript
So now everyone can just type:
Install-Script -Name Set-DesktopBackGround
to get it.
Cheers!

GetPhysicalMonitorsFromHMONITOR returned handle is always null

On the Media Foundation SDK there is the GetPhysicalMonitorsFromHMONITOR function
that I am trying to implement using C# but with no luck ...
In the returned PHYSICAL_MONITOR[], the function returns the string description of the monitor but for some mysterious reasons, the hPhysicalMonitor handle remains at 0.
I have generated the signatures with P/Invoke Interop Assistant with minor modifications.
Does the PHYSICAL_MONITOR structure or anything else needs further tuning ?
Thank you.
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using WindowsFormsApplication1;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public enum MC_DISPLAY_TECHNOLOGY_TYPE
{
MC_SHADOW_MASK_CATHODE_RAY_TUBE,
MC_APERTURE_GRILL_CATHODE_RAY_TUBE,
MC_THIN_FILM_TRANSISTOR,
MC_LIQUID_CRYSTAL_ON_SILICON,
MC_PLASMA,
MC_ORGANIC_LIGHT_EMITTING_DIODE,
MC_ELECTROLUMINESCENT,
MC_MICROELECTROMECHANICAL,
MC_FIELD_EMISSION_DEVICE,
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct PHYSICAL_MONITOR
{
public IntPtr hPhysicalMonitor;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string szPhysicalMonitorDescription;
}
#region Imports
[DllImport("user32.dll", EntryPoint = "MonitorFromWindow")]
public static extern IntPtr MonitorFromWindow(
[In] IntPtr hwnd, uint dwFlags);
[DllImport("dxva2.dll", EntryPoint = "GetMonitorTechnologyType")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetMonitorTechnologyType(
IntPtr hMonitor, ref MC_DISPLAY_TECHNOLOGY_TYPE pdtyDisplayTechnologyType);
[DllImport("dxva2.dll", EntryPoint = "GetMonitorCapabilities")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetMonitorCapabilities(
IntPtr hMonitor, ref uint pdwMonitorCapabilities, ref uint pdwSupportedColorTemperatures);
[DllImport("dxva2.dll", EntryPoint = "DestroyPhysicalMonitors")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DestroyPhysicalMonitors(
uint dwPhysicalMonitorArraySize, ref PHYSICAL_MONITOR[] pPhysicalMonitorArray);
[DllImport("dxva2.dll", EntryPoint = "GetNumberOfPhysicalMonitorsFromHMONITOR")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetNumberOfPhysicalMonitorsFromHMONITOR(
IntPtr hMonitor, ref uint pdwNumberOfPhysicalMonitors);
[DllImport("dxva2.dll", EntryPoint = "GetPhysicalMonitorsFromHMONITOR")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetPhysicalMonitorsFromHMONITOR(
IntPtr hMonitor, uint dwPhysicalMonitorArraySize, [Out] PHYSICAL_MONITOR[] pPhysicalMonitorArray);
#endregion
public Form1() { InitializeComponent(); }
private void Form1_Load(object sender, EventArgs e)
{
// Get monitor handle.
uint dwFlags = 0u;
IntPtr ptr = MonitorFromWindow(Handle, dwFlags);
// Get number of physical monitors.
uint pdwNumberOfPhysicalMonitors = 0u;
bool b1 = GetNumberOfPhysicalMonitorsFromHMONITOR(ptr, ref pdwNumberOfPhysicalMonitors);
if (b1)
{
// Get physical monitors.
uint dwPhysicalMonitorArraySize = 0u;
dwPhysicalMonitorArraySize = pdwNumberOfPhysicalMonitors;
PHYSICAL_MONITOR[] pPhysicalMonitorArray = new PHYSICAL_MONITOR[dwPhysicalMonitorArraySize];
//NOTE : Handles remain null !
bool b2 = GetPhysicalMonitorsFromHMONITOR(ptr, dwPhysicalMonitorArraySize, pPhysicalMonitorArray);
if (pPhysicalMonitorArray[0].hPhysicalMonitor
== IntPtr.Zero)
{
throw new Exception("ERROR !");
}
// Monitor has capabilities to do that ?
if (b2)
{
uint pdwMonitorCapabilities = 0u;
uint pdwSupportedColorTemperatures = 0u;
bool b3 = GetMonitorCapabilities(
ptr, ref pdwMonitorCapabilities, ref pdwSupportedColorTemperatures);
// If yes, get technology type.
if (b3)
{
MC_DISPLAY_TECHNOLOGY_TYPE type = MC_DISPLAY_TECHNOLOGY_TYPE.MC_SHADOW_MASK_CATHODE_RAY_TUBE;
bool b4 = GetMonitorTechnologyType(ptr, ref type);
if (b4)
{
// Do work.
}
else
{
throw new Exception("Couldn't get monitor technology type.");
}
}
else
{
throw new Exception("Couldn't get monitor capabilities.");
}
}
else
{
throw new Exception("The monitor doesn't have the required capabilities.");
}
bool b5 = DestroyPhysicalMonitors(dwPhysicalMonitorArraySize, ref pPhysicalMonitorArray);
if (!b5)
{
throw new Exception("Couldn't destroy physical monitors.");
}
}
else
{
throw new Exception("Couldn't get number of physical monitors.");
}
}
}
}
Your statement:
The function returns the string description of the monitor but for some mysterious reasons, the hMonitor handle remains at 0.
is correct. If you look at the docs here, you'll see that hMonitor is clearly an [in] parameter and will not be changed.
Update following comment:
Sorry, didn't realize you meant the physical handle being returned in the structure. All the information I can find on that particular problem seems to indicate that your monitor probably isn't fully DDC/CI compatible (e.g., here).
All your structure definitions look fine to me, based on the docs on MSDN for that particular call. And indeed, it is populating the description for you.
What is the value for the number of physical monitors being returned from GetNumberOfPhysicalMonitorsFromHMONITOR (pdwNumberOfPhysicalMonitors)?
Also, what is the size of your PHYSICAL_MONITOR structure and are you running in 32 or 64 bits?
It is alright that the hPhysicalMonitor value is 0. However, in the question's code sample all calls after the GetPhysicalMonitorsFromHMONITOR should use the hPhysicalMonitor reference instead of the ptr reference. The updated Form_Load method should be the following:
private void Form1_Load(object sender, EventArgs e)
{
// Get monitor handle.
uint dwFlags = 0u;
IntPtr ptr = MonitorFromWindow(Handle, dwFlags);
// Get number of physical monitors.
uint pdwNumberOfPhysicalMonitors = 0u;
bool b1 = GetNumberOfPhysicalMonitorsFromHMONITOR(ptr, ref pdwNumberOfPhysicalMonitors);
if (b1)
{
// Get physical monitors.
uint dwPhysicalMonitorArraySize = 0u;
dwPhysicalMonitorArraySize = pdwNumberOfPhysicalMonitors;
PHYSICAL_MONITOR[] pPhysicalMonitorArray = new PHYSICAL_MONITOR[dwPhysicalMonitorArraySize];
//NOTE : Handles remain null !
bool b2 = GetPhysicalMonitorsFromHMONITOR(ptr, dwPhysicalMonitorArraySize, pPhysicalMonitorArray);
// Monitor has capabilities to do that ?
if (b2)
{
uint pdwMonitorCapabilities = 0u;
uint pdwSupportedColorTemperatures = 0u;
bool b3 = GetMonitorCapabilities(pPhysicalMonitorArray[0].hPhysicalMonitor, ref pdwMonitorCapabilities, ref pdwSupportedColorTemperatures);
// If yes, get technology type.
if (b3)
{
MC_DISPLAY_TECHNOLOGY_TYPE type = MC_DISPLAY_TECHNOLOGY_TYPE.MC_SHADOW_MASK_CATHODE_RAY_TUBE;
bool b4 = GetMonitorTechnologyType(pPhysicalMonitorArray[0].hPhysicalMonitor, ref type);
if (b4)
{
// Do work.
}
else
{
throw new Exception("Couldn't get monitor technology type.");
}
}
else
{
throw new Exception("Couldn't get monitor capabilities.");
}
}
else
{
throw new Exception("The monitor doesn't have the required capabilities.");
}
bool b5 = DestroyPhysicalMonitors(dwPhysicalMonitorArraySize, ref pPhysicalMonitorArray);
if (!b5)
{
throw new Exception("Couldn't destroy physical monitors.");
}
}
else
{
throw new Exception("Couldn't get number of physical monitors.");
}
}
The monitor supports this function because with software like softMCCS and WinI2C/DDC,
the properties are returned correctly.
The return pdwNumberOfPhysicalMonitors value is 1 which is correct.
As you can see, its size is pdwNumberOfPhysicalMonitors :
PHYSICAL_MONITOR[] pPhysicalMonitorArray = new PHYSICAL_MONITOR[dwPhysicalMonitorArraySize];
And I am running Vista 32.
It is somewhat strange because half of it works, that's now about 4 days I am over it but still no progress ...
Thank you.

Is there an API for vista to detect if the desktop is running full screen?

e.g, Is the user playing a movie full screen, or looking at powerpoint in full screen mode?
I could have sworn I saw a IsFullScreenInteractive API before, but can't find it now
Here's how I've solved this problem:
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Test
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(IsForegroundWwindowFullScreen());
}
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetSystemMetrics(int smIndex);
public const int SM_CXSCREEN = 0;
public const int SM_CYSCREEN = 1;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(IntPtr hWnd, out W32RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
public struct W32RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
public static bool IsForegroundWwindowFullScreen()
{
int scrX = GetSystemMetrics(SM_CXSCREEN),
scrY = GetSystemMetrics(SM_CYSCREEN);
IntPtr handle = GetForegroundWindow();
if (handle == IntPtr.Zero) return false;
W32RECT wRect;
if (!GetWindowRect(handle, out wRect)) return false;
return scrX == (wRect.Right - wRect.Left) && scrY == (wRect.Bottom - wRect.Top);
}
}
}
Vista indeed has an API pretty much exactly for this purpose - it's called SHQueryUserNotificationState.
Use GetForegroundWindow to get a handle to the window the user is working with. GetClientRect will give the dimensions of the active part of the window sans borders; use ClientToScreen to convert the rectangle to monitor coordinates.
Call MonitorFromRect or MonitorFromWindow to get the monitor that the window is in. Use GetMonitorInfo to get the coordinates of the monitor.
Compare the two rectangles - if the window rectangle completely covers the monitor rectangle, it's a full screen window.
The preferred way of detecting the state of a window is by calling GetWindowPlacement. If you do that in conjunction with GetForegroundWindow, you can easily check if the user sees a fullscreen window or not.

Resources