Related
I'd like to list out all CSP and CNG providers installed on a system, but I can't find a good method for doing so in C#. For CSP, I can enumerate a certain registry key (inelegant, but functional), but I've been unable to find any way to get a list of CNG providers.
Is there anything remotely like System.Security.Cryptography.Get[CSP/CNG]Providers() or similarly logical/straightforward in .NET that could be used? Thanks!
To my knowledge, there isn't anything like that in .NET Framework.
For CSP providers, enumerate the subkeys of:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider
For CNG providers, enumerate the subkeys of:
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Cryptography\Providers
Use this to enumerate CSP providers and containers:
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
namespace CspSample
{
struct Provider
{
public string Name { get; set; }
public int Type { get; set; }
}
class CspUtils
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CryptEnumProviderTypes(
uint dwIndex,
uint pdwReserved,
uint dwFlags,
[In] ref uint pdwProvType,
StringBuilder pszTypeName,
[In] ref uint pcbTypeName);
[DllImport("Advapi32.dll")]
private static extern bool CryptEnumProviders(
int dwIndex,
IntPtr pdwReserved,
int dwFlags,
ref int pdwProvType,
StringBuilder pszProvName,
ref int pcbProvName);
public static List<Provider> ListAllProviders()
{
List<Provider> installedCSPs = new List<Provider>();
int cbName;
int dwType;
int dwIndex;
StringBuilder pszName;
dwIndex = 0;
dwType = 1;
cbName = 0;
while (CryptEnumProviders(dwIndex, IntPtr.Zero, 0, ref dwType, null, ref cbName))
{
pszName = new StringBuilder(cbName);
if (CryptEnumProviders(dwIndex++, IntPtr.Zero, 0, ref dwType, pszName, ref cbName))
{
installedCSPs.Add(new Provider { Name = pszName.ToString(), Type = dwType });
}
}
return installedCSPs;
}
const int PP_ENUMCONTAINERS = 2;
const int PROV_RSA_FULL = 1;
const int ERROR_MORE_DATA = 234;
const int ERROR_NO_MORE_ITEMS = 259;
const int CRYPT_FIRST = 1;
const int CRYPT_NEXT = 2;
//TODO: Find how to disable this flag (not machine keystore)
const int CRYPT_MACHINE_KEYSET = 0x20;
const int CRYPT_VERIFYCONTEXT = unchecked((int)0xF0000000);
public static IList<string> EnumerateKeyContainers(string providerName, int providerType)
{
ProvHandle prov;
if (!CryptAcquireContext(out prov, null, providerName, providerType, CRYPT_MACHINE_KEYSET | CRYPT_VERIFYCONTEXT))
throw new Win32Exception(Marshal.GetLastWin32Error());
List<string> list = new List<string>();
IntPtr data = IntPtr.Zero;
try
{
int flag = CRYPT_FIRST;
int len = 0;
if (!CryptGetProvParam(prov, PP_ENUMCONTAINERS, IntPtr.Zero, ref len, flag))
{
if (Marshal.GetLastWin32Error() != ERROR_MORE_DATA)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
data = Marshal.AllocHGlobal(len);
do
{
if (!CryptGetProvParam(prov, PP_ENUMCONTAINERS, data, ref len, flag))
{
if (Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS)
break;
//throw new Win32Exception(Marshal.GetLastWin32Error());
}
list.Add(Marshal.PtrToStringAnsi(data));
flag = CRYPT_NEXT;
}
while (true);
}
finally
{
if (data != IntPtr.Zero)
{
Marshal.FreeHGlobal(data);
}
prov.Dispose();
}
return list;
}
private sealed class ProvHandle : SafeHandleZeroOrMinusOneIsInvalid
{
public ProvHandle()
: base(true)
{
}
protected override bool ReleaseHandle()
{
return CryptReleaseContext(handle, 0);
}
[DllImport("advapi32.dll")]
private static extern bool CryptReleaseContext(IntPtr hProv, int dwFlags);
}
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool CryptAcquireContext(out ProvHandle phProv, string pszContainer, string pszProvider, int dwProvType, int dwFlags);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool CryptGetProvParam(ProvHandle hProv, int dwParam, IntPtr pbData, ref int pdwDataLen, int dwFlags);
}
}
I am trying to create application that get the list of processes that have systray icon.
I searched alot and found number of references:
http://www.raymond.cc/blog/find-out-what-program-are-running-at-windows-system-tray/
https://superuser.com/questions/708674/how-to-find-out-what-process-a-system-tray-icon-corresponds-to
Which Windows process is displaying a given taskbar system tray icon?
https://social.msdn.microsoft.com/Forums/vstudio/en-US/53e27f60-37af-406f-bbdc-45db2bd3dee6/how-to-find-a-system-tray-process
https://social.msdn.microsoft.com/Forums/vstudio/en-US/4c4f60ce-3573-433d-994e-9c17f95187f0/finding-which-applications-and-services-are-listed-in-the-system-tray?forum=csharpgeneral
http://www.codeproject.com/Articles/10497/A-tool-to-order-the-window-buttons-in-your-taskbar
Get ToolTip Text from Icon in System Tray
All of them are good resources but the most informative for me were 3 & 4.
In 1 we have an example for what I want.
I want the list of processes that have systray icon:
Example for application called "AnVir Task Manager"
Using the code from link 6 I succeed to iterate through the systray buttons and see the text of each button:
But I am not sure how I can find what process relates to each trayicon.
In code project he mentioned that the information that can help identify the process is the dwData but the problem is that when I found button that appears in Systray, its dwData = 0:
Code:
Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SystrayIcons
{
public partial class Form1 : Form
{
public Form1()
{
Engine.findProcessInSystray();
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Engine.findProcessInSystray();
}
}
}
Engine.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Common;
using System.Diagnostics;
using System.Collections;
namespace SystrayIcons
{
static class Engine
{
static public void findProcessInSystray()
{
IntPtr systemTrayHandle = GetSystemTrayHandle();
UInt32 count = User32.SendMessage(systemTrayHandle, TB.BUTTONCOUNT, 0, 0);
ArrayList tbButtons = new ArrayList();
List<TBBUTTON> tbButtons2 = new List<TBBUTTON>();
for (int i = 0; i < count; i++)
{
TBBUTTON tbButton = new TBBUTTON();
string text = String.Empty;
IntPtr ipWindowHandle = IntPtr.Zero;
bool b = GetTBButton(systemTrayHandle, i, ref tbButton, ref text, ref ipWindowHandle);
// if (tbButton.iBitmap != 0)
if(tbButton.dwData != 0)
{
tbButtons.Add(tbButton);
tbButtons2.Add(tbButton);
}
}
// CreateImageList();
System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcesses();
foreach (System.Diagnostics.Process process in processes)
{
if (process.MainWindowHandle == systemTrayHandle)
{
}
}
}
static IntPtr GetSystemTrayHandle()
{
IntPtr hWndTray = User32.FindWindow("Shell_TrayWnd", null);
if (hWndTray != IntPtr.Zero)
{
hWndTray = User32.FindWindowEx(hWndTray, IntPtr.Zero, "TrayNotifyWnd", null);
if (hWndTray != IntPtr.Zero)
{
hWndTray = User32.FindWindowEx(hWndTray, IntPtr.Zero, "SysPager", null);
if (hWndTray != IntPtr.Zero)
{
hWndTray = User32.FindWindowEx(hWndTray, IntPtr.Zero, "ToolbarWindow32", null);
return hWndTray;
}
}
}
return IntPtr.Zero;
}
public static unsafe bool GetTBButton(IntPtr hToolbar, int i, ref TBBUTTON tbButton, ref string text, ref IntPtr ipWindowHandle)
{
// One page
const int BUFFER_SIZE = 0x1000;
byte[] localBuffer = new byte[BUFFER_SIZE];
UInt32 processId = 0;
UInt32 threadId = User32.GetWindowThreadProcessId(hToolbar, out processId);
IntPtr hProcess = Kernel32.OpenProcess(ProcessRights.ALL_ACCESS, false, processId);
if (hProcess == IntPtr.Zero) { Debug.Assert(false); return false; }
IntPtr ipRemoteBuffer = Kernel32.VirtualAllocEx(
hProcess,
IntPtr.Zero,
new UIntPtr(BUFFER_SIZE),
MemAllocationType.COMMIT,
MemoryProtection.PAGE_READWRITE);
if (ipRemoteBuffer == IntPtr.Zero) { Debug.Assert(false); return false; }
// TBButton
fixed (TBBUTTON* pTBButton = &tbButton)
{
IntPtr ipTBButton = new IntPtr(pTBButton);
int b = (int)User32.SendMessage(hToolbar, TB.GETBUTTON, (IntPtr)i, ipRemoteBuffer);
if (b == 0) { Debug.Assert(false); return false; }
// this is fixed
Int32 dwBytesRead = 0;
IntPtr ipBytesRead = new IntPtr(&dwBytesRead);
bool b2 = Kernel32.ReadProcessMemory(
hProcess,
ipRemoteBuffer,
ipTBButton,
new UIntPtr((uint)sizeof(TBBUTTON)),
ipBytesRead);
if (!b2) { Debug.Assert(false); return false; }
}
// button text
fixed (byte* pLocalBuffer = localBuffer)
{
IntPtr ipLocalBuffer = new IntPtr(pLocalBuffer);
int chars = (int)User32.SendMessage(hToolbar, TB.GETBUTTONTEXTW, (IntPtr)tbButton.idCommand, ipRemoteBuffer);
if (chars == -1) { Debug.Assert(false); return false; }
// this is fixed
Int32 dwBytesRead = 0;
IntPtr ipBytesRead = new IntPtr(&dwBytesRead);
bool b4 = Kernel32.ReadProcessMemory(
hProcess,
ipRemoteBuffer,
ipLocalBuffer,
new UIntPtr(BUFFER_SIZE),
ipBytesRead);
if (!b4) { Debug.Assert(false); return false; }
text = Marshal.PtrToStringUni(ipLocalBuffer, chars);
if (text == " ") text = String.Empty;
}
// window handle
fixed (byte* pLocalBuffer = localBuffer)
{
IntPtr ipLocalBuffer = new IntPtr(pLocalBuffer);
// this is in the remote virtual memory space
IntPtr ipRemoteData = new IntPtr(tbButton.dwData);
// this is fixed
Int32 dwBytesRead = 0;
IntPtr ipBytesRead = new IntPtr(&dwBytesRead);
bool b4 = Kernel32.ReadProcessMemory(
hProcess,
ipRemoteData,
ipLocalBuffer,
new UIntPtr(4),
ipBytesRead);
if (!b4) { Debug.Assert(false); return false; }
if (dwBytesRead != 4) { Debug.Assert(false); return false; }
Int32 iWindowHandle = BitConverter.ToInt32(localBuffer, 0);
if (iWindowHandle == -1) { Debug.Assert(false); }//return false; }
ipWindowHandle = new IntPtr(iWindowHandle);
}
Kernel32.VirtualFreeEx(
hProcess,
ipRemoteBuffer,
UIntPtr.Zero,
MemAllocationType.RELEASE);
Kernel32.CloseHandle(hProcess);
return true;
}
}
}
Kernel32.cs
using System;
using System.Runtime.InteropServices;
namespace Common
{
//-----------------------------------------------------------------------------
// Structures
[StructLayout(LayoutKind.Sequential)]
internal struct SYSTEM_INFO
{
public _PROCESSOR_INFO_UNION uProcessorInfo;
public uint dwPageSize;
public uint lpMinimumApplicationAddress;
public uint lpMaximumApplicationAddress;
public uint dwActiveProcessorMask;
public uint dwNumberOfProcessors;
public uint dwProcessorType;
public uint dwAllocationGranularity;
public uint dwProcessorLevel;
public uint dwProcessorRevision;
}
[StructLayout(LayoutKind.Explicit)]
internal struct _PROCESSOR_INFO_UNION
{
[FieldOffset(0)]
public uint dwOemId;
[FieldOffset(0)]
public ushort wProcessorArchitecture;
[FieldOffset(2)]
public ushort wReserved;
}
[ StructLayout( LayoutKind.Sequential )]
internal struct BY_HANDLE_FILE_INFORMATION
{
public UInt32 dwFileAttributes;
public FILETIME ftCreationTime;
public FILETIME ftLastAccessTime;
public FILETIME ftLastWriteTime;
public UInt32 dwVolumeSerialNumber;
public UInt32 nFileSizeHigh;
public UInt32 nFileSizeLow;
public UInt32 nNumberOfLinks;
public UInt32 nFileIndexHigh;
public UInt32 nFileIndexLow;
}
[ StructLayout( LayoutKind.Sequential )]
internal class MEMORYSTATUSEX
{
public Int32 Length;
public Int32 MemoryLoad;
public UInt64 TotalPhysical;
public UInt64 AvailablePhysical;
public UInt64 TotalPageFile;
public UInt64 AvailablePageFile;
public UInt64 TotalVirtual;
public UInt64 AvailableVirtual;
public UInt64 AvailableExtendedVirtual;
public MEMORYSTATUSEX() { Length = Marshal.SizeOf( this ); }
private void StopTheCompilerComplaining()
{
Length = 0;
MemoryLoad = 0;
TotalPhysical = 0;
AvailablePhysical = 0;
TotalPageFile = 0;
AvailablePageFile = 0;
TotalVirtual = 0;
AvailableVirtual = 0;
AvailableExtendedVirtual = 0;
}
}
//-----------------------------------------------------------------------------
// Constants
internal class ProcessRights
{
public const UInt32 TERMINATE = 0x0001 ;
public const UInt32 CREATE_THREAD = 0x0002 ;
public const UInt32 SET_SESSIONID = 0x0004 ;
public const UInt32 VM_OPERATION = 0x0008 ;
public const UInt32 VM_READ = 0x0010 ;
public const UInt32 VM_WRITE = 0x0020 ;
public const UInt32 DUP_HANDLE = 0x0040 ;
public const UInt32 CREATE_PROCESS = 0x0080 ;
public const UInt32 SET_QUOTA = 0x0100 ;
public const UInt32 SET_INFORMATION = 0x0200 ;
public const UInt32 QUERY_INFORMATION = 0x0400 ;
public const UInt32 SUSPEND_RESUME = 0x0800 ;
private const UInt32 STANDARD_RIGHTS_REQUIRED = 0x000F0000;
private const UInt32 SYNCHRONIZE = 0x00100000;
public const UInt32 ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF;
}
internal class MemoryProtection
{
public const UInt32 PAGE_NOACCESS = 0x01 ;
public const UInt32 PAGE_READONLY = 0x02 ;
public const UInt32 PAGE_READWRITE = 0x04 ;
public const UInt32 PAGE_WRITECOPY = 0x08 ;
public const UInt32 PAGE_EXECUTE = 0x10 ;
public const UInt32 PAGE_EXECUTE_READ = 0x20 ;
public const UInt32 PAGE_EXECUTE_READWRITE = 0x40 ;
public const UInt32 PAGE_EXECUTE_WRITECOPY = 0x80 ;
public const UInt32 PAGE_GUARD = 0x100 ;
public const UInt32 PAGE_NOCACHE = 0x200 ;
public const UInt32 PAGE_WRITECOMBINE = 0x400 ;
}
internal class MemAllocationType
{
public const UInt32 COMMIT = 0x1000 ;
public const UInt32 RESERVE = 0x2000 ;
public const UInt32 DECOMMIT = 0x4000 ;
public const UInt32 RELEASE = 0x8000 ;
public const UInt32 FREE = 0x10000 ;
public const UInt32 PRIVATE = 0x20000 ;
public const UInt32 MAPPED = 0x40000 ;
public const UInt32 RESET = 0x80000 ;
public const UInt32 TOP_DOWN = 0x100000 ;
public const UInt32 WRITE_WATCH = 0x200000 ;
public const UInt32 PHYSICAL = 0x400000 ;
public const UInt32 LARGE_PAGES = 0x20000000 ;
public const UInt32 FOURMB_PAGES = 0x80000000 ;
}
[Flags]
public enum EFileAccess : uint
{
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000,
}
[Flags]
public enum EFileShare : uint
{
None = 0x00000000,
Read = 0x00000001,
Write = 0x00000002,
Delete = 0x00000004,
}
public enum ECreationDisposition : uint
{
New = 1,
CreateAlways = 2,
OpenExisting = 3,
OpenAlways = 4,
TruncateExisting = 5,
}
[Flags]
public enum EFileAttributes : uint
{
Readonly = 0x00000001,
Hidden = 0x00000002,
System = 0x00000004,
Directory = 0x00000010,
Archive = 0x00000020,
Device = 0x00000040,
Normal = 0x00000080,
Temporary = 0x00000100,
SparseFile = 0x00000200,
ReparsePoint = 0x00000400,
Compressed = 0x00000800,
Offline= 0x00001000,
NotContentIndexed = 0x00002000,
Encrypted = 0x00004000,
Write_Through = 0x80000000,
Overlapped = 0x40000000,
NoBuffering = 0x20000000,
RandomAccess = 0x10000000,
SequentialScan = 0x08000000,
DeleteOnClose = 0x04000000,
BackupSemantics = 0x02000000,
PosixSemantics = 0x01000000,
OpenReparsePoint = 0x00200000,
OpenNoRecall = 0x00100000,
FirstPipeInstance = 0x00080000
}
//-----------------------------------------------------------------------------
// Functions
internal class Kernel32
{
[DllImport("kernel32.dll")]
public static extern void GetSystemInfo(
out SYSTEM_INFO lpSystemInfo );
[ DllImport( "Kernel32.dll" ) ]
public static extern bool GetFileInformationByHandle
(
IntPtr hFile,
out BY_HANDLE_FILE_INFORMATION lpFileInformation
);
[ DllImport( "kernel32.dll", SetLastError = true ) ]
public static extern IntPtr CreateFile(
string lpFileName,
EFileAccess dwDesiredAccess,
EFileShare dwShareMode,
IntPtr lpSecurityAttributes,
ECreationDisposition dwCreationDisposition,
EFileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile );
[ DllImport( "Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode ) ]
public static extern bool CreateHardLink
(
string FileName,
string ExistingFileName,
IntPtr lpSecurityAttributes
);
[ DllImport( "Kernel32.dll" ) ]
public static extern bool Beep
(
UInt32 frequency,
UInt32 duration
);
[ DllImport( "Kernel32.dll", SetLastError = true ) ]
public static extern IntPtr OpenProcess(
uint dwDesiredAccess,
bool bInheritHandle,
uint dwProcessId );
[DllImport( "kernel32.dll", SetLastError = true ) ]
public static extern IntPtr VirtualAllocEx(
IntPtr hProcess,
IntPtr lpAddress,
UIntPtr dwSize,
uint flAllocationType,
uint flProtect);
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(
IntPtr hProcess,
IntPtr lpBaseAddress,
IntPtr lpBuffer,
UIntPtr nSize,
IntPtr lpNumberOfBytesRead );
[DllImport("kernel32.dll")]
public static extern bool VirtualFreeEx(
IntPtr hProcess,
IntPtr lpAddress,
UIntPtr dwSize,
UInt32 dwFreeType );
[DllImport("kernel32.dll")]
public static extern bool GlobalMemoryStatusEx(
MEMORYSTATUSEX buffer );
[ DllImport( "kernel32.dll", SetLastError = true ) ]
public static extern bool CloseHandle(
IntPtr hObject );
}
//-----------------------------------------------------------------------------
}
User32.cs
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Common
{
internal enum GW : uint
{
HWNDFIRST = 0,
HWNDLAST = 1,
HWNDNEXT = 2,
HWNDPREV = 3,
OWNER = 4,
CHILD = 5,
MAX = 6
}
internal class ICON
{
public const UInt32 SMALL = 0;
public const UInt32 BIG = 1;
public const UInt32 SMALL2 = 2; // XP+
}
internal enum MB : uint
{
SimpleBeep = 0xFFFFFFFF,
IconAsterisk = 0x00000040,
IconWarning = 0x00000030,
IconError = 0x00000010,
IconQuestion = 0x00000020,
OK = 0x00000000
}
internal class SW
{
public const int HIDE = 0;
public const int SHOWNORMAL = 1;
public const int NORMAL = 1;
public const int SHOWMINIMIZED = 2;
public const int SHOWMAXIMIZED = 3;
public const int MAXIMIZE = 3;
public const int SHOWNOACTIVATE = 4;
public const int SHOW = 5;
public const int MINIMIZE = 6;
public const int SHOWMINNOACTIVE = 7;
public const int SHOWNA = 8;
public const int RESTORE = 9;
public const int SHOWDEFAULT = 10;
public const int FORCEMINIMIZE = 11;
public const int MAX = 11;
}
internal class TB
{
public const uint GETBUTTON = WM.USER + 23 ;
public const uint BUTTONCOUNT = WM.USER + 24 ;
public const uint CUSTOMIZE = WM.USER + 27 ;
public const uint GETBUTTONTEXTA = WM.USER + 45 ;
public const uint GETBUTTONTEXTW = WM.USER + 75 ;
}
internal class TBSTATE
{
public const uint CHECKED = 0x01 ;
public const uint PRESSED = 0x02 ;
public const uint ENABLED = 0x04 ;
public const uint HIDDEN = 0x08 ;
public const uint INDETERMINATE = 0x10 ;
public const uint WRAP = 0x20 ;
public const uint ELLIPSES = 0x40 ;
public const uint MARKED = 0x80 ;
}
internal class WM
{
public const uint CLOSE = 0x0010;
public const uint GETICON = 0x007F;
public const uint KEYDOWN = 0x0100;
public const uint COMMAND = 0x0111;
public const uint USER = 0x0400; // 0x0400 - 0x7FFF
public const uint APP = 0x8000; // 0x8000 - 0xBFFF
}
internal class GCL
{
public const int MENUNAME = - 8;
public const int HBRBACKGROUND = -10;
public const int HCURSOR = -12;
public const int HICON = -14;
public const int HMODULE = -16;
public const int CBWNDEXTRA = -18;
public const int CBCLSEXTRA = -20;
public const int WNDPROC = -24;
public const int STYLE = -26;
public const int ATOM = -32;
public const int HICONSM = -34;
// GetClassLongPtr ( 64-bit )
private const int GCW_ATOM = -32;
private const int GCL_CBCLSEXTRA = -20;
private const int GCL_CBWNDEXTRA = -18;
private const int GCLP_MENUNAME = - 8;
private const int GCLP_HBRBACKGROUND = -10;
private const int GCLP_HCURSOR = -12;
private const int GCLP_HICON = -14;
private const int GCLP_HMODULE = -16;
private const int GCLP_WNDPROC = -24;
private const int GCLP_HICONSM = -34;
private const int GCL_STYLE = -26;
}
[ StructLayout( LayoutKind.Sequential ) ]
internal struct TBBUTTON
{
public Int32 iBitmap;
public Int32 idCommand;
public byte fsState;
public byte fsStyle;
// [ MarshalAs( UnmanagedType.ByValArray, SizeConst=2 ) ]
// public byte[] bReserved;
public byte bReserved1;
public byte bReserved2;
public UInt32 dwData;
public IntPtr iString;
};
internal class User32
{
private User32() {}
// public const UInt32 WM_USER = 0x0400;
// public const UInt32 WM_KEYDOWN = 0x0100;
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(
IntPtr hWnd,
UInt32 msg,
IntPtr wParam,
IntPtr lParam );
[DllImport("user32.dll")]
public static extern UInt32 SendMessage(
IntPtr hWnd,
UInt32 msg,
UInt32 wParam,
UInt32 lParam );
[ DllImport( "User32.dll" ) ]
public static extern bool PostMessage
(
IntPtr hWnd,
UInt32 Msg,
IntPtr wParam,
IntPtr lParam
);
[ DllImport( "User32.dll" ) ]
public static extern bool PostMessage
(
IntPtr hWnd,
UInt32 Msg,
UInt32 wParam,
UInt32 lParam
);
[ DllImport( "User32.dll" ) ]
public static extern bool MessageBeep
(
MB BeepType
);
[DllImport("user32.dll")]
public static extern bool ShowWindow
(
IntPtr hWnd,
int nCmdShow
);
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow
(
IntPtr hWnd
);
[ DllImport( "User32.dll" ) ]
public static extern IntPtr GetDesktopWindow
(
);
[ DllImport( "user32.dll", CharSet = CharSet.Unicode ) ]
public static extern IntPtr FindWindowEx(
IntPtr hwndParent,
IntPtr hwndChildAfter,
string lpszClass,
string lpszWindow);
[ DllImport( "User32.dll" ) ]
public static extern IntPtr GetWindow
(
IntPtr hWnd,
GW uCmd
);
[ DllImport( "User32.dll" ) ]
public static extern Int32 GetWindowTextLength
(
IntPtr hWnd
);
[ DllImport( "User32.dll", SetLastError = true, CharSet = CharSet.Auto ) ]
public static extern Int32 GetWindowText
(
IntPtr hWnd,
out StringBuilder lpString,
Int32 nMaxCount
);
[ DllImport( "User32.dll", CharSet = CharSet.Auto ) ]
public static extern Int32 GetClassName
(
IntPtr hWnd,
out StringBuilder lpClassName,
Int32 nMaxCount
);
// [ DllImport( "user32.dll", EntryPoint = "GetClassLongPtrW" ) ]
[ DllImport( "user32.dll" ) ]
public static extern UInt32 GetClassLong
(
IntPtr hWnd,
int nIndex
);
[DllImport("user32.dll")]
public static extern uint SetClassLong
(
IntPtr hWnd,
int nIndex,
uint dwNewLong
);
[ DllImport( "User32.dll", CharSet=CharSet.Auto ) ]
public static extern UInt32 GetWindowThreadProcessId
(
IntPtr hWnd,
// [ MarshalAs( UnmanagedType.
out UInt32 lpdwProcessId
);
// Systray icons
//[DllImport("user32.dll", SetLastError = true)]
// public static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildAfter, string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
}
}
I checked the application on 32bit and I saw that the dwData != 0.
This helped me to understand that the problem is when working on 64bit.
I replaced public UInt32 dwData; with public UInt64 dwData;.
[ StructLayout( LayoutKind.Sequential ) ]
internal struct TBBUTTON
{
public Int32 iBitmap;
public Int32 idCommand;
public byte fsState;
public byte fsStyle;
// [ MarshalAs( UnmanagedType.ByValArray, SizeConst=2 ) ]
// public byte[] bReserved;
public byte bReserved1;
public byte bReserved2;
// public UInt32 dwData;
public UInt64 dwData;
public IntPtr iString;
};
The dwData is now larger than zero.
I succeed to get the windows handle of the button associated to the process and get the process pid:
// window handle
fixed (byte* pLocalBuffer = localBuffer)
{
...
ipWindowHandle = new IntPtr(iWindowHandle);
threadId = User32.GetWindowThreadProcessId(ipWindowHandle, out processId);
data.setProcessPid(processId);
}
And the final result:
This solution doesn't find processes that are associated with hidden system tray icons, this is a new problem that I will need to explore :) .
New refernces that helped me to find the idea to this solution:
http://www.codeproject.com/Articles/10807/Shell-Tray-Info-Arrange-your-system-tray-icons
Which was the comment of someone named "mklencke" that gave code for 64bit:
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
BOOL IsWow64()
{
static bool isset = false;
static BOOL bIsWow64 = FALSE;
if (isset) {
return bIsWow64;
}
//IsWow64Process is not available on all supported versions of Windows.
//Use GetModuleHandle to get a handle to the DLL that contains the function
//and GetProcAddress to get a pointer to the function if available.
LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(
GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
if(NULL != fnIsWow64Process)
{
if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64))
{
//TODO handle error?
return FALSE;
}
}
isset = true;
return bIsWow64;
}
typedef struct _TBBUTTON64 {
int iBitmap;
int idCommand;
BYTE fsState;
BYTE fsStyle;
BYTE bReserved[6];
DWORD64 dwData;
DWORD64 iString;
} TBBUTTON64, NEAR* PTBBUTTON64, *LPTBBUTTON64;
typedef const TBBUTTON64 *LPCTBBUTTON64;
bool EnumSystemTray() {
bool bFound = false;
// find system tray window
HWND trayWnd = FindWindow(_T("Shell_TrayWnd"), NULL);
if (trayWnd) {
trayWnd = FindWindowEx(trayWnd, NULL,_T("TrayNotifyWnd"), NULL);
if (trayWnd) {
trayWnd = FindWindowEx(trayWnd, NULL,_T("SysPager"), NULL);
if (trayWnd) {
trayWnd = FindWindowEx(trayWnd, NULL,_T("ToolbarWindow32"), NULL);
bFound = true;
}
}
}
ASSERT(bFound);
DWORD dwTrayPid;
GetWindowThreadProcessId(trayWnd, &dwTrayPid);
int count = (int) SendMessage(trayWnd, TB_BUTTONCOUNT, 0, 0);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwTrayPid);
if (!hProcess) {
return true;
}
BOOL bIsWow64 = IsWow64();
SIZE_T dwSize = bIsWow64 ? sizeof(TBBUTTON64) : sizeof(TBBUTTON);
LPVOID lpData = VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
if (!lpData) {
return true;
}
// Loop through all systray icons
for (int i = 0; i < count; i++) {
HWND hwnd32;
SendMessage(trayWnd, TB_GETBUTTON, i, (LPARAM)lpData);
if ( bIsWow64 ) {
// Try to read memory from 64-bit Explorer process. Hope the address of the traybar data is below 4GB
TBBUTTON64 tbb;
if (!ReadProcessMemory(hProcess, lpData, (LPVOID)&tbb, sizeof(TBBUTTON64), NULL)) {
continue;
}
DWORD64 hwnd;
// First member of TRAYDATA structure is HWND, so we can just use the address of the struct to read the member
if (!ReadProcessMemory(hProcess, (LPCVOID)tbb.dwData, (LPVOID)&hwnd, sizeof(DWORD64), NULL)) {
continue;
}
// Hope this does not get truncated, but we shouldn't have that many windows
hwnd32 = (HWND)hwnd;
} else {
TBBUTTON tbb;
if (!ReadProcessMemory(hProcess, lpData, (LPVOID)&tbb, sizeof(TBBUTTON), NULL)) {
continue;
}
DWORD32 hwnd;
// First member of TRAYDATA structure is HWND, so we can just use the address of the struct to read the member
if (!ReadProcessMemory(hProcess, (LPCVOID)tbb.dwData, (LPVOID)&hwnd, sizeof(DWORD32), NULL)) {
continue;
}
hwnd32 = (HWND)hwnd;
}
DWORD dwProcessId = 0;
GetWindowThreadProcessId(hwnd32, &dwProcessId);
// XXX - DO SOMETHING WITH dwProcessId
}
VirtualFreeEx(hProcess, lpData, NULL, MEM_RELEASE);
return true;
}
We have a project of managing printing documents. At first I wonder why printing options couldn't be set up in single place. For example printer tray selection for first page and for other pages can be done using MS Word automation:
var doc = _applicationObject.Documents.OpenNoRepairDialog(FileName: ref sourceFile, ReadOnly: ref readOnly,
AddToRecentFiles: ref addToRecentFiles,
Visible: ref visible);
doc.PageSetup.FirstPageTray = (WdPaperTray) firstPageTrayCode;
doc.PageSetup.OtherPagesTray = (WdPaperTray) otherPagesTrayCode;
_applicationObject.ActivePrinter = printerPath;
doc.Activate();
_applicationObject.PrintOut(Background: ref backgroundPrint, FileName: sourceFile);
doc.Close(ref saveChanges, ref _missing, ref _missing);
In the code above printer tray is specified as integer because some printers have not standart values for trays (we had this issue with HP - it's tray codes described here). So we first retrieve what trays printer have, using code:
var setting = new PrinterSettings();
setting.PrinterName = myPrinterName;
foreach (PaperSource tray in setting.PaperSources)
{
Console.WriteLine("\t{0}: #{1}", tray.SourceName, tray.RawKind);
}
And this code works with no problems.
But there is no way to specify duplex and staple options here. Duplex can be done, using driver functions OpenPrinter and SetPrinter, like described here and recommended by Microsoft as well in this forum thread.
Staple is completely unclear and if somebody knows by the way how to implement this, please let me know. Using Stapling enum, like in this MSDN article is useless as it requires custom rendering of the document to print.
I described the situation and how parts were implemented. That works fine on our environment: Windows Server 2008 R2, MS Office 2010 x32, Printers HP LaserJet P2055 and Ricoh Nashuatec DSm635. Tested with native and universal PCL6/PCL5e drivers: duplex and tray selection works as expected.
But after deployment the application to client, printers (HP LaserJet 4250 and Ricoh Aficio MP C7501) do printing always from default tray and without duplex. Tried few different drivers with exactly the same result.
In both environments printers are network printers. So to make them apply duplex setting, using printer driver, we needed to install local driver on server and make a local printer, as recommended my Microsoft on this support forum thread.
Though environments and printers used looks very similar, one works while other do not. Any help will be highly appreciated.
In case someone else needs it, I came up with a workaround, based on storing printer settings memory block in a binary file and then restoring it. The idea was described in this blog post, but it didn't work for me when simply copy-pasted (it worked only for some drivers and for some settings while other printing options were ignored).
So I remade it a bit so that now it can support all settings I've tried on any printer (with compatible driver) I've tested. Of course if you use driver of another printer for example it won't work.
The disadvantage of thi approach is of course that you should first set default printer preferences (in Control Panel) to what you need. That isn't always possible of course, but at least in some cases it can help.
So the full source code of a test util which is capable to store printer settings into a file, load this file again into printer and print a document using the specified settings file:
using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Office.Interop.Word;
namespace PrintAdvancedTest
{
[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_DEFAULTS
{
public int pDatatype;
public int pDevMode;
public int DesiredAccess;
}
[StructLayout(LayoutKind.Sequential)]
public struct PRINTER_INFO_2
{
[MarshalAs(UnmanagedType.LPStr)]
public readonly string pServerName;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string pPrinterName;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string pShareName;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string pPortName;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string pDriverName;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string pComment;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string pLocation;
public IntPtr pDevMode;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string pSepFile;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string pPrintProcessor;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string pDatatype;
[MarshalAs(UnmanagedType.LPStr)]
public readonly string pParameters;
public IntPtr pSecurityDescriptor;
public readonly Int32 Attributes;
public readonly Int32 Priority;
public readonly Int32 DefaultPriority;
public readonly Int32 StartTime;
public readonly Int32 UntilTime;
public readonly Int32 Status;
public readonly Int32 cJobs;
public readonly Int32 AveragePPM;
}
public class PrintSettings
{
private const short CCDEVICENAME = 32;
private const short CCFORMNAME = 32;
//Constants for DEVMODE
// Constants for DocumentProperties
private const int DM_MODIFY = 8;
private const int DM_COPY = 2;
private const int DM_IN_BUFFER = DM_MODIFY;
private const int DM_OUT_BUFFER = DM_COPY;
// const intants for dmOrientation
private const int DMORIENT_PORTRAIT = 1;
private const int DMORIENT_LANDSCAPE = 2;
// const intants for dmPrintQuality
private const int DMRES_DRAFT = (-1);
private const int DMRES_HIGH = (-4);
private const int DMRES_LOW = (-2);
private const int DMRES_MEDIUM = (-3);
// const intants for dmTTOption
private const int DMTT_BITMAP = 1;
private const int DMTT_DOWNLOAD = 2;
private const int DMTT_DOWNLOAD_OUTLINE = 4;
private const int DMTT_SUBDEV = 3;
// const intants for dmColor
private const int DMCOLOR_COLOR = 2;
private const int DMCOLOR_MONOCHROME = 1;
// const intants for dmCollate
private const int DMCOLLATE_FALSE = 0;
private const int DMCOLLATE_TRUE = 1;
// const intants for dmDuplex
private const int DMDUP_HORIZONTAL = 3;
private const int DMDUP_SIMPLEX = 1;
private const int DMDUP_VERTICAL = 2;
//const for security access
private const int PRINTER_ACCESS_ADMINISTER = 0x4;
private const int PRINTER_ACCESS_USE = 0x8;
private const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
private const int PRINTER_ALL_ACCESS =
(STANDARD_RIGHTS_REQUIRED | PRINTER_ACCESS_ADMINISTER
| PRINTER_ACCESS_USE);
/* field selection bits */
private const int DM_ORIENTATION = 0x00000001;
private const int DM_PAPERSIZE = 0x00000002;
private const int DM_PAPERLENGTH = 0x00000004;
private const int DM_PAPERWIDTH = 0x00000008;
private const int DM_SCALE = 0x00000010;
private const int DM_POSITION = 0x00000020;
private const int DM_NUP = 0x00000040;
private const int DM_DISPLAYORIENTATION = 0x00000080;
private const int DM_COPIES = 0x00000100;
private const int DM_DEFAULTSOURCE = 0x00000200;
private const int DM_PRINTQUALITY = 0x00000400;
private const int DM_COLOR = 0x00000800;
private const int DM_DUPLEX = 0x00001000;
private const int DM_YRESOLUTION = 0x00002000;
private const int DM_TTOPTION = 0x00004000;
private const int DM_COLLATE = 0x00008000;
private const int DM_FORMNAME = 0x00010000;
private const int DM_LOGPIXELS = 0x00020000;
private const int DM_BITSPERPEL = 0x00040000;
private const int DM_PELSWIDTH = 0x00080000;
private const int DM_PELSHEIGHT = 0x00100000;
private const int DM_DISPLAYFLAGS = 0x00200000;
private const int DM_DISPLAYFREQUENCY = 0x00400000;
private const int DM_ICMMETHOD = 0x00800000;
private const int DM_ICMINTENT = 0x01000000;
private const int DM_MEDIATYPE = 0x02000000;
private const int DM_DITHERTYPE = 0x04000000;
private const int DM_PANNINGWIDTH = 0x08000000;
private const int DM_PANNINGHEIGHT = 0x10000000;
private const int DM_DISPLAYFIXEDOUTPUT = 0x20000000;
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCDEVICENAME)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public short dmOrientation;
public short dmPaperSize;
public short dmPaperLength;
public short dmPaperWidth;
public short dmScale;
public short dmCopies;
public short dmDefaultSource;
public short dmPrintQuality;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCFORMNAME)]
public string dmFormName;
public short dmUnusedPadding;
public short dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
}
static void Main(string[] args)
{
Dictionary<string, Action> commands = new Dictionary<string, Action>
{
{"save", PrinterPreferencesSave},
{"print", PrinterPreferencesPrint},
{"set", PrinterPreferencesSet},
{"info", PrinterInfo}
};
while (true)
{
Console.Write("Command ({0}): ", string.Join(", ", commands.Keys));
string command = Console.ReadLine();
Action action;
if (!commands.TryGetValue(command, out action))
{
Console.WriteLine("Invalid command");
}
else
{
action();
}
}
}
static void PrinterPreferencesSave()
{
Console.Write("Printer name: ");
string printerName = Console.ReadLine();
Console.Write("Settings file path format: ");
string SettingsFileNameFormat = Console.ReadLine();
string testName;
while (true)
{
Console.Write("SAVE: Settings set name: ");
testName = Console.ReadLine();
if (testName == "end")
{
break;
}
getDevMode(printerName, string.Format(SettingsFileNameFormat, testName));
}
}
static void PrinterPreferencesPrint()
{
Console.Write("Printer name: ");
string printerName = Console.ReadLine();
Console.Write("Settings file path format: ");
string SettingsFileNameFormat = Console.ReadLine();
Console.Write("Document to print: ");
string docToPrintPath = Console.ReadLine();
string testName;
while (true)
{
Console.Write("PRINT: Settings set name: ");
testName = Console.ReadLine();
if (testName == "end")
{
break;
}
string filePath = string.Format(SettingsFileNameFormat, testName);
if (!File.Exists(filePath))
{
Console.WriteLine("File {0} not exists", filePath);
return;
}
var success = setDevMode(printerName, filePath);
if (success)
{
PrintWordDocument(docToPrintPath, printerName);
}
}
}
static void PrinterPreferencesSet()
{
Console.Write("Printer name: ");
string printerName = Console.ReadLine();
Console.Write("Settings file path format: ");
string SettingsFileNameFormat = Console.ReadLine();
string testName;
while (true)
{
Console.Write("SET: Settings set name: ");
testName = Console.ReadLine();
if (testName == "end")
{
break;
}
string filePath = string.Format(SettingsFileNameFormat, testName);
if (!File.Exists(filePath))
{
Console.WriteLine("File {0} not exists", filePath);
return;
}
var success = setDevMode(printerName, filePath);
if(!success)
{
Console.WriteLine("Failed");
}
}
}
private static void PrinterInfo()
{
Console.Write("Printer name: ");
string printerName = Console.ReadLine();
IntPtr hDevMode; // handle to the DEVMODE
IntPtr pDevMode; // pointer to the DEVMODE
DEVMODE devMode; // the actual DEVMODE structure
//var printController = new StandardPrintController();
PrinterSettings printerSettings = new PrinterSettings();
printerSettings.PrinterName = printerName;
// Get a handle to a DEVMODE for the default printer settings
hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings);
// Obtain a lock on the handle and get an actual pointer so Windows won't
// move it around while we're futzing with it
pDevMode = GlobalLock(hDevMode);
// Marshal the memory at that pointer into our P/Invoke version of DEVMODE
devMode = (DEVMODE)Marshal.PtrToStructure(pDevMode, typeof(DEVMODE));
Dictionary<string, int> dmConstants = new Dictionary<string, int>
{
{"DM_ORIENTATION", 0x00000001},
{"DM_PAPERSIZE", 0x00000002},
{"DM_PAPERLENGTH", 0x00000004},
{"DM_PAPERWIDTH", 0x00000008},
{"DM_SCALE", 0x00000010},
{"DM_POSITION", 0x00000020},
{"DM_NUP", 0x00000040},
{"DM_DISPLAYORIENTATION", 0x00000080},
{"DM_COPIES", 0x00000100},
{"DM_DEFAULTSOURCE", 0x00000200},
{"DM_PRINTQUALITY", 0x00000400},
{"DM_COLOR", 0x00000800},
{"DM_DUPLEX", 0x00001000},
{"DM_YRESOLUTION", 0x00002000},
{"DM_TTOPTION", 0x00004000},
{"DM_COLLATE", 0x00008000},
{"DM_FORMNAME", 0x00010000},
{"DM_LOGPIXELS", 0x00020000},
{"DM_BITSPERPEL", 0x00040000},
{"DM_PELSWIDTH", 0x00080000},
{"DM_PELSHEIGHT", 0x00100000},
{"DM_DISPLAYFLAGS", 0x00200000},
{"DM_DISPLAYFREQUENCY", 0x00400000},
{"DM_ICMMETHOD", 0x00800000},
{"DM_ICMINTENT", 0x01000000},
{"DM_MEDIATYPE", 0x02000000},
{"DM_DITHERTYPE", 0x04000000},
{"DM_PANNINGWIDTH", 0x08000000},
{"DM_PANNINGHEIGHT", 0x10000000},
{"DM_DISPLAYFIXEDOUTPUT", 0x20000000},
};
Console.WriteLine("Allow set: {0}. Details: {1}", Convert.ToString(devMode.dmFields, 16), string.Join(",", dmConstants.Where(c=>(devMode.dmFields & c.Value)==c.Value).Select(c=>c.Key)));
//private const int DM_POSITION = 0x00000020;
//private const int DM_NUP = 0x00000040;
//private const int DM_DISPLAYORIENTATION = 0x00000080;
//private const int DM_DEFAULTSOURCE = 0x00000200;
//private const int DM_PRINTQUALITY = 0x00000400;
//private const int DM_COLOR = 0x00000800;
//private const int DM_YRESOLUTION = 0x00002000;
//private const int DM_TTOPTION = 0x00004000;
//private const int DM_FORMNAME = 0x00010000;
//private const int DM_LOGPIXELS = 0x00020000;
//private const int DM_BITSPERPEL = 0x00040000;
//private const int DM_PELSWIDTH = 0x00080000;
//private const int DM_PELSHEIGHT = 0x00100000;
//private const int DM_DISPLAYFLAGS = 0x00200000;
//private const int DM_DISPLAYFREQUENCY = 0x00400000;
//private const int DM_ICMMETHOD = 0x00800000;
//private const int DM_ICMINTENT = 0x01000000;
//private const int DM_MEDIATYPE = 0x02000000;
//private const int DM_DITHERTYPE = 0x04000000;
//private const int DM_PANNINGWIDTH = 0x08000000;
//private const int DM_PANNINGHEIGHT = 0x10000000;
//private const int DM_DISPLAYFIXEDOUTPUT = 0x20000000;
WriteDevModePropertyInfo("DeviceName", devMode.dmDeviceName, null);
WriteDevModePropertyInfo("SpecVersion", devMode.dmSpecVersion.ToString(), null);
WriteDevModePropertyInfo("DriverVersion", devMode.dmDriverVersion.ToString(), null);
WriteDevModePropertyInfo("Size", devMode.dmSize.ToString(), null);
WriteDevModePropertyInfo("DriverExtra", devMode.dmDriverExtra.ToString(), null);
WriteDevModePropertyInfo("Orientation", devMode.dmOrientation.ToString(), (devMode.dmFields & DM_ORIENTATION) == DM_ORIENTATION);
WriteDevModePropertyInfo("PaperSize", devMode.dmPaperSize.ToString(), (devMode.dmFields & DM_PAPERSIZE) == DM_PAPERSIZE);
WriteDevModePropertyInfo("PaperLength", devMode.dmPaperLength.ToString(), (devMode.dmFields & DM_PAPERLENGTH) == DM_PAPERLENGTH);
WriteDevModePropertyInfo("PaperWidth", devMode.dmPaperWidth.ToString(), (devMode.dmFields & DM_PAPERWIDTH) == DM_PAPERWIDTH);
WriteDevModePropertyInfo("Scale", devMode.dmScale.ToString(), (devMode.dmFields & DM_SCALE) == DM_SCALE);
WriteDevModePropertyInfo("Copies", devMode.dmCopies.ToString(), (devMode.dmFields & DM_COPIES) == DM_COPIES);
WriteDevModePropertyInfo("Duplex", devMode.dmDuplex.ToString(), (devMode.dmFields & DM_DUPLEX) == DM_DUPLEX);
WriteDevModePropertyInfo("YResolution", devMode.dmYResolution.ToString(), null);
WriteDevModePropertyInfo("TTOption", devMode.dmTTOption.ToString(), null);
WriteDevModePropertyInfo("Collate", devMode.dmCollate.ToString(), (devMode.dmFields & DM_COLLATE) == DM_COLLATE);
WriteDevModePropertyInfo("FormName", devMode.dmFormName.ToString(), null);
WriteDevModePropertyInfo("UnusedPadding", devMode.dmUnusedPadding.ToString(), null);
WriteDevModePropertyInfo("BitsPerPel", devMode.dmBitsPerPel.ToString(), null);
WriteDevModePropertyInfo("PelsWidth", devMode.dmPelsWidth.ToString(), null);
WriteDevModePropertyInfo("PelsHeight", devMode.dmPelsHeight.ToString(), null);
WriteDevModePropertyInfo("DisplayFlags", devMode.dmDisplayFlags.ToString(), null);
WriteDevModePropertyInfo("DisplayFrequency", devMode.dmDisplayFlags.ToString(), null);
}
private static void WriteDevModePropertyInfo(string settingName, string value, bool? allowSet)
{
Console.WriteLine("{0} {1} {2}", allowSet.HasValue ? (allowSet.Value ? "+" : "-") : " ", settingName.PadRight(20, '.'), value);
}
[DllImport("kernel32.dll", ExactSpelling = true)]
public static extern IntPtr GlobalFree(IntPtr handle);
[DllImport("kernel32.dll", ExactSpelling = true)]
public static extern IntPtr GlobalLock(IntPtr handle);
[DllImport("kernel32.dll", ExactSpelling = true)]
public static extern IntPtr GlobalUnlock(IntPtr handle);
[DllImport("kernel32.dll", EntryPoint = "GetLastError", SetLastError = false,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern Int32 GetLastError();
[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesA", SetLastError = true,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter,
[MarshalAs(UnmanagedType.LPStr)] string pDeviceNameg,
IntPtr pDevModeOutput, ref IntPtr pDevModeInput, int fMode);
[DllImport("winspool.Drv", EntryPoint = "GetPrinterA", SetLastError = true,
CharSet = CharSet.Ansi, ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
private static extern bool GetPrinter(IntPtr hPrinter, Int32 dwLevel,
IntPtr pPrinter, Int32 dwBuf, out Int32 dwNeeded);
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA",
SetLastError = true, CharSet = CharSet.Ansi,
ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern bool
OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter,
out IntPtr hPrinter, ref PRINTER_DEFAULTS pd);
[DllImport("winspool.drv", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr
pPrinter, int Command);
[DllImport("kernel32.dll")]
static extern IntPtr GlobalAlloc(uint uFlags, int dwBytes);
public static void getDevMode(string printerName, string filepath)
{
PRINTER_DEFAULTS PrinterValues = new PRINTER_DEFAULTS();
PrinterValues.pDatatype = 0;
PrinterValues.pDevMode = 0;
PrinterValues.DesiredAccess = PRINTER_ALL_ACCESS;
IntPtr ptrZero = IntPtr.Zero;
IntPtr hPrinter;
IntPtr pDevMode = new IntPtr();
//get printer handle
OpenPrinter(printerName, out hPrinter, ref PrinterValues);
//allocate memory for ptr to devmode, 0 argument retrieves bytes required
int bytes = DocumentProperties(new IntPtr(0), hPrinter, printerName, ptrZero, ref pDevMode, 0);
pDevMode = GlobalAlloc(0, bytes);
//set the pointer
DocumentProperties(new IntPtr(0), hPrinter, printerName, pDevMode, ref ptrZero, DM_OUT_BUFFER);
//write the devMode to a file
using (FileStream fs = new FileStream(filepath, FileMode.Create))
{
for (int i = 0; i < bytes; i++)
{
fs.WriteByte(Marshal.ReadByte(pDevMode, i));
}
}
//free resources
GlobalFree(pDevMode);
ClosePrinter(hPrinter);
}
public static bool setDevMode(string printerName, string filepath)
{
if(!File.Exists(filepath))
{
return false;
}
IntPtr hPrinter;
int bytes = 0;
IntPtr pPInfo;
IntPtr pDevMode;
PRINTER_INFO_2 pInfo = new PRINTER_INFO_2();
PRINTER_DEFAULTS PrinterValues = new PRINTER_DEFAULTS();
PrinterValues.pDatatype = 0;
PrinterValues.pDevMode = 0;
PrinterValues.DesiredAccess = PRINTER_ALL_ACCESS;
//retrieve the devmode from file
using (FileStream fs = new FileStream(filepath, FileMode.Open))
{
int length = Convert.ToInt32(fs.Length);
pDevMode = GlobalAlloc(0, length);
for (int i = 0; i < length; i++)
{
Marshal.WriteByte(pDevMode, i, (byte)fs.ReadByte());
}
}
//get printer handle
OpenPrinter(printerName, out hPrinter, ref PrinterValues);
//get bytes for printer info structure and allocate memory
GetPrinter(hPrinter, 2, IntPtr.Zero, 0, out bytes);
if (bytes == 0)
{
throw new Exception("Get Printer Failed");
}
pPInfo = GlobalAlloc(0, bytes);
//set pointer to printer info
GetPrinter(hPrinter, 2, pPInfo, bytes, out bytes);
//place the printer info structure
pInfo = (PRINTER_INFO_2)Marshal.PtrToStructure(pPInfo, typeof(PRINTER_INFO_2));
//insert the new devmode
pInfo.pDevMode = pDevMode;
pInfo.pSecurityDescriptor = IntPtr.Zero;
//set pointer to new printer info
Marshal.StructureToPtr(pInfo, pPInfo, true);
//update
SetPrinter(hPrinter, 2, pPInfo, 0);
//free resources
GlobalFree(pPInfo);
GlobalFree(pDevMode);
ClosePrinter(hPrinter);
return true;
}
private static void PrintWordDocument(string path, string printerName)
{
object readOnly = true;
object addToRecentFiles = false;
object visible = false;
object backgroundPrint = false;
object saveChanges = false;
object sourceFile = path;
var wordApplication = new Application();
var doc = wordApplication.Documents.OpenNoRepairDialog(FileName: ref sourceFile, ReadOnly: ref readOnly,
AddToRecentFiles: ref addToRecentFiles,
Visible: ref visible);
wordApplication.ActivePrinter = printerName;
doc.Activate();
wordApplication.PrintOut(Background: ref backgroundPrint, FileName: sourceFile);
object _missing = Type.Missing;
doc.Close(ref saveChanges, ref _missing, ref _missing);
}
}
}
UPDATE 2018-12-04 (in 5,5 years): There was a nasty rare problem with Marshal.StructureToPtr call in this code and today I finally got an answer to that question (see comment from Hans Passant). I'm not able to verify if that actually works since I no longer work on that project, but it seems you may need to apply that fix if you try using this code.
I've been searching for a c# library that gets the icon of a given path with many sizes, finally when I got exactly the class that I need, It has a problem:
This method gets icon of a given path:
public static BitmapSource GetIcon(string FileName, bool small, bool checkDisk, bool addOverlay)
{
SHFILEINFO shinfo = new SHFILEINFO();
uint SHGFI_USEFILEATTRIBUTES = 0x000000010;
uint SHGFI_LINKOVERLAY = 0x000008000;
uint flags;
if (small)
{
flags = SHGFI_ICON | SHGFI_SMALLICON;
}
else
{
flags = SHGFI_ICON | SHGFI_LARGEICON;
}
if (!checkDisk)
flags |= SHGFI_USEFILEATTRIBUTES;
if (addOverlay)
flags |= SHGFI_LINKOVERLAY;
var res = SHGetFileInfo(FileName, 0, ref shinfo, Marshal.SizeOf(shinfo), flags);
if (res == 0)
{
throw (new System.IO.FileNotFoundException());
}
var ico = System.Drawing.Icon.FromHandle(shinfo.hIcon); //**Here**
var bs = BitmapFromIcon(ico);
ico.Dispose();
bs.Freeze();
DestroyIcon(shinfo.hIcon);
// CloseHandle(shinfo.hIcon); it always give exception
return bs;
}
public static extern Boolean CloseHandle(IntPtr handle);
The previous code as it is in this question works as it suppose to, however AFTER getting the icons of file paths in a directory successfully, it gives an exception on this line :
var ico = System.Drawing.Icon.FromHandle(shinfo.hIcon);
An exception of type 'System.IO.FileNotFoundException' occurred in WPF_REMOTE.exe but was not handled in user code
Additional information: Unable to find the specified file.
So Why is this happening?
Update: I found out that it happened because there were a path that contains unicode characters and i need to use SHFILEINFOW instead, still can't figure how to change SHFILEINFO to SHFILEINFOW
another question about the line CloseHandle(shinfo.hIcon); always give an exception :
An exception of type 'System.Runtime.InteropServices.SEHException' occurred in WPF_REMOTE.exe but was not handled in user code
Additional information: External component has thrown an exception.
I'm wondering why it's not working! and why should I use it if the method is already working without it.
also if you have any improvement I could use in this class tell me, Thanks in Advance.
After editing some code I get it, you can get the icons easily from this library, in my case i needed the icon as byte[]
public class IconHelper
{
// Constants that we need in the function call
private const int SHGFI_ICON = 0x100;
private const int SHGFI_SMALLICON = 0x1;
private const int SHGFI_LARGEICON = 0x0;
private const int SHIL_JUMBO = 0x4;
private const int SHIL_EXTRALARGE = 0x2;
// This structure will contain information about the file
public struct SHFILEINFO
{
// Handle to the icon representing the file
public IntPtr hIcon;
// Index of the icon within the image list
public int iIcon;
// Various attributes of the file
public uint dwAttributes;
// Path to the file
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string szDisplayName;
// File type
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
};
[System.Runtime.InteropServices.DllImport("Kernel32.dll")]
public static extern Boolean CloseHandle(IntPtr handle);
private struct IMAGELISTDRAWPARAMS
{
public int cbSize;
public IntPtr himl;
public int i;
public IntPtr hdcDst;
public int x;
public int y;
public int cx;
public int cy;
public int xBitmap; // x offest from the upperleft of bitmap
public int yBitmap; // y offset from the upperleft of bitmap
public int rgbBk;
public int rgbFg;
public int fStyle;
public int dwRop;
public int fState;
public int Frame;
public int crEffect;
}
[StructLayout(LayoutKind.Sequential)]
private struct IMAGEINFO
{
public IntPtr hbmImage;
public IntPtr hbmMask;
public int Unused1;
public int Unused2;
public Rect rcImage;
}
[DllImport("shell32.dll", EntryPoint = "#727")]
private extern static int SHGetImageList(
int iImageList,
ref Guid riid,
out IImageList ppv
);
// The signature of SHGetFileInfo (located in Shell32.dll)
[DllImport("Shell32.dll", CharSet = CharSet.Unicode)]
public static extern int SHGetFileInfo(string pszPath, int dwFileAttributes, ref SHFILEINFO psfi, int cbFileInfo, uint uFlags);
[DllImport("Shell32.dll", CharSet = CharSet.Unicode)]
public static extern int SHGetFileInfo(IntPtr pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, int cbFileInfo, uint uFlags);
[DllImport("shell32.dll", SetLastError = true)]
static extern int SHGetSpecialFolderLocation(IntPtr hwndOwner, Int32 nFolder,
ref IntPtr ppidl);
[DllImport("user32")]
public static extern int DestroyIcon(IntPtr hIcon);
public struct pair
{
public System.Drawing.Icon icon { get; set; }
public IntPtr iconHandleToDestroy { set; get; }
}
private static byte[] ByteFromIcon(System.Drawing.Icon ic)
{
var icon = System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(ic.Handle,
System.Windows.Int32Rect.Empty,
System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
icon.Freeze();
byte[] data;
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(icon));
using (MemoryStream ms = new MemoryStream())
{
encoder.Save(ms);
data = ms.ToArray();
}
return data;
}
private static byte[] GetSmallIcon(string FileName, IconSize iconSize)
{
SHFILEINFO shinfo = new SHFILEINFO();
uint flags;
if (iconSize == IconSize.Small)
{
flags = SHGFI_ICON | SHGFI_SMALLICON;
}
else
{
flags = SHGFI_ICON | SHGFI_LARGEICON;
}
var res = SHGetFileInfo(FileName, 0, ref shinfo, Marshal.SizeOf(shinfo), flags);
if (res == 0)
{
throw (new System.IO.FileNotFoundException());
}
var ico = (System.Drawing.Icon)System.Drawing.Icon.FromHandle(shinfo.hIcon);
var bs = ByteFromIcon(ico);
ico.Dispose();
DestroyIcon(shinfo.hIcon);
return bs;
}
private static byte[] GetLargeIcon(string FileName)
{
SHFILEINFO shinfo = new SHFILEINFO();
uint SHGFI_SYSICONINDEX = 0x4000;
int FILE_ATTRIBUTE_NORMAL = 0x80;
uint flags;
flags = SHGFI_SYSICONINDEX;
var res = SHGetFileInfo(FileName, FILE_ATTRIBUTE_NORMAL, ref shinfo, Marshal.SizeOf(shinfo), flags);
if (res == 0)
{
throw (new System.IO.FileNotFoundException());
}
var iconIndex = shinfo.iIcon;
Guid iidImageList = new Guid("46EB5926-582E-4017-9FDF-E8998DAA0950");
IImageList iml;
int size = SHIL_EXTRALARGE;
var hres = SHGetImageList(size, ref iidImageList, out iml); // writes iml
IntPtr hIcon = IntPtr.Zero;
int ILD_TRANSPARENT = 1;
hres = iml.GetIcon(iconIndex, ILD_TRANSPARENT, ref hIcon);
var ico = System.Drawing.Icon.FromHandle(hIcon);
var bs = ByteFromIcon(ico);
ico.Dispose();
DestroyIcon(hIcon);
return bs;
}
}
and you can get four different sizes for the icon
I'm trying to use the Windows API to set the primary monitor. It doesn't seem to work - my screen just flicks and nothing happens.
public const int DM_ORIENTATION = 0x00000001;
public const int DM_PAPERSIZE = 0x00000002;
public const int DM_PAPERLENGTH = 0x00000004;
public const int DM_PAPERWIDTH = 0x00000008;
public const int DM_SCALE = 0x00000010;
public const int DM_POSITION = 0x00000020;
public const int DM_NUP = 0x00000040;
public const int DM_DISPLAYORIENTATION = 0x00000080;
public const int DM_COPIES = 0x00000100;
public const int DM_DEFAULTSOURCE = 0x00000200;
public const int DM_PRINTQUALITY = 0x00000400;
public const int DM_COLOR = 0x00000800;
public const int DM_DUPLEX = 0x00001000;
public const int DM_YRESOLUTION = 0x00002000;
public const int DM_TTOPTION = 0x00004000;
public const int DM_COLLATE = 0x00008000;
public const int DM_FORMNAME = 0x00010000;
public const int DM_LOGPIXELS = 0x00020000;
public const int DM_BITSPERPEL = 0x00040000;
public const int DM_PELSWIDTH = 0x00080000;
public const int DM_PELSHEIGHT = 0x00100000;
public const int DM_DISPLAYFLAGS = 0x00200000;
public const int DM_DISPLAYFREQUENCY = 0x00400000;
public const int DM_ICMMETHOD = 0x00800000;
public const int DM_ICMINTENT = 0x01000000;
public const int DM_MEDIATYPE = 0x02000000;
public const int DM_DITHERTYPE = 0x04000000;
public const int DM_PANNINGWIDTH = 0x08000000;
public const int DM_PANNINGHEIGHT = 0x10000000;
public const int DM_DISPLAYFIXEDOUTPUT = 0x20000000;
public const int ENUM_CURRENT_SETTINGS = -1;
public const int CDS_UPDATEREGISTRY = 0x01;
public const int CDS_TEST = 0x02;
public const int CDS_SET_PRIMARY = 0x00000010;
public const long DISP_CHANGE_SUCCESSFUL = 0;
public const long DISP_CHANGE_RESTART = 1;
public const long DISP_CHANGE_FAILED = -1;
public const long DISP_CHANGE_BADMODE = -2;
public const long DISP_CHANGE_NOTUPDATED = -3;
public const long DISP_CHANGE_BADFLAGS = -4;
public const long DISP_CHANGE_BADPARAM = -5;
public const long DISP_CHANGE_BADDUALVIEW = -6;
public static void SetPrimary(Screen screen)
{
DISPLAY_DEVICE d = new DISPLAY_DEVICE();
DEVMODE dm = new DEVMODE();
d.cb = Marshal.SizeOf(d);
uint deviceID = 1;
User_32.EnumDisplayDevices(null, deviceID, ref d, 0); //
User_32.EnumDisplaySettings(d.DeviceName, 0, ref dm);
dm.dmPelsWidth = 2560;
dm.dmPelsHeight = 1600;
dm.dmPositionX = screen.Bounds.Right;
dm.dmFields = DM_POSITION | DM_PELSWIDTH | DM_PELSHEIGHT;
User_32.ChangeDisplaySettingsEx(d.DeviceName, ref dm, IntPtr.Zero, CDS_SET_PRIMARY, IntPtr.Zero);
}
I call the method like this:
SetPrimary(Screen.AllScreens[1])
Any ideas?
Here is the full code based on ADBailey's solution:
public class MonitorChanger
{
public static void SetAsPrimaryMonitor(uint id)
{
var device = new DISPLAY_DEVICE();
var deviceMode = new DEVMODE();
device.cb = Marshal.SizeOf(device);
NativeMethods.EnumDisplayDevices(null, id, ref device, 0);
NativeMethods.EnumDisplaySettings(device.DeviceName, -1, ref deviceMode);
var offsetx = deviceMode.dmPosition.x;
var offsety = deviceMode.dmPosition.y;
deviceMode.dmPosition.x = 0;
deviceMode.dmPosition.y = 0;
NativeMethods.ChangeDisplaySettingsEx(
device.DeviceName,
ref deviceMode,
(IntPtr)null,
(ChangeDisplaySettingsFlags.CDS_SET_PRIMARY | ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY | ChangeDisplaySettingsFlags.CDS_NORESET),
IntPtr.Zero);
device = new DISPLAY_DEVICE();
device.cb = Marshal.SizeOf(device);
// Update remaining devices
for (uint otherid = 0; NativeMethods.EnumDisplayDevices(null, otherid, ref device, 0); otherid++)
{
if (device.StateFlags.HasFlag(DisplayDeviceStateFlags.AttachedToDesktop) && otherid != id)
{
device.cb = Marshal.SizeOf(device);
var otherDeviceMode = new DEVMODE();
NativeMethods.EnumDisplaySettings(device.DeviceName, -1, ref otherDeviceMode);
otherDeviceMode.dmPosition.x -= offsetx;
otherDeviceMode.dmPosition.y -= offsety;
NativeMethods.ChangeDisplaySettingsEx(
device.DeviceName,
ref otherDeviceMode,
(IntPtr)null,
(ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY | ChangeDisplaySettingsFlags.CDS_NORESET),
IntPtr.Zero);
}
device.cb = Marshal.SizeOf(device);
}
// Apply settings
NativeMethods.ChangeDisplaySettingsEx(null, IntPtr.Zero, (IntPtr)null, ChangeDisplaySettingsFlags.CDS_NONE, (IntPtr)null);
}
}
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
public struct DEVMODE
{
public const int CCHDEVICENAME = 32;
public const int CCHFORMNAME = 32;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
[System.Runtime.InteropServices.FieldOffset(0)]
public string dmDeviceName;
[System.Runtime.InteropServices.FieldOffset(32)]
public Int16 dmSpecVersion;
[System.Runtime.InteropServices.FieldOffset(34)]
public Int16 dmDriverVersion;
[System.Runtime.InteropServices.FieldOffset(36)]
public Int16 dmSize;
[System.Runtime.InteropServices.FieldOffset(38)]
public Int16 dmDriverExtra;
[System.Runtime.InteropServices.FieldOffset(40)]
public UInt32 dmFields;
[System.Runtime.InteropServices.FieldOffset(44)]
Int16 dmOrientation;
[System.Runtime.InteropServices.FieldOffset(46)]
Int16 dmPaperSize;
[System.Runtime.InteropServices.FieldOffset(48)]
Int16 dmPaperLength;
[System.Runtime.InteropServices.FieldOffset(50)]
Int16 dmPaperWidth;
[System.Runtime.InteropServices.FieldOffset(52)]
Int16 dmScale;
[System.Runtime.InteropServices.FieldOffset(54)]
Int16 dmCopies;
[System.Runtime.InteropServices.FieldOffset(56)]
Int16 dmDefaultSource;
[System.Runtime.InteropServices.FieldOffset(58)]
Int16 dmPrintQuality;
[System.Runtime.InteropServices.FieldOffset(44)]
public POINTL dmPosition;
[System.Runtime.InteropServices.FieldOffset(52)]
public Int32 dmDisplayOrientation;
[System.Runtime.InteropServices.FieldOffset(56)]
public Int32 dmDisplayFixedOutput;
[System.Runtime.InteropServices.FieldOffset(60)]
public short dmColor; // See note below!
[System.Runtime.InteropServices.FieldOffset(62)]
public short dmDuplex; // See note below!
[System.Runtime.InteropServices.FieldOffset(64)]
public short dmYResolution;
[System.Runtime.InteropServices.FieldOffset(66)]
public short dmTTOption;
[System.Runtime.InteropServices.FieldOffset(68)]
public short dmCollate; // See note below!
[System.Runtime.InteropServices.FieldOffset(72)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)]
public string dmFormName;
[System.Runtime.InteropServices.FieldOffset(102)]
public Int16 dmLogPixels;
[System.Runtime.InteropServices.FieldOffset(104)]
public Int32 dmBitsPerPel;
[System.Runtime.InteropServices.FieldOffset(108)]
public Int32 dmPelsWidth;
[System.Runtime.InteropServices.FieldOffset(112)]
public Int32 dmPelsHeight;
[System.Runtime.InteropServices.FieldOffset(116)]
public Int32 dmDisplayFlags;
[System.Runtime.InteropServices.FieldOffset(116)]
public Int32 dmNup;
[System.Runtime.InteropServices.FieldOffset(120)]
public Int32 dmDisplayFrequency;
}
public enum DISP_CHANGE : int
{
Successful = 0,
Restart = 1,
Failed = -1,
BadMode = -2,
NotUpdated = -3,
BadFlags = -4,
BadParam = -5,
BadDualView = -6
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DISPLAY_DEVICE
{
[MarshalAs(UnmanagedType.U4)]
public int cb;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string DeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceString;
[MarshalAs(UnmanagedType.U4)]
public DisplayDeviceStateFlags StateFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string DeviceKey;
}
[Flags()]
public enum DisplayDeviceStateFlags : int
{
/// <summary>The device is part of the desktop.</summary>
AttachedToDesktop = 0x1,
MultiDriver = 0x2,
/// <summary>The device is part of the desktop.</summary>
PrimaryDevice = 0x4,
/// <summary>Represents a pseudo device used to mirror application drawing for remoting or other purposes.</summary>
MirroringDriver = 0x8,
/// <summary>The device is VGA compatible.</summary>
VGACompatible = 0x10,
/// <summary>The device is removable; it cannot be the primary display.</summary>
Removable = 0x20,
/// <summary>The device has more display modes than its output devices support.</summary>
ModesPruned = 0x8000000,
Remote = 0x4000000,
Disconnect = 0x2000000,
}
[Flags()]
public enum ChangeDisplaySettingsFlags : uint
{
CDS_NONE = 0,
CDS_UPDATEREGISTRY = 0x00000001,
CDS_TEST = 0x00000002,
CDS_FULLSCREEN = 0x00000004,
CDS_GLOBAL = 0x00000008,
CDS_SET_PRIMARY = 0x00000010,
CDS_VIDEOPARAMETERS = 0x00000020,
CDS_ENABLE_UNSAFE_MODES = 0x00000100,
CDS_DISABLE_UNSAFE_MODES = 0x00000200,
CDS_RESET = 0x40000000,
CDS_RESET_EX = 0x20000000,
CDS_NORESET = 0x10000000
}
public class NativeMethods
{
[DllImport("user32.dll")]
public static extern DISP_CHANGE ChangeDisplaySettingsEx(string lpszDeviceName, ref DEVMODE lpDevMode, IntPtr hwnd, ChangeDisplaySettingsFlags dwflags, IntPtr lParam);
[DllImport("user32.dll")]
// A signature for ChangeDisplaySettingsEx with a DEVMODE struct as the second parameter won't allow you to pass in IntPtr.Zero, so create an overload
public static extern DISP_CHANGE ChangeDisplaySettingsEx(string lpszDeviceName, IntPtr lpDevMode, IntPtr hwnd, ChangeDisplaySettingsFlags dwflags, IntPtr lParam);
[DllImport("user32.dll")]
public static extern bool EnumDisplayDevices(string lpDevice, uint iDevNum, ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags);
[DllImport("user32.dll")]
public static extern bool EnumDisplaySettings(string deviceName, int modeNum, ref DEVMODE devMode);
}
[StructLayout(LayoutKind.Sequential)]
public struct POINTL
{
public int x;
public int y;
}
I ran into exactly the same problem, both from C# and after following the advice here to try it in C++. I eventually discovered that the thing the Microsoft documentation doesn't make clear is that the request to set the primary monitor will be ignored (but with the operation reported as successful!) unless you also set the position of the monitor to (0, 0) on the DEVMODE struct. Of course, this means that you also need to shift the positions of your other monitors so that they stay in the same place relative to the new primary monitor. Per the documentation (http://msdn.microsoft.com/en-us/library/windows/desktop/dd183413%28v=vs.85%29.aspx), call ChangeDisplaySettingsEx for each monitor with the CDS_NORESET flag and then make a final call with everything null.
The following code worked for me:
public static void SetAsPrimaryMonitor(uint id)
{
var device = new DISPLAY_DEVICE();
var deviceMode = new DEVMODE();
device.cb = Marshal.SizeOf(device);
NativeMethods.EnumDisplayDevices(null, id, ref device, 0);
NativeMethods.EnumDisplaySettings(device.DeviceName, -1, ref deviceMode);
var offsetx = deviceMode.dmPosition.x;
var offsety = deviceMode.dmPosition.y;
deviceMode.dmPosition.x = 0;
deviceMode.dmPosition.y = 0;
NativeMethods.ChangeDisplaySettingsEx(
device.DeviceName,
ref deviceMode,
(IntPtr)null,
(ChangeDisplaySettingsFlags.CDS_SET_PRIMARY | ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY | ChangeDisplaySettingsFlags.CDS_NORESET),
IntPtr.Zero);
device = new DISPLAY_DEVICE();
device.cb = Marshal.SizeOf(device);
// Update remaining devices
for (uint otherid = 0; NativeMethods.EnumDisplayDevices(null, otherid, ref device, 0); otherid++)
{
if (device.StateFlags.HasFlag(DisplayDeviceStateFlags.AttachedToDesktop) && otherid != id)
{
device.cb = Marshal.SizeOf(device);
var otherDeviceMode = new DEVMODE();
NativeMethods.EnumDisplaySettings(device.DeviceName, -1, ref otherDeviceMode);
otherDeviceMode.dmPosition.x -= offsetx;
otherDeviceMode.dmPosition.y -= offsety;
NativeMethods.ChangeDisplaySettingsEx(
device.DeviceName,
ref otherDeviceMode,
(IntPtr)null,
(ChangeDisplaySettingsFlags.CDS_UPDATEREGISTRY | ChangeDisplaySettingsFlags.CDS_NORESET),
IntPtr.Zero);
}
device.cb = Marshal.SizeOf(device);
}
// Apply settings
NativeMethods.ChangeDisplaySettingsEx(null, IntPtr.Zero, (IntPtr)null, ChangeDisplaySettingsFlags.CDS_NONE, (IntPtr)null);
}
Note that a signature for ChangeDisplaySettingsEx with a DEVMODE struct as the second parameter obviously won't allow you to pass in IntPtr.Zero. Create yourself two different signatures for the same extern call, i.e.
[DllImport("user32.dll")]
public static extern DISP_CHANGE ChangeDisplaySettingsEx(string lpszDeviceName, ref DEVMODE lpDevMode, IntPtr hwnd, ChangeDisplaySettingsFlags dwflags, IntPtr lParam);
[DllImport("user32.dll")]
public static extern DISP_CHANGE ChangeDisplaySettingsEx(string lpszDeviceName, IntPtr lpDevMode, IntPtr hwnd, ChangeDisplaySettingsFlags dwflags, IntPtr lParam);
I can't really help you with the winapi-stuff but if you are using a Nvidia card you may have a look at the NVcontrolPanel Api Documentation
Then you could make the secondary output your primary using rundll32.exe NvCpl.dll,dtcfg primary 2
Hope that will help you.
According to the documentation for ChangeDisplaySettingsEx, "the dmSize member must be initialized to the size, in bytes, of the DEVMODE structure." Furthermore, the EnumDisplaySettings documentation states, "Before calling EnumDisplaySettings, set the dmSize member to sizeof(DEVMODE), and set the dmDriverExtra member to indicate the size, in bytes, of the additional space available to receive private driver data". I don't see this happening in the code sample given in the question; that's one reason why it may be failing.
Additionally, you might have errors in the definitions of the DEVMODE and DISPLAY_DEVICE structs, which were not included in the question. Roger Lipscombe's suggestion to get it working from C/C++ first is an excellent way to rule out this type of problem.
Finally, check the return value from ChangeDisplaySettingsEx and see if that gives a clue as to why it might be failing.