why does cmd.exe in CreateProcess behaves different from DOS-prompt? - windows

I use Altar's GetDOSOutput() (variant 1) to this question to call dos-commands by a simple delphi program. However, existing DOS program's such as DiskPart cannot be found when called by by CreateProcess whereas they present no problem when called from the DOS-prompt (Windows server 2003 X64). What could be the cause of this?
commandline: `ListVolumes.bat'
ListVolumes.bat:
path
C:\WINDOWS\SYSTEM32\DiskPart.exe /s ListVolumes.scr
dir C:\WINDOWS\SYSTEM32\DiskPart.exe
output through the program call:
I:\PartScan>path
PATH=C:\WINDOWS;C:\WINDOWS\System32;C:\WINDOWS\System32\wbem;C:\Program Files (x86)\Borland\Delphi7\Bin; C:\Program Files (x86)\Borland\Delphi7\Projects\Bpl\;
I:\PartScan>C:\WINDOWS\SYSTEM32\DiskPart.exe /s ListVolumes.scr
'C:\WINDOWS\SYSTEM32\DiskPart.exe' is not recognized as an internal or external command,
operable program or batch file.
I:\PartScan>dir C:\WINDOWS\SYSTEM32\DiskPart.exe
Volume in drive C is system
Volume Serial Number is 351F-0221
Directory of C:\WINDOWS\SYSTEM32
File Not Found
output when called from the DOS prompt (note the final dir command):
PATH=C:\WINDOWS;C:\WINDOWS\System32;C:\WINDOWS\System32\wbem;C:\Program Files (x86)\Borland\Delphi7\Bin; C:\Program Files (x86)\Borl
and\Delphi7\Projects\Bpl\;
I:\PartScan>C:\WINDOWS\SYSTEM32\DiskPart.exe /s ListVolumes.scr
Microsoft DiskPart version 5.2.3790.3959
Copyright (C) 1999-2001 Microsoft Corporation.
On computer: ISOETES
Volume ### Ltr Label Fs Type Size Status Info
---------- --- ----------- ----- ---------- ------- --------- --------
Volume 0 F DVD-ROM 0 B Healthy
...
Volume 11 G DVD-ROM 0 B Healthy
I:\PartScan>dir C:\WINDOWS\SYSTEM32\DiskPart.exe
Volume in drive C is system
Volume Serial Number is 351F-0221
Directory of C:\WINDOWS\SYSTEM32
17-Feb-2007 08:17 263,680 diskpart.exe
1 File(s) 263,680 bytes
0 Dir(s) 33,111,334,912 bytes free

You've not shown code so we cannot diagnose this with 100% certainty. However, the likely cause is that your process is a 32 bit process running under the WOW64 emulator. When you create a cmd process under the emulator you get a 32 bit cmd process, also running under the emulator. You are comparing that with a 64 bit process. Do note that under the emulator, system32 is redirected to SysWOW64 by the file system redirector.
The way you deal with this is to create a 64 bit process. That's quite tricky to do for cmd when creating from inside the emulator. The easiest way to do so is to call CreateProcess from a 64 bit process.
Since you use Delphi 7 you may need to use a modern compiler to make a small 64 bit executable that will do the work for you. Call the small executable from your Delphi 7 program.
An alternative that may fit your needs is to use the sysnative alias to reach the 64 bit system directory from inside the emulator. That is described in the file system redirector documentation.

Related

Why is the value of the ProgramFiles variable different in cmd.exe and PowerShell?

I am using Windows 7 Professional SP1 on a 64 bit PC. If I open up a PowerShell console and look at $env:ProgramFiles, its value is "C:\Program Files (x86)". However, if I open up cmd.exe and look at %ProgramFiles%, its value is "C:\Program Files".
Can someone explain why they're different? I would have expected them to either be the same, or for cmd.exe to be the one that thought it was living in a 32 bit environment.
You have opened 32 bit Power Shell: Windows Power Shell (x86)
The value of the variable depends on the bitness of the calling process.
If you open 32 bit CMD.EXE, (%windir%\SysWoW64\cmd.exe) the %ProgramFiles% will also point to the x86 folder.

How to run a batch file in 64 bit mode from a batch file in 32 bit mode

I want my program to run in 32 bit mode if in a 32 bit OS or in 64 bit mode if it's in a 64 bit OS.
That program is created with Bat To Exe Converter v2.1.4, so it's basically a batch file. Normally, when I run a batch file on a 32 bit OS it runs in 32 bit mode and when I run it on a 64 bit OS it runs in 64 bit mode, isn't it?
The problem is, using Bat To Exe Converter v2.1.4, I can choose if the program is 32 or 64 bit. So I have to choose 32 or else, I don't think it will run on a 32 bit OS.
I tried using .vbs files to re-launch the program using .Run and .ShellExecute, but the result was the architecture being the same as the one set in the converter.
I also tried cmd /c and %WINDIR%\System32\cmd.exe /c and also %WINDIR%\SysWOW64\cmd.exe /c, but I couldn't find a way to do it.
I use Windows 8.0 x64 and my VM is Windows 8.1 x64.
You could use following at top of your batch file:
#echo off
set "SystemPath=%SystemRoot%\System32"
if not "%ProgramFiles(x86)%" == "" set "SystemPath=%SystemRoot%\Sysnative"
Next you need to call every console application in System32 directory of Windows with %SystemPath% in your batch file, for example %SystemPath%\findstr.exe. Of course you could also start cmd with %SystemPath%\cmd.exe to run always 64-bit command line interpreter from within the batch file.
How it works?
The environment variable SystemPath is set first to System32 directory of Windows.
The batch file packed into a 32-bit executable runs now all console applications indeed from System32 directory on 32-bit Windows, but from %SystemRoot%\SysWOW64 directory on 64-bit Windows.
Therefore the batch file checks next if environment variable ProgramFiles(x86) exists which is the case only on Windows x64. Therefore the condition on third line is false on Windows x86 and SystemPath is not changed. But SystemPath is modified to %SystemRoot%\Sysnative on 64-bit Windows to call the applications in %SystemRoot%\System32 from 32-bit executable respectively cmd.exe without redirection to %SystemRoot%\SysWOW64.
For more details see the Microsoft documentation page File System Redirector.
But better would be to do that all inside the 32-bit executable which extracts the batch file to %TEMP% and run it either with
%SystemRoot%\System32\cmd.exe /C "%TEMP%\ExtractedBatch.bat"
for 32-bit Windows where environment variable ProgramFiles(x86) does not exist or with
%SystemRoot%\Sysnative\cmd.exe /C "%TEMP%\ExtractedBatch.bat"
on 64-bit Windows.
Here is one more code which can be used at top of a batch file to run always 64-bit console applications independent on being started on Windows x64 with 32-bit or with 64-bit cmd.exe.
#echo off
set "SystemPath=%SystemRoot%\System32"
if not "%ProgramFiles(x86)%" == "" if exist %SystemRoot%\Sysnative\cmd.exe set "SystemPath=%SystemRoot%\Sysnative"
On Windows x64 it is additionally checked if there are files in %SystemRoot%\Sysnative. In this case the batch file is executed with 32-bit cmd.exe and only in this case %SystemRoot%\Sysnative needs to be used at all. Otherwise %SystemRoot%\System32 can be used also on Windows x64 as when the batch file is started with 64-bit cmd.exe, this is the directory containing the 64-bit console applications.
Note: %SystemRoot%\Sysnative is not a directory. It is not possible to cd to %SystemRoot%\Sysnative or use if exist %SystemRoot%\Sysnative.
c:\windows\sysnative
Gives access to System32 for 32 bit programs.
32 Bit
C:\Windows\System32 accesses syswow64
c:\windows\sysnative accesses System32
64 Bit just does what's told, access the folders directly - eg C:\windows\system32 accesses System32 and C:\windows\syswow64 accesses Syswow64.
The point is you should only be writing a 32 bit program. 64 bit programs are mostly 32 bit internally (only memory addresses are 64 bit everything else remains 32 bits). 64 bits is for server apps. Use 32 bits for general programs.
EDIT
32 Bit programs are 32 bit with a 64 bit addressing mode of which 32 bits (base address is always 0 in Windows) are unused so only 32 bits (offset) is required for memory addresses.
64 Bit programs are 32 bit with a 64 bit offset memory address (I don't know the size of the base address in 64 bit mode as they are always 0 and have been irrelevent for decades). A 64 bit program can become a full 64 bit program, just by using 64 bit instructions when it chooses, generally for scientific or video processing tasks. But 64 bit everything chews too much memory and Windows' and other's libraries expect 32 bit values.
The general principal is that you need do nothing to achieve your tasks. People get into trouble when they start thinking about 32bit/64bit. If you ignore the bitness, Microsoft has put all the work in to make it just work.
If you type iexpress in Start - Run (Winkey + R) dialog you'll be able to make your own bat2exe.
TL;DR version of Mofi's detailed answer:
#echo off
set "SystemPath=%SystemRoot%\System32"
if not "%ProgramFiles(x86)%"=="" (
if exist %SystemRoot%\Sysnative\* set "SystemPath=%SystemRoot%\Sysnative"
)
rem Example: Run a command from the system folder:
%SystemPath%\dism
This sets %SystemPath% to whatever path which points to the native / "real" C:\Windows\System32 folder.
Rephrased explanation:
On a 32-bit OS, it will point to C:\Windows\System32, which is the only folder that exists.
On a 64-bit OS when running as a 32-bit process, it will point to the 64-bit C:\Windows\System32 via the path C:\Windows\Sysnative.
On a 64-bit OS when running as a 64-bit process, it will point to the 64-bit C:\Windows\System32 via the path C:\Windows\System32.
Reasoning:
On a 32-bit OS, there is only one 32-bit OS and no file system redirector.
On a 64-bit OS, when running as a 32-bit process, the path C:\Windows\System32 redirects to C:\Windows\SysWOW64, whereas C:\Windows\Sysnative redirects to C:\Windows\System32.
On a 64-bit OS, when running as a 64-bit process, the path C:\Windows\Sysnative does not exist, and C:\Windows\System32 points to C:\Windows\System32.
See official documentation.

Windows 7 registry %ProgramFiles% issue

I have some kind of windows appilcation, running on Windows 7 32-bit. I'm trying to register particular file extension open command with my application using windows registry. Such file extension is my own and also registered by me. The application installed in particular subdirectory within Program Files. I want my installer to register application properly for both 32 and 64-bit platfroms, since actual Program Files directory names can be different on x86 and x64 platfroms, while I need to specify path to my application, I'm using registry redirection %ProgramFiles%. Here I reproduce what records I make to registry:
// file extension
HKEY_CURRENT_USER
Software
Classes
.myext
Default REG_SZ myapp.myext
// application
HKEY_CURRENT_USER
Software
Classes
.myapp.myext
Shell
Open
Command
Default REG_SZ "%ProgramFiles%\path\to\my\app\myapp.exe" -u -i "%1"
Actual path to program files dir in my test machine is C:\Program Files
With such registration I receive an error:
Windows cannot access the specified device path, or file. You may not
have the appropriate permissions to access the item.
If I replace %ProgramFiles% with actual C:\Program Files everything works fine. Also when I'm using same path: "%ProgramFiles%\path\to\my\app\myapp.exe" to run application from console everything works fine too. What can be reason of such issue.
%ProgramFiles% evaluates to the 32 bit program files dir in a 32 bit process and the 64 bit program files dir in a 64 bit process. However, the location of your executable does not depend on the bitness of the shell, which is the thing that reads those registry settings. You want to write the following value to the registry:
"C:\Program Files (x86)\path\to\my\app\myapp.exe" -u -i "%1"
where I assume that is the path to the 32 bit program files dir. You should not be writing an environment variable into this registry key. Your install program will already know the full path to the executable, and you should simply write that.
One issue that I think may be confusing you is that you may be under the believe that the program files directories are subject to file system redirection. They are not.
As an aside, if you ever need to write an environment variable into the registry, and want it to be expanded upon reading, use REG_EXPAND_SZ rather than REG_SZ.

How to test for floppy in drive?

I am using Windows XP Pro with Service Pack 3.
I have a .bat file that tests if various drives are available, and if they are then the bat file writes a little script that another program, an exe file, uses.
The problem is when the bat file issues the command to test if a disk is in the floppy drive, Windows generates an error and a message box. Windows is doing its own testing and pops up a message box, which halts my bat file. I don't want Windows to do the testing.
Here is a short bat file I wrote to demonstrate the problem:
#ECHO OFF
CLS
REM TEST FOR FLASH DRIVE
IF EXIST G:\NUL (
ECHO Flash Drive found
)
REM TEST FOR FLOPPY DISK IN DRIVE
IF EXIST A:\NUL (
ECHO Floppy in Drive
)
How do I test for a floppy without Windows getting in the way?
you can remove the null operator, and have it just try the A: drive. This works for windows 7 so it might work for windows xp. or you can use a power shell command that list all active drives which is
get-psdrive.
Power shell has the ability to run commands like cmd with some more built in features.

Is it possible to properly execute qwinsta from a Cygwin ssh session?

I have Cygwin running on a Windows 7 machine and have the Cygwin ssh server running on it. On Linux I have a shell script where I want to do
ssh myuser#mymachine "qwinsta | grep Active"
to see who is logged in. This worked fine for a Windows Server 2008 R2 machine, but seems to have problems on Windows 7.
If I try this on the Windows 7 machine, I get:
bash: qwinsta: command not found
Now, here is where the weirdness begins...
If I login to the Windows 7 machine normally and look in C:\Windows\System32 with Windows Explorer, I see qwinsta.exe. If I open a CMD session and do a dir in C:\Windows\System32, I see qwinsta.exe. If I open a Cygwin shell and do a ls qwinsta.exe in /cygdrive/c/Windows/System32, I get:
ls: cannot access qwinsta.exe: No such file or directory
If I do a cmd /c dir C:\\\\Windows\\\\System32\\\\qwinsta.exe from the Cygwin shell, I get a "File Not Found"
If I copy qwinsta.exe into my Cygwin home directory, then it is visible in my home directory with ls. If I try to run this local copy of qwinsta from the Cygwin shell, it runs, but it also outputs a line:
{Message(): LoadString failed, Error 15105, (0x00003B01)}
What's up with qwinsta on Windows 7?
The problem is that qwinsta.exe is not actually located in C:\Windows\System32. It is actually found in
C:\Windows\winsxs\amd64_microsoft-windows-t..commandlinetoolsmqq_31bf3856ad364e35_6.XX.XXX.XXXX_none_XXXXXXXX\qwinsta.exe
Using the above path (or a softlink to the same) will run qwinsta.exe as it exists on any machine, and will not require you to copy the executable to your home directory.
The error message {Message(): LoadString failed, Error 15105, (0x00003B01)} is about the Multilinugal User Interface (localization) system not being able to find error message localization information for the program being run (see System Error Codes). In this case, it appears that the cygwin shell does not provide qwinsta.exe with the information it needs to find qwinsta.exe.mui in your language's locale folder (usually C:\Windows\System32\en-US or whatever your locale happens to be). Looking into this folder is somewhat misleading, as explorer will show the file in this directory, but when you run ls /cygdrive/c/Windows/System32/en-US, there is no qwinsta.exe.mui file. I suspect this has something to do with the new linking structure in NTFS (see mklink command), but I haven't figured out how to fix this part of the problem yet.
Solved:
First, go to C:\Windows\winsxs\amd64_microsoft-windows-t..commandlinetoolsmqq_31bf3856ad364e35_6.1.7600.16385_none_851e6308c5b62529
(Copy and pasting that location works just as well as manually finding it.)
You should find three files: Msg.exe , Quser.exe, and qwinsta.exe .
Copy these files to your C:\Windows\system32 folder
Next, go to C:\Windows\winsxs\amd64_microsoft-windows-t..etoolsmqq.resources_31bf3856ad364e35_6.1.7600.16385_en-us_7bef78d9f4a6a8ac
You should find three similarly named files, except these will end with .mui.
Copy all three of these files to your C:\Windows\system32\en-US folder.
Now try running the msg program. It should work without issue.
Windows 10
Following on from Erutan2099's answer, for Windows 10 it's a little trickier, since the files are compressed (binary delta compression, file signature 44 43 53 01). Trying to use them as is throws an Unsupported 16-Bit Application error:
The program or feature "\??\C:\Windows\System32\msg.exe" cannot start or run due to incompatibility with 64-bit versions of Windows. Please contact the software vendor to ask if a 64-bit Windows compatible version is available.
A specific tool has been made to decompress such files: SXSEXP (this post pointed me in the right direction)
Usage:
> sxsexp64.exe msg.exe expand\msg.exe
Processing target path msg.exe
msg.exe => expand\msg.exe
File size 12602 bytes
DCS_HEADER found.
NumberOfBlocks 1
UncompressedFileSize 26112
DCS_BLOCK #1
Block->CompressedBlockSize 0000312A
Block->DecompressedBlockSize 00006600
Operation Successful
> sxsexp64.exe msg.exe.mui expand\msg.exe.mui
Processing target path msg.exe.mui
msg.exe.mui => expand\msg.exe.mui
File size 2150 bytes
DCS_HEADER found.
NumberOfBlocks 1
UncompressedFileSize 7680
DCS_BLOCK #1
Block->CompressedBlockSize 00000856
Block->DecompressedBlockSize 00001E00
Operation Successful
These decompressed files can now be copied to C:\Windows\System32 and C:\Windows\System32\en-US respectively.
Example:
> msg * Hello, World!

Resources