In c# / VB.NET I would not say its evil.
For example, when debugging windows services, I put the following in Main so that when in Debug mode, I can run the service as an application.
<MTAThread()> _
<System.Diagnostics.DebuggerNonUserCode()> _
Shared Sub Main()
#If DEBUG Then
'Starts this up as an application.'
Dim _service As New DispatchService()
_service.ServiceStartupMethod(Nothing)
System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite)
#Else
'Runs as a service. '
Dim ServicesToRun() As System.ServiceProcess.ServiceBase
ServicesToRun = New System.ServiceProcess.ServiceBase() {New DispatchService}
System.ServiceProcess.ServiceBase.Run(ServicesToRun)
#End If
End Sub
This is configuring the behavior of the application, and is certainly not evil. At the very least, its not as evil as trying to debug a service startup routine.
Please correct me if I read your OP wrong, but it seems that you are complaining about others using a preprocessor when a simple boolean would suffice. If thats the case, dont damn the preprocessors, damn those using them in such fashion.
EDIT:
Re: first comment. I dont get how that example ties in here. The problem is that the preprocessor is being mis-used, not that it is evil.
I'll give you another example. We have an application that does version checking between client and server on startup. In development, we often have different versions and dont want to do a version check. Is this evil?
I guess what I am trying to say is that the preprocessor is not evil, even when changing program behavior. The problem is that someone is misusing it. What is wrong with saying that? Why are you trying to dismiss a language feature?
Much later EDIT: FWIW: i haven't used preprocessor directives for this in a few years. I do use Environment.UserInteractive with a specific arg set ("-c" = Console), and a neat trick I picked up from somewhere here on SO to not block the application but still wait for user input.
Shared Sub Main(args as string[])
If (Environment.UserInteractive = True) Then
'Starts this up as an application.
If (args.Length > 0 AndAlso args[0].Equals("-c")) Then 'usually a "DeriveCommand" function that returns an enum or something similar
Dim isRunning As Boolean = true
Dim _service As New DispatchService()
_service.ServiceStartupMethod(Nothing)
Console.WriteLine("Press ESC to stop running")
While (IsRunning)
While (Not (Console.KeyAvailable AndAlso (Console.ReadKey(true).Key.Equals(ConsoleKey.Escape) OrElse Console.ReadKey(true).Key.Equals(ConsoleKey.R))))
Thread.Sleep(1)
End While
Console.WriteLine()
Console.WriteLine("Press ESC to continue running or any other key to continue shutdown.")
Dim key = Console.ReadKey();
if (key.Key.Equals(ConsoleKey.Escape))
{
Console.WriteLine("Press ESC to load shutdown menu.")
Continue
}
isRunning = false
End While
_service.ServiceStopMethod()
Else
Throw New ArgumentException("Dont be a double clicker, this needs a console for reporting info and errors")
End If
Else
'Runs as a service. '
Dim ServicesToRun() As System.ServiceProcess.ServiceBase
ServicesToRun = New System.ServiceProcess.ServiceBase() {New DispatchService}
System.ServiceProcess.ServiceBase.Run(ServicesToRun)
End If
End Sub