Can I get the icon associated with an installed printer through .Net? - c#

I know how to get the list of all of the installed printers on a machine with .Net:
foreach (String printer in PrinterSettings.InstalledPrinters)
{
Console.WriteLine(printer.ToString());
}
Console.ReadLine();
InstalledPrinters is just a list of strings though. Is there any way to get the installed printer objects that contain both the name and the icon image that I would ordinarily see under "Devices and Printers" in the Windows Explorer?

The icon is normally embedded into either one of the dll files or the main EXE, look at the System.Drawing.Icon static methods, the link below is for WinForms, its slightly different with WPF as you have to create an ImageSource from the extracted icon stream.
How to: Extract the Icon Associated with a File in Windows Forms

C# code for this task:
public static class PrinterIcons
{
public static Dictionary<string, Icon> GetPrintersWithIcons(IntPtr hwndOwner)
{
Dictionary<string, Icon> result = new Dictionary<string, Icon>();
Shell32.IShellFolder iDesktopFolder = Shell32.GetDesktopFolder();
try
{
IntPtr pidlPrintersFolder;
if (Shell32.SHGetFolderLocation(hwndOwner, (int)Shell32.CSIDL.CSIDL_PRINTERS, IntPtr.Zero, 0, out pidlPrintersFolder) == 0)
try
{
StringBuilder strDisplay = new StringBuilder(260);
Guid guidIShellFolder = Shell32.IID_IShellFolder;
IntPtr ptrPrintersShellFolder;
iDesktopFolder.BindToObject(pidlPrintersFolder, IntPtr.Zero, ref guidIShellFolder, out ptrPrintersShellFolder);
Object objPrintersShellFolder = Marshal.GetTypedObjectForIUnknown(ptrPrintersShellFolder, Shell32.ShellFolderType);
try
{
Shell32.IShellFolder printersShellFolder = (Shell32.IShellFolder)objPrintersShellFolder;
IntPtr ptrObjectsList;
printersShellFolder.EnumObjects(hwndOwner, Shell32.ESHCONTF.SHCONTF_NONFOLDERS, out ptrObjectsList);
Object objEnumIDList = Marshal.GetTypedObjectForIUnknown(ptrObjectsList, Shell32.EnumIDListType);
try
{
Shell32.IEnumIDList iEnumIDList = (Shell32.IEnumIDList)objEnumIDList;
IntPtr[] rgelt = new IntPtr[1];
IntPtr pidlPrinter;
int pceltFetched;
Shell32.STRRET ptrString;
while (iEnumIDList.Next(1, rgelt, out pceltFetched) == 0 && pceltFetched == 1)
{
printersShellFolder.GetDisplayNameOf(rgelt[0],
Shell32.ESHGDN.SHGDN_NORMAL, out ptrString);
if (Shell32.StrRetToBuf(ref ptrString, rgelt[0], strDisplay,
(uint)strDisplay.Capacity) == 0)
{
pidlPrinter = Shell32.ILCombine(pidlPrintersFolder, rgelt[0]);
string printerDisplayNameInPrintersFolder = strDisplay.ToString();
Shell32.SHFILEINFO shinfo = new Shell32.SHFILEINFO();
Shell32.SHGetFileInfo(pidlPrinter, 0, out shinfo, (uint)Marshal.SizeOf(shinfo), Shell32.SHGFI.PIDL | Shell32.SHGFI.AddOverlays | Shell32.SHGFI.Icon);
Icon printerIcon = (Icon)Icon.FromHandle(shinfo.hIcon).Clone();
Shell32.DestroyIcon(shinfo.hIcon);
result.Add(printerDisplayNameInPrintersFolder, printerIcon);
}
}
}
finally
{
Marshal.ReleaseComObject(objEnumIDList);
}
}
finally
{
Marshal.ReleaseComObject(objPrintersShellFolder);
}
}
finally
{
Shell32.ILFree(pidlPrintersFolder);
}
}
finally
{
Marshal.ReleaseComObject(iDesktopFolder);
}
return result;
}
}
Beware, that printer names in result dictionary will be printer names shown in Printers shell folder, and they can be different from printer names, used in PrinterSettings class (for example, network printers in Printers shell folder can be shown as " on ", and word "on" depends from windows localization and can be not machine network name). I don`t know yet how to get "real" printer name from IShellFolder to use it with standart PrinterSettings class.
Anyway, this code loads printers system icons, so you can use it for you task.
Upd: Shell32 class code, used in this code can be found here (too big for answer): http://pastebin.com/thJuWx45

Related

How can I retrieve items and their details from the shell:AppsFolder virtual folder using c#?

I am trying to get all the items from the FOLDERID_AppsFolder, which you can access by running explorer.exe shell:appsFolder command, and their details, specifically the AppUserModelID.
I can get the name of the items using the code below but I am not sure how to get the AppUserModelID. Can I get this value somehow?
IShellItem appsFolder;
string str;
var res = ShellItemUtilities.SHGetKnownFolderItem(ShellItemUtilities.FOLDERID_AppsFolder,
0, IntPtr.Zero, typeof(IShellItem).GUID, out appsFolder);
if (res < 0) return;
try
{
var pidl = default(PIDLIST_ABSOLUTE);
foreach (var app in appsFolder.Enumerate())
{
try
{
recyleBin.GetDisplayName(2, out ptr);
// Get the actual name of the item
str = Marshal.PtrToStringUni(ptr);
}
finally
{
if (ptr != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(ptr);
ptr = IntPtr.Zero;
}
}
}
}
...
Perhaps the IShellItem::GetAttributes method is what I need but it can only retrieve the attribute that I specify through the sfgaoMask parameter and the documentation regarding the values for this parameter does not include anything related to the AppUserModelID.
And for reference, this is what the apps folder looks like:
Can you hear the crickets chirping?
I was eventually able to find a solution to this problem when I stumbled upon the Microsoft.WindowsAPICodePack-Shell nuget package which wraps all the shell commands I needed so that I don't have to P/Ivoke them. The code now becomes:
// GUID taken from https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
var FODLERID_AppsFolder = new Guid("{1e87508d-89c2-42f0-8a7e-645a0f50ca58}");
ShellObject appsFolder = (ShellObject)KnownFolderHelper.FromKnownFolderId(FODLERID_AppsFolder);
foreach (var app in (IKnownFolder)appsFolder)
{
string name = app.Name;
// The ParsingName property is the AppUserModelID
string appUserModelID = app.ParsingName; // or app.Properties.System.AppUserModel.ID
ImageSource icon = app.Thumbnail.MediumBitmapSource;
}
The ShellObject object actually contains a whole lot more proeprties available through it's Properties.System props.
In case you are wondering why I am casting the appsFolder to a ShellObject at instantiation only to cast it back to an IKnownFolder when enumerating, that's because the API code pack actually comes with a ShellObjectWatcher which takes a ShellObject and monitors it for changes. If a new app is installed and it is listed in this virtual folder, the watcher can be used to monitor for this:
ShellObjectWatcher sow = new ShellObjectWatcher(appFolder, false);
sow.AllEvents += (s, e) => DoWhatever();
sow.Start();

C# Get the Windows Explore Path which has the focus

I would like to get the Path of the windows which has the focus.
Ex: I have 3 windows Opened
a. C:\Windows
b. C:\Windows\System32
c. C:\Users\COMP-0\Documents
And i am working on c (C:\Users\COMP-0\Documents)
So i would like to get this path (C:\Users\COMP-0\Documents) programmatically in C#.
Expanding on this answer to get the selected files in a folder, you can use a similar approach to get the current folder and therefore it's path.
This needs some COM and requires:
Getting the active window using GetForegroundWindow
Find the current list of InternetExplorer windows using SHDocVw.ShellWindows,
Matching handle pointers to find the current window
Getting hold of the folder path inside the active window using the IShellFolderViewDual2 COM interface.
There are a couple of caveats to be aware of:
Special folders (Favourites, My Computer etc) will give you the file path as "::{GUID}" where the GUID points to the CLSID for that folder in the registry. It is probably possible to convert that value to a path.
Going to "Desktop" will return null for the current folder
Focussing Internet Explorer will trigger a match on the active window so we need to ensure we are in a Shell Folder
If in a special folder or Desktop this code will just return the current window title - usually the name of the special folder - using the details in this answer.
private static string GetActiveExplorerPath()
{
// get the active window
IntPtr handle = GetForegroundWindow();
// Required ref: SHDocVw (Microsoft Internet Controls COM Object) - C:\Windows\system32\ShDocVw.dll
ShellWindows shellWindows = new SHDocVw.ShellWindows();
// loop through all windows
foreach (InternetExplorer window in shellWindows)
{
// match active window
if (window.HWND == (int)handle)
{
// Required ref: Shell32 - C:\Windows\system32\Shell32.dll
var shellWindow = window.Document as Shell32.IShellFolderViewDual2;
// will be null if you are in Internet Explorer for example
if (shellWindow != null)
{
// Item without an index returns the current object
var currentFolder = shellWindow.Folder.Items().Item();
// special folder - use window title
// for some reason on "Desktop" gives null
if (currentFolder == null || currentFolder.Path.StartsWith("::"))
{
// Get window title instead
const int nChars = 256;
StringBuilder Buff = new StringBuilder(nChars);
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
}
else
{
return currentFolder.Path;
}
}
break;
}
}
return null;
}
// COM Imports
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);

How do I determine the icon index for Desktop and Network for use in SHGetImageList?

I am able to successfully extract the icons for file system drives, folders and files using the APIs I included below. Additional info on the DLL imports etc. that helped me get this far can be found here. By calling the method GetExtraLargeIconForFolder I get a 48x48 sized image in the icon.
public enum ImageListIconSize : int
{
Large = 0x0,
Small = 0x1,
ExtraLarge = 0x2,
Jumbo = 0x4
}
private static IImageList GetSystemImageListHandle(ImageListIconSize size)
{
IImageList iImageList;
Guid imageListGuid = new Guid("46EB5926-582E-4017-9FDF-E8998DAA0950");
int ret = SHGetImageList(
(int)size,
ref imageListGuid,
out iImageList
);
return iImageList;
}
public static Icon GetExtraLargeIconForFolder(string path)
{
SHFILEINFO shinfo = new SHFILEINFO();
IntPtr retVal = SHGetFileInfo(
path, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo),
(int)(SHGetFileInfoConstants.SHGFI_SYSICONINDEX |
SHGetFileInfoConstants.SHGFI_ICON));
int iconIndex = shinfo.iIcon;
IImageList iImageList =
(IImageList)GetSystemImageListHandle(ImageListIconSize.ExtraLarge);
IntPtr hIcon = IntPtr.Zero;
if (iImageList != null)
{
iImageList.GetIcon(iconIndex,
(int)ImageListDrawItemConstants.ILD_TRANSPARENT, ref hIcon);
}
Icon icon = null;
if (hIcon != IntPtr.Zero)
{
icon = Icon.FromHandle(hIcon).Clone() as Icon;
DestroyIcon(shinfo.hIcon);
}
return icon;
}
In Windows Explorer one sees, icons for the Desktop, Network and Computer. How does one go about getting the correct icon index for these file system nodes?
You are nearly there. You still use SHGetFileInfo but instead you will need to pass SHGFI_PIDL in the flags parameter.
Then you need to specify the shell object of interest by passing a PIDL rather than a path. Obtain the PIDL by calling SHGetSpecialFolderLocation. Pass a CSIDL value to this routine, e.g. CSIDL_DESKTOP, CSIDL_DRIVES, CSIDL_NETWORK etc.

How to determine if a directory path was SUBST'd

How can I figure out if a file is in a folder that has been SUBST'ed or is located in a user folder using C#?
I think you need to P/Invoke QueryDosDevice() for the drive letter. Subst drives will return a symbolic link, similar to \??\C:\blah. The \??\ prefix indicates it is substituted, the rest gives you the drive+directory.
This is the code I use to get the information if a path is substed:
(Some parts come from pinvoke)
using System.Runtime.InteropServices;
[DllImport("kernel32.dll", SetLastError=true)]
static extern uint QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, int ucchMax);
public static bool IsSubstedPath(string path, out string realPath)
{
StringBuilder pathInformation = new StringBuilder(250);
string driveLetter = null;
uint winApiResult = 0;
realPath = null;
try
{
// Get the drive letter of the path
driveLetter = Path.GetPathRoot(path).Replace("\\", "");
}
catch (ArgumentException)
{
return false;
//<------------------
}
winApiResult = QueryDosDevice(driveLetter, pathInformation, 250);
if(winApiResult == 0)
{
int lastWinError = Marshal.GetLastWin32Error(); // here is the reason why it fails - not used at the moment!
return false;
//<-----------------
}
// If drive is substed, the result will be in the format of "\??\C:\RealPath\".
if (pathInformation.ToString().StartsWith("\\??\\"))
{
// Strip the \??\ prefix.
string realRoot = pathInformation.ToString().Remove(0, 4);
// add backshlash if not present
realRoot += pathInformation.ToString().EndsWith(#"\") ? "" : #"\";
//Combine the paths.
realPath = Path.Combine(realRoot, path.Replace(Path.GetPathRoot(path), ""));
return true;
//<--------------
}
realPath = path;
return false;
}
I think you have a few choices --
Via System.Management classes:
http://briancaos.wordpress.com/2009/03/05/get-local-path-from-unc-path/
Or
Via P/Invoking this MAPI function:
ScUNCFromLocalPath
http://msdn.microsoft.com/en-us/library/cc842520.aspx
If SUBST is run without parameters it produces a listing of all current substitutions. Get the list, and check your directory against the list.
There is also the issue of mapping a volume to a directory. I have never attempted to detect these, but the mount point directories do show up differently than regular directories, so they must have a different attribute of some kind, and that could be detected.

Get Firefox URL?

How can I get the url from a running instance of firefox using .NET 2.0 windows/console app? C# or VB codes will do.
Thanks!
Building on Rob Kennedy's answer and using NDde
using NDde.Client;
class Test
{
public static string GetFirefoxURL()
{
DdeClient dde = new DdeClient("Firefox", "WWW_GetWindowInfo");
dde.Connect();
string url = dde.Request("URL", int.MaxValue);
dde.Disconnect();
return url;
}
}
NB: This is very slow. It takes a few seconds on my computer. The result will look something like this :
"http://stackoverflow.com/questions/430614/get-firefox-url","Get Firefox URL? - Stack Overflow",""
More info on browser DDE here.
For most browsers, including Internet Explorer, Navigator, Firefox, and Opera, the supported and sanctioned way of doing this is to use DDE. The topic name in all of them is WWW_GetWindowInfo; only the name of the target window varies. That technique will be difficult for you, though, because .Net doesn't support DDE. If you can find a way to get around that limitation, you'll be all set.
it seems that this might be difficult, here's some discussion on it: http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/c60b1699-9fd7-408d-a395-110c1cd4f297/
You may want to check into the source code of WatiN. Their next version is open source and supports firefox, so I would imagine the functionality for doing this is in it.
Use MozRepl: https://github.com/bard/mozrepl/wiki/ + mozRepl .NET Connector: http://mozreplconnector.codeplex.com/releases/view/17398
var connect = new MozReplConnectDotNet.MozReplConnect(4242);
connect.Connect();
Console.WriteLine(connect.SendRecieve("gBrowser.currentURI.spec"));
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr parentHandle,
IntPtr childAfter, string className, IntPtr windowTitle);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd,
int msg, int wParam, StringBuilder ClassName);
private static string GetURL(IntPtr intPtr, string programName, out string url)
{
string temp=null;
if (programName.Equals("chrome"))
{
var hAddressBox = FindWindowEx(intPtr, IntPtr.Zero, "Chrome_OmniboxView", IntPtr.Zero);
var sb = new StringBuilder(256);
SendMessage(hAddressBox, 0x000D, (IntPtr)256, sb);
temp = sb.ToString();
}
if (programName.Equals("iexplore"))
{
foreach (InternetExplorer ie in new ShellWindows())
{
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(ie.FullName);
if (fileNameWithoutExtension != null)
{
var filename = fileNameWithoutExtension.ToLower();
if (filename.Equals("iexplore"))
{
temp+=ie.LocationURL + " ";
}
}
}
}
if (programName.Equals("firefox"))
{
DdeClient dde = new DdeClient("Firefox", "WWW_GetWindowInfo");
dde.Connect();
string url1 = dde.Request("URL", int.MaxValue);
dde.Disconnect();
temp = url1.Replace("\"","").Replace("\0","");
}
url = temp;
return temp;
}
Please do following to run this code
Add Reference > Com > Microsoft.Internet.Controls from VS.NET in your project
Download the bin from http://ndde.codeplex.com/ for DdeClient class and add it to your project
Please Let me know if any issue
Poor man's solution, if anything else fails: activate the Firefox window, send Ctrl+L (activates address bar), send Ctrl+C (copy selection, ie. URL, to clipboard) and read the clipboard.
Lot of issues with this method (among them it does strange stuff for the user if they are in front of the computer) so it is only a backup solution...
try this one:
//get all running process of firefox
Process[] procsfirefox = Process.GetProcessesByName("firefox");
foreach (Process firefox in procsfirefox)
{
//the firefox process must have a window
if (firefox.MainWindowHandle == IntPtr.Zero)
{
continue;
}
AutomationElement sourceElement = AutomationElement.FromHandle(firefox.MainWindowHandle);
AutomationElement editBox = sourceElement.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "Search with Google or enter address"));
// if it can be found, get the value from the editbox
if (editBox != null)
{
ValuePattern val = ((ValuePattern)editBox.GetCurrentPattern(ValuePattern.Pattern));
Console.WriteLine("\n Firefox URL found: " + val.Current.Value);
}
//-----------------------------find titlebar element for site title---------------------------------//
AutomationElement elmTitleBar = sourceElement.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.TitleBar));
if (elmTitleBar != null)
Console.WriteLine("\n Firefox TitleBar found: " + elmTitleBar.Current.Name);
}
full source code:https://github.com/Moeedahmad899/GetFirefoxURL

Categories