Why setlocal interferes with chdir in windows batch files?

The HELP documentation for SETLOCAL (help setlocal or setlocal /?) actually explains the situation pretty well. The only thing that is not obvious is that "localization of environment changes" doesn't just include environment variables, but also includes the current directory, and the delayed expansion and extensions states. There may be more, but I can't think of it at the moment.

The thing that is tripping you up is actually explained fairly well: "When the end of a batch script is reached, an implied ENDLOCAL is executed for any outstanding SETLOCAL commands issued by that batch script." Not stated is that the same is true for called subroutines.

When your batch script ends, the implicit ENDLOCAL "erases" the effect of your CHDIR. Your explicit ENDLOCAL in your second code gets you back to the root environment, so your CHDIR is then preserved.


Update

The current directory is not an environment variable, even though you can normally get the current value using %CD%. You can prove it by tring SET CD - it will probably give you "Environment variable CD not defined". If you explicitly define your own true CD variable using set "CD=some value", then %CD% will return the value you assigned, and not the current directory.

The original SETLOCAL command did not control delayed expansion or extensions back in the old COMMAND.COM days. The Enable/Disable DelayedExpansion and Enable/Disable Extensions options were added when CMD.EXE was introduced. It's just how MS decided to implement the feature. It didn't have to be that way. In many ways it is unfortunate that you cannot control those states without SETLOCAL/ENDLOCAL. I often wish I could enable or disable delayed expansion without localizing the environment.