Avoiding duplicate icon resources in a .NET (C#) project - c#

I'm using Visual C# 2008 Express. I'd like to use the same icon for the application (ie, the icon shown for the .exe), and for the main form. Unfortunately, VC# doesn't seem to be very smart about this, and insists on duplicating the icon data.
There doesn't seem to be a way of selecting an "already embedded" icon for use in the form or project icon (only selecting a file), and using the same file for both icons just embeds the file twice as far as I can see. It's not a big deal (hard drive space is cheap now, right?), but it bugs me.
Any idea how to avoid this? Is there a way to programatically load the executable's icon for use when the form is constructed, say? A couple of forum posts about similar things seem to suggest that .NET resources don't use the normal old Windows resource system -- is there a way from within the framework of getting at the old-style resources? Or do I have to bind the Win32 API functions to do it?

You're right, and it's rather annoying.
You have to load the icons yourself instead of relying on designer-generated code. Save the icon as a project resource, then load the resource into the form's Icon property in the form's constructor:
this.Icon = Properties.Resources.myIconResourceName;

You're looking for Icon.ExtractAssociatedIcon. Call passing your executable:
var icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location);

Yeah, it's pretty annoying. But the problem with the proposed answer of Icon.ExtractAssociatedIcon is that it will retrieve the 32x32 icon, and then downsample to a 16x16 icon in your forms window or on the taskbar, which will look terrible unless your 32x32 icon is very cleverly constructed.
The way I'm doing it is with interop (put the first line in your form constructor):
this.Icon = ExtractSmallIconFromLibrary(Application.ExecutablePath);
...
public static Icon ExtractSmallIconFromLibrary(string file) {
IntPtr[] reficon = new IntPtr[1];
int nextracted = ExtractIconEx(file, 0, null, reficon, 1);
if (nextracted < 1)
return null;
Icon unmanaged_icon = Icon.FromHandle(reficon[0]);
Icon icon = (Icon)unmanaged_icon.Clone();
DestroyIcon(unmanaged_icon.Handle);
return icon;
}
[DllImport("Shell32", CharSet = CharSet.Auto)]
extern static int ExtractIconEx(
[MarshalAs(UnmanagedType.LPTStr)]
string lpszFile,
int nIconIndex,
IntPtr[] phIconLarge,
IntPtr[] phIconSmall,
int nIcons
);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
extern static bool DestroyIcon(IntPtr handle);
But this isn't great, either, since you do want the 32x32 icon for things like the Alt-Tab icon list. So you really need to extract the entire icon, which is a bigger job. Maybe there's a straightforward way to combine the two icons into one. Or you can do like this codeproject program, which extracts the whole icon in the first place with a huge pile of code.

I think in many cases including a duplicate icon is at the end of the day more efficient than trying to extract it from the unmanaged resource - considering you can't use Icon.ExtractAssociatedIcon for UNC paths.

I had a similar problem.
I have and exe icon I want to reuse for subforms without increasing file size.
//From MyApp
MySubForm msf = new MySubForm();
msf.Icon = this.Icon;
msf.Show();
I don't know if this is useful, but I want to share it in anyway.

Related

How do I arrange or sort the desktop icons in c#?

You know when you right-click the desktop and there's a "Sort by" option that allows you to sort the icons by "Name", "Size", "Item Type", or "Date Modified"? Well, I want to find a way to sort the desktop's icons with a push of a button.
I saw a similar question being asked here on stackoverflow but it's old and the code didn't work for me. The link to the question is: Arranging desktop icons with C# . I'm trying to achieve this in Windows 10.
There was a comment on there that said that the LVM_* and LVA_* values are stored in the commctrl.h file which comes with the SDK. I couldn't find that file for some reason.
Here's what i'm using:
//sort desktop
public const int LVM_ARRANGE = 4118;
public const int LVM_ALIGNLEFT = 1;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
//end of sort desktop
private void organizeBtn_Click(object sender, EventArgs e)
{
var DesktopHandle = GetDesktopWindow();
MessageBox.Show(GetDesktopWindow().ToString());
SendMessage(GetDesktopWindow(), LVM_ARRANGE, LVM_ALIGNLEFT, 0);
}
I've been digging for info or some sort of direction about this topic, especially regarding windows 10, but I can't find much. Please help?
In Windows 10, the desktop (not tile world!) is still a SysListView32, but the GetDesktopWindow API call will return the handle of its grandparent, a Progman window - reminiscence of the ancient "Program Manager" of Windows 3.0. Then there is a shim of the SHELLDLL_DefView class, and below that you find buried the listview you're after.
Use the information from this answer to move down from the shell window to the folder view, which you can eventually send the LVM_ARRANGE message.
This is a brittle approach, as it relies on undocumented properties of the operating system, which may change at any time with updates or new versions. It will probably break also when a user uses a slideshow as desktop background, because Windows then rearranges the desktop window stack. Hack to deal with this here.
Another approach which is documented and less likely to break in future versions, with the downside of involving COM and a nightmare from C#, is via the IFolderView of shell automation, two relevant finds here and here.

Clearing/Emptying office clipboard using C# Interop

I am working on an application which on the click of a button will clear both the system clipboard and the office clipboard.
I have tried System.Windows.Forms.Clipboard.Clear() and the following
static class WinAPI
{
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern bool OpenClipboard(System.IntPtr WinHandle);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern bool EmptyClipboard();
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern bool CloseClipboard();
public static void ClearClipboard()
{
if (OpenClipboard(System.IntPtr.Zero))
{
EmptyClipboard();
CloseClipboard();
}
}
}
Both of them seem clear only the system clipboard. Is there a way to extend this to office clipboard.
Per this question, the answer appears to be "no, not cleanly":
There has been no VBA support for manipulating the Office clipboard
after Excel 2000. SendKeys is flaky, but it's the only way.
The "SendKeys" referred to is a "solution" where you send the necessary keystrokes or window messages -- assuming one is running, if not, you'll have to start one. There are also other ways to do that, like AutoIt. Unfortunately, all of these solutions are fragile and prone to break between versions of Office, because they rely on internal details of window names and appearances. Before you do this, make sure you're willing to accept the maintenance burden.
A better approach is probably telling whoever wants this application that it can't be done because Microsoft wants clipboard control to remain with the end user -- or, alternatively, that there already is a button for clearing the clipboard, and it exists in Office (Alt, H, FO shows it in my version). That doesn't always work, but when it does it saves you a ton of work.
You could copy a single space to the clipboard, if the other methods are troublesome?
You can try the solution presented here. It's meant for VBA but you can easily convert the code to C#.
Let me know if you need help.

Why is the text from the title of another application window incorrect?

I'm running a small tool (on Windows 7, 32 Bit) that I would like to see what document is open in another application I've tried this, which works for NotePad on Windows.
var myProcess = Process.GetProcessesByName("NotePad");
string title = myProcess[0].MainWindowTitle;
MessageBox.Show(title);
the output is:
"New Text Document - Notepad"
Now, if I try another application it doesn't always give me the correct title but I noticed that most microsoft applications seem to be fine - NotePad, WordPad, EXCEL etc. It's this other software that is an issue. It has a long title but just returns a very simple name.
Here's what I get from my application which has processName = "FooBar"
The actual running window has this up the top:
"FooBar Software Verson 1.2 - [Results]"
and my code gives:
"FooBar"
any ideas?
[EDIT} 2012-11-19
The very crux of this issue is that I was trying to get the name of the open file from the window. It now seems that the software I'm using doesn't display it there. What I've discovered is that a program called "AutoIT3 Window Spy" can get the text I need as the text of the open file is on the window and not only in the title. I downloaded the source (it's part of http://www.autohotkey.com/ which is open source. It seems to rely on many of the suggestions already made but I'm not able to figure it out just yet.) The source code that I"m looking at is c++ and is located here https://github.com/AutoHotkey/AutoHotkey
So I think the solution to my problem may lay elsewhere. This one may go unanswered.
The main window title is what you see when you go in to task manager and look at the Description column, not the window title itself.
It's the title of the process, not the title of a particular window in the process. A given process may have any number of windows open at one time.
If you need the actual window title, you have to hook user32 something like this:
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Security;
namespace Application
{
public class Program
{
public static void Main ( )
{
IntPtr hwnd = UnsafeNativeMethods.FindWindow("Notepad", null);
StringBuilder stringBuilder = new StringBuilder(256);
UnsafeNativeMethods.GetWindowText(hwnd, stringBuilder, stringBuilder.Capacity);
Console.WriteLine(stringBuilder.ToString());
}
}
[SuppressUnmanagedCodeSecurity]
internal static class UnsafeNativeMethods
{
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern int GetWindowText ( IntPtr hWnd, [Out] StringBuilder lpString, int nMaxCount );
[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr FindWindow ( string lpClassName, string lpWindowName );
}
}
It's possible that the 'title' you're seeing is some kind of owner-drawn UI element which is overriding the visual representation of the title, while the Windows APIs are likely to ignore that sort of thing.
I'd recommend examining the window with a tool like Spy++ to see if that's the case.
It's also possible that the author of the application decided to override the WM_GETTEXT message and is returning that instead of what's actually in the titlebar, although I'm not 100% sure if GetWindowText() is being called here and whether or not it sends the message explicitly or works some other way.
The developer has stated the following:
"I think the failure may be due to a discontinued property Form.Caption in VB 6.0 that was replaced with a Form.Text in. NET"
Thank you all for your valuable suggestions along the way!

Code Access Security is preventing PInvoking Setup API calls

I'm rewording this question since I understand a bit more now. Originally, what I had was too vague. I've discovered that I'm being routed by something called "Code Access Security." This is old-hat to everyone reading this, I'm sure, but not to me.
The application is very large so in a nutshell I have two assemblies. One is a utilities assembly with various "tools" used throughout the program. The other is calling upon these tools in order to function.
In the utilities assembly, there are many functions that are PInvoked but the one giving me grief is: SetupDiGetDeviceInterfaceDetail() (see here). My function prototype looks like this:
[DllImport("SetupApi.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool SetupDiGetDeviceInterfaceDetail(
SafeHandleZeroOrMinusOneIsInvalid deviceInfoSet,
ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
IntPtr deviceInterfaceDetailData,
uint deviceInterfaceDetailDataSize,
IntPtr requiredSize,
IntPtr deviceInfoData);
In the assembly which uses this function, I'm using the two step process outlined in the remarks in order to gain the understanding of how much space I need to store the DevicePath which is in the SP_DEVICE_INTERFACE_DETAIL_DATA structure (see here). For example:
string GetDevicePath(SafeHandleSeroOrMinusOneIsInvalid hList, SP_DEVICE_INTERFACE_DATA infoSet)
{
IntPtr pReqSize = Marshal.AllocHGlobal(4);
Marshal.WriteInt32(pReqSize, 0);
uint reqSize;
// get the size needed
PInvoke.SetupDiGetDeviceInterfaceDetail(hList,
ref infoSet,
IntPtr.Zero,
0,
pReqSize,
IntPtr.Zero);
reqSize = (uint)Marshal.ReadInt32(pReqSize, 0);
IntPtr pDevInfoDetail = Marshal.AllocHGlobal((int)reqSize + 4); // +4 for cbSize
// call again, this time getting the actual data wanted
PInvoke.SetupDiGetDeviceInterfaceDetail(hList,
ref infoSet,
pDevInfoDetail,
reqSize,
IntPtr.Zero,
IntPtr.Zero);
string path;
// work .NET magic to read from unmanaged memory the path string and assign it
// to the above variable. Deallocate both unmanaged memory blocks.
return path;
}
The most frustrating thing is, these assemblies are used by two different programs. One is a GUI using the Visual Studio Isolated Shell. The other is simply a command line program. When the GUI is running, the above code is called and executes as expected. In the command line tool however, they fail (as described in the MSDN reference for this Setup API function) with some data about what happened. At this point, I'm able only to recover a portion of the data that is returned.
This is what comes back from the runtime:
stem.Security.PartialTrustVisibilityLevel, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
I know this has something to do with Code Access Security but I'm not at all sure how to fix. Using some suggestions that I've found thus far I've tried this attribute to the assembly (I placed it before the namespace block of code):
[assembly: AllowPartiallyTrustedCallers]
But this caused other compilation problems.
Please, anything would be most helpful and greatly appreciated.
Andy
Unfortunately, the problem isn't yet fixed. However, the problem appears to have nothing to do with Code Access Security as I first thought. I was being thrown a red herring. I stepped my way through the code using the memory window in Visual Studio and noticed that these strings were in memory before calling the Setup API function to fill them. Occasionally, I would get a different block of memory with different contents too, I just usually ended up with the contents I pasted.
The problem actually appears to have something to do with the 64 vs. 32 bit environments (at least, that's my theory at this point).
However, this question isn't the actual problem so I'm "answering" it to close it.

Console Application with selectable text

I want the user to be able to select the text (just like in Commandprompt) where you right click on the console application's surface and a menu will show, the user can then choose same functions as in commandprompt:
Mark
Copy (Shortcut: Enter)
Paste
Select All
Scroll
Find
I have tried to Google after things like "C# Console Application select text" and other kind of things but can't seem to find a proper solution for this, since the user should be able to mark the text he/she wan't to copy or replace (with paste).
Do you have a solution for my question?
There are no managed methods to do this, but quick edit mode can be enabled through P/Invoke. Quick edit mode allows console text to be selected with the mouse and copied, and for text to be pasted with the right-moue button. (See this article for a description of quick edit mode.)
// using System.Runtime.InteropServices;
[DllImport("kernel32.dll")]
static extern bool SetConsoleMode(IntPtr hConsoleHandle, int mode);
[DllImport("kernel32.dll")]
static extern bool GetConsoleMode(IntPtr hConsoleHandle, out int mode);
[DllImport("kernel32.dll")]
static extern IntPtr GetStdHandle(int handle);
const int STD_INPUT_HANDLE = -10;
const int ENABLE_QUICK_EDIT_MODE = 0x40 | 0x80;
public static void EnableQuickEditMode()
{
int mode;
IntPtr handle = GetStdHandle(STD_INPUT_HANDLE);
GetConsoleMode(handle, out mode);
mode |= ENABLE_QUICK_EDIT_MODE;
SetConsoleMode(handle, mode);
}
maybe I didn't get you but when you execute your console application it will be hosted into a command-prompt window which allows you to copy end past text where ever you like.
You can't do context menu in console apps or in the command prompt.
Console Apps act exactly like the default cmd.exe. You need to go to the menu by clicking the icon on the top left, and the edit menu will give you the options you've listed.
You can also go to properties to turn quick edit on.
If you build the command prompt app then you'll get the select/copy/paste behavior for free. If you want to implement a right click menu (context menu) I don't think you can.
Maybe to simple but you can implement a simple switch based menu:

Categories