Tuesday, April 21, 2015

DOS error levels

Today I was in the mood to quit my job and become a potato farmer...
 
I tried to work with batch scripts to create an "easy" install script (which is currently growing to a quite sophisticated all-in-one solution). (The post is related to http://itados.blogspot.co.at/2014/12/windows-start-script-for-applications.html )
 
The "easy" concept was to...
* delete and re-create a hard coded folder on the c: - drive (making always a clean "check-out" from the source path)
* connect to a UNC path on a server
* xcopy the files locally
 
My first problem was that connecting to the UNC path which is shared for the group "everyone", but which was not reachable by "everyone", because "everyone" seems to be "everyone" inside of the domain scope and not "everyone" as I would expect it. There are some related problems which can be solved by tweaking the windows security policy which can be found in secpol.msc at "Local Policies > Security Settings > Network Access: *", but I think it is a bad idea to change these (the infrastructure experts will definitely have an eye on these settings (= problems for me) and this means to open a new gate into your system and be open for new threats).
 
So I created a user on the system locally and gave him a secure password what leads me to my second problem, which was that if the "net use" command has credentials inserted and the credentials contain special characters then an error about "password or user incorrect" shows up. Attention: I copy/paste-d the command to my normal cmd-box and it worked properly (and created completely different output in windows event log on the server)! I used the same statement in a batch script... it failed. Now some magic: using "abc" as a password worked in both cases properly... (I really hate that...).
 
The net use command stores the credentials of this local account. In case these have changed or just to work proper I deleted them before setting clean new ones... in case it is the first time to connect, nothing can be deleted so we get an error message saying "hey, this can't be executed, nothing found to delete". So I printed the user (as an information only) that "something happened here, but don't worry everything is still fine"... using "if %errorlevel% NEQ 0" ( ... ATTENTION: there is an undocumented feature on /DELETE ... the /YES forces the delete of the connections even if the connection is currently open... it do is undocumented in /? of net use!
 
Next problem: the parenthesis of the if has to be on the same line as the if statement in the batch script to identify the following statement as a block which should be executed when the if condition is true. In case this would have made serious troubles a nice goto would make you jump out of the critical section probably to the end of the script (what equals to a "return" in common programming languages).
 
Back to step 1: To delete the folder is an easy call using "rd" or "rmdir" (i believe these are synonyms). Here I have troubles getting an error code and in fact the BUILT-IN COMMANDS have troubles setting the error code right!
http://stackoverflow.com/questions/6500314/do-ms-dos-built-in-commands-return-an-error-exit-code
the answer of stackoverflow user "dbenham" brought me to solution that works: "rd testdir || rem" ... again... mind blowing! you have to pipe the result to a comment to get the errorcode of the original statement set up. W00t?
 
And now to my favorite part... setting and resetting the error level. To copy the application binaries I use "taskkill" to close any running applications (=processes). Resetting the errorcode (because some users really close their application before installing a new version leading to the error "process not found" = code 128) is a common problem and could be accomplished in many different ways like
* verify >nul
* ver >nul
* cmd /c "exit /b 0"
* (call )
* (call)
 
attention: Don't use "set %errorlevel% = 0", because this creates a local variable with the same name, what disables further error handling possibilities, because then the local variable and not the system variable will be evaluated for further checks ( http://stackoverflow.com/questions/1113727/what-is-the-easiest-way-to-reset-errorlevel-to-zero )
 
but none of them work inside of an if statement...
 
taskkill /f /im doesnotexist.exe
echo after taskkill %errorlevel%
if 0==0 (
 cmd /c "exit /b 0"
 echo cmd %errorlevel%
)
echo after if %errorlevel%
 
output:
 
FEHLER: Der Prozess "doesnotexist.exe" wurde nicht gefunden. (german for process not found)
after taskkill 128
cmd 128
after if 0
 
 
meaning ... if you have something like:
kill my process => errorlevel become 128
if folder exists (
  delete folder => errorlevel should become 0
  check error code (HERE THE ERRORCODE STAYS 128 AND THE ERRORCODE OF DELETE FOLDER WILL BE IGNORED)
)
 
the solution here is to reset the errorcode after killing the process and to make an "if folder exists" after deleting the folder and to get the information here... another valid solution is to jump to code parts outside of any ifs using goto.
 
The effect is explained in http://blogs.msdn.com/b/oldnewthing/archive/2006/08/23/714650.aspx who says that an if is executed as a single command what means that something like a preprocessor pushes the values as textreplace into the placeholders. So it does not count what happens inside the if because the text-replace was already done. 

Following example from the link shows it quite good:

C:\>set VAR=before
C:\>set VAR=after & echo %VAR%
before

There seems to be a solution for it with "SETLOCAL ENABLEDELAYEDEXPANSION" (please check the link for further information).

In the end i am not even sure if i am using the errorlevel right, because i am not sure if the %-signs are necessary (both ways seem valid)
 
cmd /c "exit /b 123"
if errorlevel == 123 (
 echo errorlevel 123 %errorlevel%
) else (
 echo else %errorlevel%
)
 
cmd /c "exit /b 123"
if %errorlevel% == 123 (
 echo errorlevel 123 %errorlevel%
) else (
 echo else %errorlevel%
)
 
for this example the next trap came across ... closing parenthesis in echo also close blocks!
 
cmd /c "exit /b 123"
if %errorlevel% == 123 (
 echo errorlevel 123 (%errorlevel%)
) else (
 echo else (%errorlevel%)
)
 
output:
errorlevel 123 (123
else (123)
 
 
now I know why everyone loves powershell (and also for small scripts) and also why install projects or ClickOnce-Deployment have their right to be... (nevertheless why it is cool to build websites without installing stuff)

kind regards,
Daniel

Null propagation in C# 6.0

Today I read ( http://www.volatileread.com/Wiki?id=2104 ) about the null propagation Operator ?. ... a C# 6.0 feature what enables you the possibility to make things like

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
string invalidResult = string.Empty;
 
if(this.CurrentUser != null)
{
    if(this.CurrentUser.Name != null)
    {
        return this.CurrentUser.Name;
    }
    else
    {
        return invalidResult;
    }
}
else
{
    return invalidResult;
}

in only one simple statement:


1
return this.CurrentUser?.Name ?? string.Empty;

... semantically "?." means "if not null evaluate property, else return null". 


cool stuff...

kind regards,
Daniel

Tuesday, April 7, 2015

performant vs clean code

Today I read a really good article about development performance vs simple-clean development.
 
http://arne-mertz.de/2015/03/simple-and-clean-code-vs-performance/
 
The article (and its comments) highlights some very good points I definitely agree with.
 
In short:
 
simple is better than performant:
  • before: performance (how fast to do something) != efficiency (how long to do it)
    • if you run 2 miles you can be more performant than a guy walking a single mile,
    • but if you reach the same goal by walking only one mile you are much more effective
  • try to write efficient code before you write performant code because your intention will still be understandable and the code keeps being readable
  • check whether the code to optimize is needed to be optimized (or called rarely, so it doesn't contribute to total run-time)
  • optimization needs tooling to find painful parts in the code
  • optimize the data store first
  • check for other bottlenecks (e.g.: hardware, user input,...) 
  • check for best practices, libraries and patterns
  • use caching instead of recalculating the hard stuff

kind regards,
Daniel