9

I would like to show balloon notifications from batches that run during long time.

I know you can show popups (and some solutions are very imaginative), but it would be interesting to have this option too.

Is there a way you can show a balloon notification from a batch file? Starting from Windows 7 at least please but XP welcome too.

I suppose it would necessary at least a tray icon but frankly I cannot go much farther. Maybe with rundll32.exe? I know this is not a recommended way to do anything from command line.

The function signature required for functions called by rundll32.exe is documented in this Knowledge Base article. That hasn't stopped people from using rundll32 to call random functions that weren't designed to be called by rundll32, like user32 LockWorkStation or user32 ExitWindowsEx.

But I found this RunAnyDll utility by Yaniv Pessach that appears to make the .dll calls right. Unfortunately, I found not a working link to test it (no, it isn't in the Internet archive). I've written the author to ask him for one. Let's see.

(I don't even know if it will be useful but it's an interesting utility. Maybe it could make work this answer to the popup question. I think the difference between the Run box and the command line execution has something to do with the calling convention thing in Raymond Chen post.)

Another unfindable utility to test is the deprecated eventtriggers but again I'm not sure if it would be of help.

Edit to clarify:

Both npocmaka and greg zakharov answers are very interesting. npocmaka's one is nearer to what I was asking because it produces a balloon notification. greg zakharov's uses a diferent technique but its outcome is very similar (in this case a text appearing at mouse pointer location). I've tested them in XP and both work (with .NET installed). Both have a timeout and I don't know how to keep the notification until the user clicks (just in case the user is out but you want to see it).

Ideally, my question was about getting something like this.

@echo off

echo Some long part job that goes well.

call :balloon "Report about above tasks" [optional parameters about icon, title, timeout, etc.]

echo More tasks but one fails
if errorlevel 1 call :balloon "Error this and that" [optional parameters]

echo This is the end of the tasks
goto :eof

:balloon

    echo If you don't want to make a temporary file
    echo then you must make one-liner calls to PowerShell, wscript or whatever.

exit /b

So being hybrid scripts the drawback I see is that it must be very difficult to insert this inside any batch. Generating an .exe for every balloon seems impracticable if you are going to reuse several times in your batch so, in the end, as greg zakharov states, it's more robust and flexible to generate just an .exe that accept parameters and reuse it (just as notifu). In the end, I suppose that using a :balloon subroutine that makes a one-liner powershell call is the best you can get (almost natively in XP because you need a download but you also need .net).

7
  • You will be more successful, taking PowerShell as a base not batch. There are be plenty of links to be found.
    – user6811411
    Commented Jun 19, 2018 at 11:32
  • By balloon notifications you mean messages from the system tray ? I the link to the MS article leads to nowhere. Though your only option is to use Platform Invoke through .NET which means using C#/VB.NET/JScript.NET/powershell which can be embedded in batch. Probably here - pinvoke.net - you'll be able to find examples for what you looking for.
    – npocmaka
    Commented Jun 19, 2018 at 11:55
  • 1
    Looks like .net api has direct calls to the system tray - stackoverflow.com/questions/13373060/…
    – npocmaka
    Commented Jun 19, 2018 at 11:59
  • Have you tried using powershell? I can give you my script for it though it requires some security settings to be changed, I also have a cmdlet if you’re interested
    – Mark Deven
    Commented Jun 21, 2018 at 2:11
  • 1
    I don't know powershell (I'm not even sure of what a cmdlet is) and I am not very fond of it because of default security settings, that makes difficult use it in any system, but that doesn't matter. Maybe I use it, but even if I don't you should as well give your solution just in case some other people looking for a way to do this can use it. So, please, if your answer is relevant is welcome.
    – cdlvcdlv
    Commented Jun 21, 2018 at 8:17

5 Answers 5

16

Using a PowerShell one-liner

No need to create a file, it works no matter what execution policy is. Thanks to Mark Dodsons that showed us the way to do this with PS and to npocmaka for the icons list.

Save as, for example, balloon.bat (of course, you can insert this as subroutine in any batch).

@echo off

if "%~1"=="" call :printhelp  & exit /b 1
setlocal

if "%~2"=="" (set Icon=Information) else (set Icon=%2)
powershell -Command "[void] [System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); $objNotifyIcon=New-Object System.Windows.Forms.NotifyIcon; $objNotifyIcon.BalloonTipText='%~1'; $objNotifyIcon.Icon=[system.drawing.systemicons]::%Icon%; $objNotifyIcon.BalloonTipTitle='%~3'; $objNotifyIcon.BalloonTipIcon='None'; $objNotifyIcon.Visible=$True; $objNotifyIcon.ShowBalloonTip(5000);"

endlocal
goto :eof

:printhelp
echo USAGE: %~n0 Text [Icon [Title]]
echo Icon can be: Application, Asterisk, Error, Exclamation, Hand, Information, Question, Shield, Warning or WinLogo
exit /b

If there's no second parameter it defaults to Information.

I suppose you can use mshta and vbscript or javascript, but I had no success until now.

2
  • Very nice! Is it possible to extend the notification at all, such as adding an action to perform when the notification is clicked? (or even better, adding an interactable button to the notification?)
    – ETL
    Commented Aug 28, 2023 at 2:53
  • This is great, one small question. Is it possible to change the toast title from Windows Powershell? example image Commented Jan 24 at 14:08
8

Here's a jscript.net/bat polyglot script (must be saved as .bat) with hardcoded values (code is almost the same as in the link in my comment) that will create a simple system tray notification (will work on every system with installed .NET. .NET is installed by default on every windows machine since Windows Vista so this is more backward compatible than powershell):

@if (@X)==(@Y) @end /* JScript comment
@echo off

setlocal
del /q /f %~n0.exe >nul 2>&1
for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d  /o:-n "%SystemRoot%\Microsoft.NET\Framework\*jsc.exe"') do (
   set "jsc=%%v"
)

if not exist "%~n0.exe" (
    "%jsc%" /nologo /out:"%~n0.exe" "%~dpsfnx0"
)

if exist "%~n0.exe" ( 
    "%~n0.exe" %* 
)


endlocal & exit /b %errorlevel%

end of jscript comment*/

import System;
import System.Windows;
import System.Windows.Forms;
import System.Drawing;
import System.Drawing.SystemIcons;

var notification;
try {
    notification = new System.Windows.Forms.NotifyIcon();


} catch (err){

}

notification.Icon = System.Drawing.SystemIcons.Hand; // Could be Application,Asterisk,Error,Exclamation,Hand,Information,Question,Shield,Warning,WinLogo
notification.BalloonTipText = "Message!!";
notification.Visible = true;
// optional - BalloonTipIcon = System.Windows.Forms.ToolTipIcon.Info;
// optional - BalloonTipTitle = "My Title",




// Display for 2 seconds.
notification.ShowBalloonTip(2000);
System.Threading.Thread.Sleep(2100);

notification.Dispose();

It will compile a small executable that will be called. If you don't want to create a temporary executables you can check the msbuild technique used here (though this will work only on windows 10). I can create also a parametrized version of this.

EDIT: here's a tool with a command line options:

call systemTrayNotification.bat   -tooltip warning -time 3000 -title "Woow" -text "Boom" -icon question
4

Methood 1: LC.exe CMDLet
This little CMDlet (A Portable .exe file you can call from within a batch file) can compare two files and export lines unique to the first file in a txt doc named old.txt and lines unique to the second in a txt named new.txt the context is lc File1.txt File2.txt
Here is the file: 1drv.ms/u/s!AlRLV33Rdz2CgrgeqSerXbeXyVhRYQ
Methood 2: Powershell Script in Batch File

:notification
::Synax is call :notification [Title] [Body] [Icon]
echo notification: %1 %2 %3

echo [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") > notfy.ps1
echo $objNotifyIcon = New-Object System.Windows.Forms.NotifyIcon  >> notfy.ps1
echo $objNotifyIcon.Icon = "ico%3.ico" >> notfy.ps1
echo $objNotifyIcon.BalloonTipIcon = "None"  >> notfy.ps1
echo $objNotifyIcon.BalloonTipText = %2  >> notfy.ps1
echo $objNotifyIcon.BalloonTipTitle = %1 >> notfy.ps1
echo $objNotifyIcon.Visible = $True  >> notfy.ps1
echo $objNotifyIcon.ShowBalloonTip(10000) >> notfy.ps1

Powershell "& ""%cd%\notfy.ps1"""
del /f /q "notfy.ps1"
exit /b

Simply add that to your batch file and call it with call :notification "Title" "Body" "Icon File"

Note: This methood requires The Powershell Execution Policy to be set to Unrestricted (not a default setting). You can test this with this:

powershell Get-ExecutionPolicy >pgep.t
Find "Unrestricted" "pgep.t" >nul
if %errorlevel%==0 goto setuphere
del /f /q pgep.t
:setuphere
::Start a Batch File as admin with the command [Powershell Set-ExecutionPolicy $POLICY -Force] in it. (remove the brackets).
4
  • The program's name is notification.exe but your explanation makes no sense to me. I thought you would add a powershell code so readers can make themselves an idea of how you did it and use or extend it.
    – cdlvcdlv
    Commented Jun 21, 2018 at 14:09
  • I havn't tested this but in theory you could replace the Powershell "& ""%cd%\notfy.ps1""" with powershell.exe -executionpolicy bypass -windowstyle hidden -noninteractive -nologo -file "notify.ps1" However, I think that would require the batch file to be admin.
    – Mark Deven
    Commented Jun 21, 2018 at 16:36
  • You see, that's why I don't like using .ps1 files: first, I'll avoid if possible creating any file. Even less with a fixed name, just in case concurrent instances of batch files are using such a technique -- the goal is a general way (i.e. reusable) to let .bat files show balloon popups and if you create/delete files on the fly you'll expect collisions. ...
    – cdlvcdlv
    Commented Jun 25, 2018 at 9:10
  • ... Plus, if the execution policy is the default (the most likely) it will fail or to change the policy and restore it you need admin rights... and just to show a damned balloon. You end feeling like using a sledgehammer to crack a nut. But thank you (+1) for showing how to make it. I think I'll give a try to this avoiding exec policy with one-liners.
    – cdlvcdlv
    Commented Jun 25, 2018 at 9:10
3

There are a lot of hacky methods to achieve the stated goal. One of them was described by npocmaka. Pay close attention that same method can be used with PowerShell too (example how to combine PowerShell scripts can be found here). Another monster that can engender the mind (this one can compatible with WinXP) can be CMD plus WSH.

0</* :
@echo off
  setlocal
    echo Some batch code here...
    cscript /nologo /e:jscript "%~f0" "Warning!"
    pause
    echo Another batch code...
    cscript /nologo /e:jscript "%~f0" "All done!"
    pause
  endlocal
exit /b*/0;
(function(msg) {
   with (new ActiveXObject('Internet.HHCtrl')) {
      TextPopup(msg, 'Lucida Console, 23', 1, 1, 1, 1);
      WScript.Sleep(1500);
   }
}(
   WScript.Arguments.length !== 1 ?
   WScript.Quit(1) : WScript.Arguments.Unnamed(0)
));

Note that I do not strongly recommended to use hybrids and other hymera. I'm sure the best way to do what you ask about is write special command line tool.

0
1

Simplified from @cdlvcdlv answer, this runs faster if called from a different file.

notify.bat

@echo off
if "%~3"=="" (set Icon=Information) else (set Icon=%3)
cmd /c powershell -WindowStyle Hidden -Command "[void] [System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); $global:balloon = New-Object System.Windows.Forms.NotifyIcon; $balloon.Icon = [system.drawing.systemicons]::%Icon%; $balloon.Visible = $true; $balloon.ShowBalloonTip(5000, '%1', '%2', 'None'); Timeout /NoBreak 5; $balloon.Dispose(); Exit;"
EXIT 0

This can now be called like this:

// Synthax
notify.bat "Title" "Message" "Type"

// Example 1
notify.bat "MyScript - Warning" "Some files are missing" "Warning"

// Example 2
cmd /c notify.bat "MyScript - Success" "Client is running"

If you noticed, all of these:

$balloon.BalloonTipIcon = 'Icon'
$balloon.BalloonTipText = 'Text'
$balloon.BalloonTipTitle = 'Title'

Were simplified to this:

$balloon.ShowBalloonTip(5000, 'Title', 'Text', 'Icon');

Not the answer you're looking for? Browse other questions tagged or ask your own question.