Is it possible to send Toast notification from console application? - c#

Is it possible to send Toast notifications from console application using ToastNotificationManager ?
I know that it is possible to send Toast notifications from Windows Universal app:
var toast = new ToastNotification(doc);
ToastNotificationManager.CreateToastNotifier().Show(toast);
*doc - Toast stored in XML string
To use ToastNotificaionManager I need Windows.UI.Notifications library which I can't reference in console application project.
The library I mentionet before is actualy used by WinRT. Is it possible to use WinRT APIs in Windows console application ?

At first you need to declare that your program will be using winRT libraries:
Right-click on your yourProject, select Unload Project
Right-click on your yourProject(unavailable) and click Edit yourProject.csproj
Add a new property group:<targetplatformversion>8.0</targetplatformversion>
Reload project
Add reference Windows from Windows > Core
Now you need to add this code:
using Windows.UI.Notifications;
and you will be able to send notifications using this code:
var toast = new ToastNotification(doc);
ToastNotificationManager.CreateToastNotifier().Show(toast);
Reference: How to call WinRT APIs in Windows 8 from C# Desktop Applications - WinRT Diagram

I ran into some problems here with Evaldas B's Code I was missing a string.
(Where It Says Need String Here)
.CreateToastNotifier(<needed a string here>).Show(toast);
warning I am kind of new to C# so my code probably sucks- but it does work and is pretty simplistic and that's more than I can say for most solutions I have found
Also I was having a hell of a time getting the xml document to read. I was fighting with System.xml (I think) and Windows.Data.Dom.Xml (also not completely sure).
In the end I settled on making them hard coded strings for my example file and used a switch statement to switch between them.
I have found a ton of people, looking for the solution that I have come up with, on stack overflow. It seems use of the toast notification system with console or background applications would be super useful, and the documentation that surrounds the toast notification system with windows applications all suggest that it needs to be used with an application. The Action Center is super useful for notifications vrs the NotificationTray/NotifyIcon route. I have not found a full solution anywhere else on the web. Here is example code.
/*
At first you need to declare that your program will be using winRT libraries:
1. Right click on your yourProject, select Unload Project
2. Right click on your youProject(unavailable) and click Edit yourProject.csproj
3. Add a new property group:<TargetPlatformVersion>8.0</TargetPlatformVersion>
4. Reload project
5. Add referece Windows from Windows > Core
*/
using System;
using Windows.Data.Xml.Dom;
using Windows.Storage;
using Windows.Storage.Streams;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Notifications;
namespace ConsoleApplication6
{
public class NewToastNotification
{
public NewToastNotification(string input, int type)
{
string NotificationTextThing = input;
string Toast = "";
switch (type)
{
case 1:
{
//Basic Toast
Toast = "<toast><visual><binding template=\"ToastImageAndText01\"><text id = \"1\" >";
Toast += NotificationTextThing;
Toast += "</text></binding></visual></toast>";
break;
}
default:
{
Toast = "<toast><visual><binding template=\"ToastImageAndText01\"><text id = \"1\" >";
Toast += "Default Text String";
Toast += "</text></binding></visual></toast>";
break;
}
}
XmlDocument tileXml = new XmlDocument();
tileXml.LoadXml(Toast);
var toast = new ToastNotification(tileXml);
ToastNotificationManager.CreateToastNotifier("New Toast Thing").Show(toast);
}
}
class Program
{
static void Main(string[] args)
{
NewToastNotification Window = new NewToastNotification("Yes",1);
}
}
}

1) For a toast notification to appear using a console or Desktop application, your application must have a shortcut on the start menu.
2) For an application to have a shortcut icon(not tile icon) in the start menu of Windows, your app must have an AppId.
To create a short cut for you you application create a new class named ShellHelpers.cs and Paste this code in it.
using System;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.WindowsAPICodePack.Shell.PropertySystem;
using MS.WindowsAPICodePack.Internal;
namespace DesktopToastsSample.ShellHelpers
{
internal enum STGM : long
{
STGM_READ = 0x00000000L,
STGM_WRITE = 0x00000001L,
STGM_READWRITE = 0x00000002L,
STGM_SHARE_DENY_NONE = 0x00000040L,
STGM_SHARE_DENY_READ = 0x00000030L,
STGM_SHARE_DENY_WRITE = 0x00000020L,
STGM_SHARE_EXCLUSIVE = 0x00000010L,
STGM_PRIORITY = 0x00040000L,
STGM_CREATE = 0x00001000L,
STGM_CONVERT = 0x00020000L,
STGM_FAILIFTHERE = 0x00000000L,
STGM_DIRECT = 0x00000000L,
STGM_TRANSACTED = 0x00010000L,
STGM_NOSCRATCH = 0x00100000L,
STGM_NOSNAPSHOT = 0x00200000L,
STGM_SIMPLE = 0x08000000L,
STGM_DIRECT_SWMR = 0x00400000L,
STGM_DELETEONRELEASE = 0x04000000L,
}
internal static class ShellIIDGuid
{
internal const string IShellLinkW = "000214F9-0000-0000-C000-000000000046";
internal const string CShellLink = "00021401-0000-0000-C000-000000000046";
internal const string IPersistFile = "0000010b-0000-0000-C000-000000000046";
internal const string IPropertyStore = "886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99";
}
[ComImport,
Guid(ShellIIDGuid.IShellLinkW),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellLinkW
{
UInt32 GetPath(
[Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile,
int cchMaxPath,
//ref _WIN32_FIND_DATAW pfd,
IntPtr pfd,
uint fFlags);
UInt32 GetIDList(out IntPtr ppidl);
UInt32 SetIDList(IntPtr pidl);
UInt32 GetDescription(
[Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile,
int cchMaxName);
UInt32 SetDescription(
[MarshalAs(UnmanagedType.LPWStr)] string pszName);
UInt32 GetWorkingDirectory(
[Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir,
int cchMaxPath
);
UInt32 SetWorkingDirectory(
[MarshalAs(UnmanagedType.LPWStr)] string pszDir);
UInt32 GetArguments(
[Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs,
int cchMaxPath);
UInt32 SetArguments(
[MarshalAs(UnmanagedType.LPWStr)] string pszArgs);
UInt32 GetHotKey(out short wHotKey);
UInt32 SetHotKey(short wHotKey);
UInt32 GetShowCmd(out uint iShowCmd);
UInt32 SetShowCmd(uint iShowCmd);
UInt32 GetIconLocation(
[Out(), MarshalAs(UnmanagedType.LPWStr)] out StringBuilder pszIconPath,
int cchIconPath,
out int iIcon);
UInt32 SetIconLocation(
[MarshalAs(UnmanagedType.LPWStr)] string pszIconPath,
int iIcon);
UInt32 SetRelativePath(
[MarshalAs(UnmanagedType.LPWStr)] string pszPathRel,
uint dwReserved);
UInt32 Resolve(IntPtr hwnd, uint fFlags);
UInt32 SetPath(
[MarshalAs(UnmanagedType.LPWStr)] string pszFile);
}
[ComImport,
Guid(ShellIIDGuid.IPersistFile),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IPersistFile
{
UInt32 GetCurFile(
[Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile
);
UInt32 IsDirty();
UInt32 Load(
[MarshalAs(UnmanagedType.LPWStr)] string pszFileName,
[MarshalAs(UnmanagedType.U4)] STGM dwMode);
UInt32 Save(
[MarshalAs(UnmanagedType.LPWStr)] string pszFileName,
bool fRemember);
UInt32 SaveCompleted(
[MarshalAs(UnmanagedType.LPWStr)] string pszFileName);
}
[ComImport]
[Guid(ShellIIDGuid.IPropertyStore)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IPropertyStore
{
UInt32 GetCount([Out] out uint propertyCount);
UInt32 GetAt([In] uint propertyIndex, out PropertyKey key);
UInt32 GetValue([In] ref PropertyKey key, [Out] PropVariant pv);
UInt32 SetValue([In] ref PropertyKey key, [In] PropVariant pv);
UInt32 Commit();
}
[ComImport,
Guid(ShellIIDGuid.CShellLink),
ClassInterface(ClassInterfaceType.None)]
internal class CShellLink { }
public static class ErrorHelper
{
public static void VerifySucceeded(UInt32 hresult)
{
if (hresult > 1)
{
throw new Exception("Failed with HRESULT: " + hresult.ToString("X"));
}
}
}
}
Code for creating a shortcut(This code can be added to the same class where you will be showing the toast)
public bool TryCreateShortcut()
{
String shortcutPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Microsoft\\Windows\\Start Menu\\Programs\\FixSus Toasts Sample .lnk";
if (!File.Exists(shortcutPath))
{
InstallShortcut(shortcutPath);
return true;
}
return false;
}
private void InstallShortcut(String shortcutPath)
{
// Find the path to the current executable
String exePath = Process.GetCurrentProcess().MainModule.FileName;
IShellLinkW newShortcut = (IShellLinkW)new CShellLink();
// Create a shortcut to the exe
DesktopToastsSample.ShellHelpers.ErrorHelper.VerifySucceeded(newShortcut.SetPath(exePath));
DesktopToastsSample.ShellHelpers.ErrorHelper.VerifySucceeded(newShortcut.SetArguments(""));
// Open the shortcut property store, set the AppUserModelId property
IPropertyStore newShortcutProperties = (IPropertyStore)newShortcut;
using (PropVariant appId = new PropVariant(APP_ID))
{
DesktopToastsSample.ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutProperties.SetValue(SystemProperties.System.AppUserModel.ID, appId));
DesktopToastsSample.ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutProperties.Commit());
}
// Commit the shortcut to disk
IPersistFile newShortcutSave = (IPersistFile)newShortcut;
DesktopToastsSample.ShellHelpers.ErrorHelper.VerifySucceeded(newShortcutSave.Save(shortcutPath, true));
}
Now you can create an show a toast
// Get a toast XML template
XmlDocument toastXml = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastImageAndText04);
// Fill in the text elements
XmlNodeList stringElements = toastXml.GetElementsByTagName("text");
stringElements[1].AppendChild(toastXml.CreateTextNode("Message" + newMessage));
// Specify the absolute path to an image
string codeWebFolderPath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, #"..\..\"));
String imagePath = "file:///" + Path.GetFullPath(codeWebFolderPath+ "Resources\\FixSus.png");
XmlNodeList imageElements = toastXml.GetElementsByTagName("image");
imageElements[0].Attributes.GetNamedItem("src").NodeValue = imagePath;
// Create the toast and attach event listeners
ToastNotification toast = new ToastNotification(toastXml);
toast.Activated += ToastActivated;
toast.Dismissed += ToastDismissed;
toast.Failed += ToastFailed;
// Show the toast. Be sure to specify the AppUserModelId on your application's shortcut!
ToastNotificationManager.CreateToastNotifier(APP_ID).Show(toast);
The APP_ID can be any string. In my case it was "NotificationTest.KEY"
Note: Dont modify the ShellHelper class.
Edit : Follow Evaldas B's answer first then apply this solution.

Related

How to open file with Process.Start if there is no associated program for the file type?

When I want to open an .ico file using Process.Start it throws an error System.ComponentModel.Win32Exception and this is because there is no default program to open that file. I need to show the window to select the default program instead of throwing exception. How can I do that?
private void btnOpenFile_Click(object sender, EventArgs e)
{
Process.Start(txtSavedAs.Text);
}
What you want to do is pinvoke the AssocQueryString API. Documentation here.
I use that API to get a command string associated with a shell verb. So, for example, if I use the .txt extension, it would return:
C:\Windows\system32\NOTEPAD.EXE %1
Now we know that shell knows what program to execute and how to pass in the command-line argument for that specific extension.
So, if there is a "Command" associated with that extension, it is safe to assume shell will know how to execute that type of file; Hence we should be able to use ShellExecute normally.
If there is no "Command" associated with that file extension, we will show the "openas" dialog allowing the user to pick the application they want to open the file.
Here is a class I put together to do that work:
AppAssociation.cs
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
public static class AppAssociation
{
private static class Win32Native
{
public const int ASSOCF_NONE = 0;
public const int ASSOCSTR_COMMAND = 1;
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode,
EntryPoint = "AssocQueryStringW")]
public static extern uint AssocQueryString(int flags, int str,
string pszAssoc, string pszExtra, StringBuilder pszOut, ref uint pcchOut);
}
public static Process StartProcessForFile(FileInfo file)
{
var command = GetCommandForFileExtention(file.Extension);
return Process.Start(new ProcessStartInfo()
{
WindowStyle = ProcessWindowStyle.Normal,
FileName = file.FullName,
Verb = string.IsNullOrEmpty(command) ? "openas" : null,
UseShellExecute = true,
ErrorDialog = true
});
}
private static string GetCommandForFileExtention(string ext)
{
// query length of the buffer we need
uint length = 0;
if (Win32Native.AssocQueryString(Win32Native.ASSOCF_NONE,
Win32Native.ASSOCSTR_COMMAND, ext, null, null, ref length) == 1)
{
// build the buffer
var sb = new StringBuilder((int)length);
// ask for the actual command string with the right-sized buffer
if (Win32Native.AssocQueryString(Win32Native.ASSOCF_NONE,
Win32Native.ASSOCSTR_COMMAND, ext, null, sb, ref length) == 0)
{
return sb.ToString();
}
}
return null;
}
}
You would call this like so:
AppAssociation.StartProcessForFile(new FileInfo(#"c:\MyFiles\TheFile.txt"));

Save Mail in MIME format (*.eml) in Outlook Add-In

I want to write a little Outlook addin (C#), which saves a selected mail (MailItem) to disk in plain MIME format (.eml).
The MailItem.SaveAs() method only allows to save in .msg format.
Is there any other (simple) way, to save the mail in eml-format? I want to keep all details of the original mail.
I've read something about the Outlook WebServices. Maybe I can search the exchange server for the currently in outlook selected mail and receive it again from exchange and save it as .eml? What will I need for this option?
Is it possible to convert a saved .msg to .eml in an easy way (with keeping all details, headers and so on)?
I hope someone can help me with this problem, because I have spent a couple of hours on searching for a solution without any result.
Here's an IConvertSession proxy for C#:
using Microsoft.Office.Interop.Outlook;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace Exchange.Export.MAPIMessageConverter
{
internal class MAPIMethods
{
[Flags]
public enum MAPITOMIMEFLAGS
{
CCSF_SMTP = 0x0002,
CCSF_NOHEADERS = 0x0004,
CCSF_USE_TNEF = 0x0010,
CCSF_INCLUDE_BCC = 0x0020,
CCSF_8BITHEADERS = 0x0040,
CCSF_USE_RTF = 0x0080,
CCSF_PLAIN_TEXT_ONLY = 0x1000,
CCSF_NO_MSGID = 0x4000,
}
[Flags]
public enum CLSCTX
{
CLSCTX_INPROC_SERVER = 0x1,
CLSCTX_INPROC_HANDLER = 0x2,
CLSCTX_LOCAL_SERVER = 0x4,
CLSCTX_REMOTE_SERVER = 0x10,
CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER,
CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,
CLSCTX_ALL = CLSCTX_SERVER | CLSCTX_INPROC_HANDLER
}
public static Guid CLSID_IConverterSession = new Guid("{4e3a7680-b77a-11d0-9da5-00c04fd65685}");
public static Guid IID_IConverterSession = new Guid("{4b401570-b77b-11d0-9da5-00c04fd65685}");
public enum ENCODINGTYPE
{
IET_BINARY = 0,
IET_BASE64 = 1,
IET_UUENCODE = 2,
IET_QP = 3,
IET_7BIT = 4,
IET_8BIT = 5,
IET_INETCSET = 6,
IET_UNICODE = 7,
IET_RFC1522 = 8,
IET_ENCODED = 9,
IET_CURRENT = 10,
IET_UNKNOWN = 11,
IET_BINHEX40 = 12,
IET_LAST = 13
}
public enum MIMESAVETYPE
{
SAVE_RFC822 = 0,
SAVE_RFC1521 = 1
}
[ComVisible(false)]
[ComImport()]
[Guid("00020307-0000-0000-C000-000000000046")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IMessage
{
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("4b401570-b77b-11d0-9da5-00c04fd65685")]
public interface IConverterSession
{
[PreserveSig]
int Placeholder0();
[PreserveSig]
uint SetEncoding(
[In, MarshalAs(UnmanagedType.I4)] ENCODINGTYPE DispId
);
[PreserveSig]
int Placeholder1();
[PreserveSig]
uint MIMEToMAPI(
[In, MarshalAs(UnmanagedType.Interface)]
Stream pstm,
[Out, MarshalAs(UnmanagedType.Interface)]
MailItem pmsg,
object pszSrcSrv,
uint ulFlags
);
[PreserveSig]
uint MAPIToMIMEStm(
[In, MarshalAs(UnmanagedType.Interface)]
IMessage pmsg,
[Out, MarshalAs(UnmanagedType.Interface)]
IStream pstm,
MAPITOMIMEFLAGS ulFlags
);
[PreserveSig]
int Placeholder2();
[PreserveSig]
int Placeholder3();
[PreserveSig]
int Placeholder4();
[PreserveSig]
int SetTextWrapping(
bool fWrapText,
uint ulWrapWidth
);
[PreserveSig]
uint SetSaveFormat(
[In, MarshalAs(UnmanagedType.I4)]
MIMESAVETYPE mstSaveFormat
);
[PreserveSig]
int Placeholder5();
[PreserveSig]
int Placeholder6();
}
}
}
Using it:
private Stream GetEmlStream(Outlook.MailItem mail)
{
Type converter = Type.GetTypeFromCLSID(MAPIMethods.CLSID_IConverterSession);
object obj = Activator.CreateInstance(converter);
MAPIMethods.IConverterSession session = (MAPIMethods.IConverterSession)obj;
if (session != null)
{
uint hr = session.SetEncoding(MAPIMethods.ENCODINGTYPE.IET_QP);
hr = session.SetSaveFormat(MAPIMethods.MIMESAVETYPE.SAVE_RFC822);
var stream = new ComMemoryStream();
hr = session.MAPIToMIMEStm((MAPIMethods.IMessage)mail.MAPIOBJECT, stream, MAPIMethods.MAPITOMIMEFLAGS.CCSF_SMTP);
if (hr != 0)
throw new ArgumentException(There are some invalid COM arguments");
stream.Position = 0;
return stream;
}
return null;
}
ComMemoryStream: https://stackoverflow.com/a/6602066/2532356
Other fonts:
http://www.pcreview.co.uk/threads/iconvertersession-in-c.3716714/
http://www.microsoft-questions.com/microsoft/Plaform-SDK-Mapi/31018989/mimetomapi-and-mapitomimestm-method-take-a-pointer-to-an-imessage-extended.aspx
You can either
Create MIME file explicitly in your code one property at a time. You can also use existing MIME converters (I used Lumisoft in the past) - but they won't convert Outlook messages in a single call; you will need to expliiclty build all the headers and MIME parts.
Use IConverterSession object (C++ or Delphi only) - this is the same MIME converter used by Outlook. You can play with it in OutlookSpy (I am its author) - click IConverterSession button. Note that as of Outlook 2016, IConverterSession interface can be used only if running inside the outlook.exe address space (COM addin). You cannot create an instance of that interface when running in a separate process.
Use Redemption (I am also its author) and its RDOMail.SaveAs or SafeMailItem.SaveAs methods - it can save in the MIME format (olRfc822) along with a dozen or so other formats. It uses IConverterSession object when it is available (Outlook 2003 and up) or its own converter otherwise. The following script (VBS) will save the currently selected message in Outlook as an EML file
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set rItem = Session.GetMessageFromID(Application.ActiveExplorer.Selection(1).EntryID)
rItem.SaveAs "c:\temp\test.eml", 1024`

WPF application file association: DefaultIcon is not working

I want to association ".abc" file to my WPF application.
I add the association using this code:
public class FileAssociation
{
static RegistryKey Root
{
get
{
return Registry.CurrentUser;
}
}
// Associate file extension with progID, description, icon and application
public static void Associate(string extension,
string progID, string description, string application)
{
Require.NotNullOrEmpty(extension, "extension");
Require.NotNullOrEmpty(progID, "progID");
Require.NotNullOrEmpty(application, "application");
Require.NotNullOrEmpty(description, "description");
Root.CreateSubKey(extension).SetValue("", progID);
using (var key = Root.CreateSubKey(progID))
{
key.SetValue("", description);
key.CreateSubKey("DefaultIcon").SetValue("", ToShortPathName(application).Quote() + ",0");
key.CreateSubKey(#"Shell\Open\Command").SetValue("", ToShortPathName(application).Quote() + " \"%1\"");
// Tell explorer the file association has been changed
SHChangeNotify(0x08000000, 0x0000, IntPtr.Zero, IntPtr.Zero);
}
}
// Return true if extension already associated in registry
public static bool IsAssociated(string extension)
{
return (Root.OpenSubKey(extension, false) != null);
}
[DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
[DllImport("Kernel32.dll")]
private static extern uint GetShortPathName(string lpszLongPath,
[Out] StringBuilder lpszShortPath, uint cchBuffer);
// Return short path format of a file name
private static string ToShortPathName(string longName)
{
StringBuilder s = new StringBuilder(1000);
uint iSize = (uint)s.Capacity;
uint iRet = GetShortPathName(longName, s, iSize);
return s.ToString();
}
}
Note: The Quote() extension method is used just to make string abc to "abc".
Now the file association works fine! I can double click the ".abc" files to open my WPF app.
But the DefaultIcon is not working. The DefaultIcon Registery key is set to "D:\path\to\MyWPFApp.exe",0. The application icon of my WPF app is set to an icon in the properties page (I can see that the icon of MyWPFApp.exe is already changed). What's wrong? Thanks!
BTW: I'm using .NET 4 in Windows 8
You don't need the DefaultIcon entry. The first icon is used by default.
remove it and it should work ^^
If I remove the ToShortPathName (long name is ok with quotes) and
change the Root property returns Registry.ClassesRoot the code works here.

How to add an icon to an existing exe file (without an icon by default)?

When I was searching on Google I found a useful class which let us change the icon of any .exe file using the following line of code :
WindowsFormsApplication1.IconInjector.InjectIcon("myfile.exe", "myicon.ico", 200, 1);
Where 200 and 1 are respectively icon GroupID and icon BaseID which I can determine using Resource Hacker. In this case the file's icon changes successfully without corrupting the file.
So i planned to use this class on my program which is a SFX / Software protector, the output file always hasn't an icon, all what I can see on Resource hacker is the below :
i can't see icon group id nor the base id, anyway, (I don't know what to put instead of 200 and 1 in this case) So I tried to change the icon using the same line of code mentioned above, I used the following line of code (same as above):
WindowsFormsApplication1.IconInjector.InjectIcon("myfile.exe", "myicon.ico", 200, 1);
The file icon was successfully changed but the file doesn't work anymore!
When I tried to reopen the file using ResourceHacker, I found the below:
It seems that the icon resources were successfully added, but i can't figure out why the file doesn't work anymore, it seems that is corrupted.
Any help would be appreciated.
Note : I tried using this class with unprotected file and it works like a charm!
The class I am using is the below:
// IconInjector.cs
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace WindowsFormsApplication1
{
/// <summary>
/// IconInjectorクラスの定義
/// </summary>
public class IconInjector
{
[DllImport("kernel32.dll", SetLastError = true)]
//static extern bool UpdateResource(IntPtr hUpdate, string lpType, string lpName, ushort wLanguage, IntPtr lpData, uint cbData);
static extern int UpdateResource(IntPtr hUpdate, uint lpType, uint lpName, ushort wLanguage, byte[] lpData, uint cbData);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr BeginUpdateResource(string pFileName,
[MarshalAs(UnmanagedType.Bool)]bool bDeleteExistingResources);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);
/// <summary>
/// アプリケーションのメイン エントリ ポイントです。
/// </summary>
[STAThread]
public static void InjectIcon(string execFileName, string iconFileName, uint iconGroupID, uint iconBaseID)
{
const uint RT_ICON = 3;
const uint RT_GROUP_ICON = 14;
// アイコンファイルの読み込み
IconFile iconFile = new IconFile();
iconFile.Load(iconFileName);
// リソースの更新開始
IntPtr hUpdate = BeginUpdateResource(execFileName, false);
Debug.Assert(hUpdate != IntPtr.Zero);
// RT_GROUP_ICON 書き込み
byte[] data = iconFile.CreateIconGroupData(iconBaseID);
UpdateResource(hUpdate, RT_GROUP_ICON, iconGroupID, 0, data, (uint)data.Length);
// RT_ICON書き込み
for (int i = 0; i < iconFile.GetImageCount(); i++)
{
byte[] image = iconFile.GetImageData(i);
UpdateResource(hUpdate, RT_ICON, (uint)(iconBaseID + i), 0, image, (uint)image.Length);
}
// リソースの更新終了
EndUpdateResource(hUpdate, false);
}
}
}
Any help or suggestion on adding the icon to the protected file without corrupting it?
It sounds like the protection application is verifying that the contents of the file haven't been tampered with. Injecting an icon is definitely a form of tampering, and unless the protection software is updated to ignore it, it will always fail. Alternatively if you own the protection software you could update it to not strip the icons.
I just experienced the same issue with a 7zip Self-Extractor exe.
Updating the icon of the 7zS.sfx (instead of the exe) before creating the Self-Extractor exe does the trick and the exe is not corrupted.
Your application's icon can be added to this executable with a tool like Resource Hacker.
and visit http://georezo.net/jparis/MI_Enviro/Icons/adding_w_RH.htm

How to open text in Notepad from .NET?

When I click a button on a Windows Forms form, I would like to open a Notepad window containing the text from a TextBox control on the form.
How can I do that?
You don't need to create file with this string. You can use P/Invoke to solve your problem.
Usage of NotepadHelper class:
NotepadHelper.ShowMessage("My message...", "My Title");
NotepadHelper class code:
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace Notepad
{
public static class NotepadHelper
{
[DllImport("user32.dll", EntryPoint = "SetWindowText")]
private static extern int SetWindowText(IntPtr hWnd, string text);
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
public static void ShowMessage(string message = null, string title = null)
{
Process notepad = Process.Start(new ProcessStartInfo("notepad.exe"));
if (notepad != null)
{
notepad.WaitForInputIdle();
if (!string.IsNullOrEmpty(title))
SetWindowText(notepad.MainWindowHandle, title);
if (!string.IsNullOrEmpty(message))
{
IntPtr child = FindWindowEx(notepad.MainWindowHandle, new IntPtr(0), "Edit", null);
SendMessage(child, 0x000C, 0, message);
}
}
}
}
}
References (pinvoke.net and msdn.microsoft.com):
SetWindowText: pinvoke | msdn
FindWindowEx: pinvoke | msdn
SendMessage: pinvoke | msdn
Try this out:
System.IO.File.WriteAllText(#"C:\test.txt", textBox.Text);
System.Diagnostics.Process.Start(#"C:\test.txt");
Save the file to disk using File.WriteAllText:
File.WriteAllText("path to text file", myTextBox.Text);
Then use Process.Start to open it in notepad:
Process.Start("path to notepad.exe", "path to text file");
For non ASCII user.
[DllImport("User32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
private static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
Based on #Peter Mortensen answer
Add CharSet = CharSet.Unicode to the attribute for supporting Unicode characters
I was using the NotepadHelper solution until I discovered it doesn't work on Windows 11. Writing the file to disk and starting with the default text editor seems to be the best solution. This has already been posted, but I discovered you need to pass UseShellExecute=true.
System.IO.File.WriteAllText(path, value);
System.Diagnostics.ProcessStartInfo psi = new() { FileName = path, UseShellExecute = true };
System.Diagnostics.Process.Start(psi);
I write to the System.IO.Path.GetTempPath() folder and run a cleanup when the application exits - searching for a unique prefix pattern for file names used by my app. Something like this:
string pattern = TempFilePrefix + "*.txt";
foreach (string f in Directory.EnumerateFiles(Path.GetTempPath(), pattern))
{
File.Delete(f);
}

Categories