64bit PowerShell calls the 32bit DLL - powershell

On a Windows 10 computer, I am calling QuickBooks from a 4th Dimension (4D) database using a PowerShell script. The script calls the QBXMLRP2.dll, a 32bit COM object, to talk to QuickBooks 2019. As I understand, if you call a 32bit dll using a 64bit version of PowerShell, it will fail and vice versa. However, I'm getting different results depending on if I use a 32bit or 64bit version of 4D. This makes no sense to me. Here are my test results:
OS 4D PowerShell DLL Result
64 32 32 32 OK
64 32 64 32 OK //According to my research this should not work!
64 64 32 32 OK
64 64 64 32 X //According to my research this is the expected behavior
Any thoughts on why the 64bit version of PowerShell/32bit dll works with the 32 bit version of 4D? What I really want is for it to work with the 64bit versions of 4D and PowerShell.
In response to questions... Here is what I am doing. 4D first creates a .ps1 script file and saves it to disk, then launches PowerShell in an external process. For example, this would launch 64bit PowerShell (Windows 10) and PowerShell would execute the previously saved script:
"C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe -file \""+$ScriptPath+"\""
The PowerShell script looks like this:
[String]$requestXML = '<?xml version="1.0" ?>
<?qbxml version="2.0"?><QBXML>
<QBXMLMsgsRq onError="stopOnError">
<CompanyQueryRq requestID="1">
</CompanyQueryRq>
</QBXMLMsgsRq>
</QBXML>'
$myQBXMLRP = New-Object -com QBXMLRP2.RequestProcessor
$myQBXMLRP.OpenConnection2("qb4D","CCFolioPro",1)
$ticket = $myQBXMLRP.BeginSession("C:\Company Files\Cadinha & Co.,LLC.QBW",$myQBXMLRP.qbFileOpenDoNotCare)
$myQBXMLRP.ProcessRequest($ticket, $requestXML) > $env:_4D_OPTION_OUTPUT_STREAM
$myQBXMLRP.EndSession($ticket)
$myQBXMLRP.CloseConnection()
"Stop" > $env:_4D_OPTION_STOP_TOKEN
As I understand, 4D is out of the picture once PowerShell is launched. 4D does not interact with the dll. It's all PowerShell/dll (COM)/QuickBooks.

With the help off this forum and a bit more research, I understand now that in a 64bit windows operating system, if the OS detects that a 32bit application is making a call to the System32 directory, it will automatically redirect the call to the SysWow64 directory. Thus in my scenario 32bit PowerShell will always run when called from 32bit 4D. Note that this redirect is only in play when the calling app is 32bit on a 64bit machine. So if a 64bit application (4D) cals the 64bit PowerShell in the System32 directory the redirect will not occur. In this case if a 32bit dll is invoked by PowerShell, it will fail. PowerShell/dll must match bitness.
If one wants to force the 64bit version in the System32 directory to run from a 32bit application, the special "Sysnative" directory can be used instead of the System32 directory. Note that this is a virtual directory. You will not find it in the file system. Again,if you do force the use of the 64bit Powershell and PowerShell tries to invoke a 32bit dll, it will fail.
The following article is a very good read...
https://learn.microsoft.com/en-us/windows/desktop/winprog64/file-system-redirector
Here is my corrected testing chart...
OS 4D PowerShell DLL Result
64 32 32 32 OK
64 32 64-OS->32 32 OK //OS redirected to 32bit PS. Expected behavior!
64 64 32 32 OK
64 64 64 32 X //No redirect. Expected behaviour

Related

Get Different Results of Running Command on Jenkins Job and on Slave Itself [duplicate]

I'm pretty new to powershell integration in Jenkins and my scripts won't run because (I believe) I need powershell to be executed in 64 bit. Running:
[Environment]::Is64BitProcess
in my execution sequence yields false an then a cmdlet that I use (Get-WindowsFeature) is shown as not recognized as a cmdlet, etc. Any way to execute 64 bit powershell scripts?
Thanks!
Environment
Jenkins on Windows (mine happens to run as a service)
plus Powershell plugin (for running Powershell scripts as "build steps")
Jenkins will typically call upon the correct version of powershell.exe. However, the executor/slave process must be running a 64-bit JRE so that PowerShell can also operate in 64-bit mode.
A simple tester project with the following Powershell script can show the above 32-bit vs 64-bit nature:
$env:Path # Path will have the right Powershell available
[intptr]::size # outputs: 4 = 32-bit, 8 = 64-bit
Stop-WebAppPool FOOBAR # fails when 32-bit, succeeds when 64-bit
Console output example (extra blank lines for clarity):
[Powershell Test] $ powershell.exe -NonInteractive -ExecutionPolicy ByPass "& 'C:\Windows\TEMP\hudson123456789.ps1'"
C:\Windows\system32;C:\Windows;C:\Windows\System32\WindowsPowerShell\v1.0\
4
Stop-WebAppPool : Retrieving the COM class factory for component with CLSID
{688EEEE5-6A7E-422F-B2E1-6AF00DC944A6} failed due to the following error:
80040154 Class not registered (Exception from HRESULT: 0x80040154
(REGDB_E_CLASSNOTREG)).
At C:\Windows\TEMP\hudson123456789.ps1:7 char:1
Solution
tl;dr... Install 64-bit JRE, and configure Jenkins to be 64-bit.
I used chocolatey to install a fairly recent JRE, via "Administrator" PowerShell:
First, install chocolatey:
iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))
Looked for the latest version available https://chocolatey.org/packages?q=java (chocolatey has multiple packages for the same thing, often not kept fully up to date).
Then, install JRE (using the one with the higher JRE number):
choco install -y javaruntime
Or:
choco install -y jre8
Finally, I edited my jenkins.xml configuration so that it would run using the 64-bit JRE instead of the built-in JRE.
Changed:
<executable>%BASE%\jre\bin\java</executable>
To (set the path as appropriate for your instance):
<executable>C:\Program Files\Java\jre1.8.0_66\bin\java</executable>
This one should be an "always fresh" symlink (handled by system updates) that ought to allow your Jenkins instance to survive Restart and Update events:
<executable>C:\ProgramData\Oracle\Java\javapath\java.exe</executable>
Then I restarted Jenkins. Powershell execution woke up to the might of 64-bits. Note: I am using a single Jenkins instance that does double duty as the "server" and "execution slave" at the same time. For fully autonomous slaves, I would suppose doing whatever to get the slave-agents processes in 64-bit mode would result in a similar success.
Full automation? According to the chocolatey "jre8" package documentation, using command line switches, it's even be possible to force fixed destination paths for JRE, and exclude 32-bit and/or 64-bit editions, if fully automated non-interactive steps are needed. https://chocolatey.org/packages/jre8
I am not familiar with Jenkins, but it seems like it's a 32 bit process itself.
Can you specify the location of the PowerShell executable? If so, try to use this path:
C:\Windows\SysNative\WindowsPowerShell\v1.0\powershell.exe
If you can't do that, then you might be able to do it in code in your "execution sequence" with Invoke-Command:
Invoke-Command -ComputerName . -ScriptBlock { [Environment]::Is64BitProcess }
All the code in the scriptblock will be run in a separate 64 bit process and the results will be serialized and returned.
Explanations
Paths
On a 32 bit Windows OS, the system folder is C:\Windows\System32.
On a 64 bit Windows OS, the 64 bit system folder is also C:\Windows\System32. But the system folder for 32 bit processes on a 64 bit Windows installation is in fact C:\Windows\SysWOW64.
For compatibility, a 32 bit process on a 64 bit OS will have any calls to C:\Windows\System32 transparently redirected to C:\Windows\SysWOW64, unbeknownst to the process.
To enable a 32 bit process to reference the real System32 on a 64 bit OS, you can you use C:\Windows\SysNative.
Since PowerShell has a 32 bit and a 64 bit version, and it lives inside the system folders, you need to use the above rules to reference the correct executable depending on whether you're calling it from a 64 or 32 bit process.
The typical scenario (you want to call the version of the same bitness) is easiest (just call powershell.exe or reference it via System32), but it gets hairy if you want to reference the other version.
Invoke-Command Method
The Invoke-Command cmdlet lets you run code, typically on another computer, but you can run it on the same computer as well. This will spawn a completely separate process, and any output gets serialized and sent back to the calling process.
The caveat to this method is that you must enable PowerShell remoting on the machine, via Enable-PSRemoting or Group Policy (shameless self plug).
The default profile (Microsoft.PowerShell) that you connect to on a 64 bit machine will be a 64 bit version of PowerShell, regardless of the OS of the caller.
Incidentally, if you wanted to use Invoke-Command to connect to a 32 bit version, you could do so by explicitly specifying the profile Microsoft.PowerShell32.
OK, so the answer was pretty simple, yet maddening all at once. Basically, the module(s) didn't exist in both of the Powershell paths (x86 and x64), so copying the modules over to the 32-bit powershell environment fixed the issue.
further suggestions:
check path of 32bit-JRE, remove path or uninstall 32bit-JRE --
also swapping path-position with 64bit-JRE might work
check path(s) of PowerShell, remove path of 32bit PowerShell
(..\SysWOW64\..) and add the other one to the path (..\System32\..)
This worked for me!

Why 64bit Process writing to 32bit registry hive

I have a PowerShell script that I wrap with NSIS script to create .exe
PS script writes value to HKEY_LOCAL_MACHINE\SOFTWARE\FolderName
However, I noticed that it is actually writing to HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\FolderName
I am on 64bit OS. The default Hive should be 64bit and I am not setting or redirecting the registry to 32bit hive.
Executeable from NSIS writes to 32bit Hive.
If I run PS script in ISE, it write to 64bit hive, as expceted.
So, I do not know why it is writing under WOW6432Node when wrapped in NSIS. Anything I need to look at?
If you're running the script of 64bit machine, this would work.
${If} ${RunningX64}
${DisableX64FSRedirection}
${EndIf}
# put your code here
${If} ${RunningX64}
${EnableX64FSRedirection}
${EndIf}
As per my knowledge, NSIS uses two Win32 APIs to execute processes ShellExecute and CreateProcess
If your Operating System is 64 bit then both of them can run 64 bit process (x64) from NSIS 32 bit process.
The issue what you have faced is because it might have invoked a 32 bit PS. So double check on that part.

Win32::TieRegistry ws 64 bit on Windows 7 8 10

We have one perl script that write one file based on the Windows registry contents.
The script need to be executed for the 32 and 64 registry hive.
Using this command "C:\stdperl\bin\perl.exe scriptname.pl" we process the 32 bit registry hive,
using this command "C:\stdperl64\bin\perl.exe scriptname.pl" we process the 64 bit registry hive.
We have :
- This is perl, v5.8.7 built for MSWin32-x86-multi-thread
- This is perl, v5.8.9 built for MSWin32-x64-multi-thread
The restricted test case is :
use strict;
use warnings;
use Win32::TieRegistry( ArrayValues=>0, Delimiter=>"/" );
use Config;
my ($chiave, $regKey, $key)="";
$chiave='HKEY_LOCAL_MACHINE/SOFTWARE/ODBC/ODBCINST.INI';
$regKey = $Registry->Open($chiave, {Access=> 0x20019});
foreach $key (keys %$regKey){
print"$key: " . $regKey->{"$key"} . "\n";
};
The above code do not work when executed with perl 64 bit on Windows 7/8/10.
Instead work fine when executed with perl 32 bit on all Windows version and work fine on 64 system with Windows server 2012, Windows 2003 Windows 2008.
Why?!
The script is very complex and old, undocumented and is the kind of script that nobody want to "touch" or "rewrite" so any ligth will appreciated a lot.
We do not get any error.
Simply when we run the script using 32 bit perl we get the following:
C:\>x:\stdperl\bin\perl 160609_t05.pl
Aster ODBC Driver/: Win32::TieRegistry=HASH(0x53b61a4)
Microsoft Access Driver (*.mdb)/: Win32::TieRegistry=HASH(0x53b61ec)
...
omissis
....
Oracle in OraClient12c_32/: Win32::TieRegistry=HASH(0x53b627c)
SQLite3 ODBC Driver/: Win32::TieRegistry=HASH(0x53b621c)
When I run the script using the 64 bit perl I do not get any entry.
C:\>x:\stdperl64\bin\perl 160609_t05.pl
C:\>
Under HKEY_LOCAL_MACHINE/SOFTWARE/ODBC/ODBCINST.INI registry there are ODBC drivers specification and there are entry on 32 and 64 hive registry.
Using more recent version of ActiveState perl the problem do not happen.
ciao
GIovanni

Jenkins powershell plugin is running 32 bit Powershell and I need 64bit

I'm pretty new to powershell integration in Jenkins and my scripts won't run because (I believe) I need powershell to be executed in 64 bit. Running:
[Environment]::Is64BitProcess
in my execution sequence yields false an then a cmdlet that I use (Get-WindowsFeature) is shown as not recognized as a cmdlet, etc. Any way to execute 64 bit powershell scripts?
Thanks!
Environment
Jenkins on Windows (mine happens to run as a service)
plus Powershell plugin (for running Powershell scripts as "build steps")
Jenkins will typically call upon the correct version of powershell.exe. However, the executor/slave process must be running a 64-bit JRE so that PowerShell can also operate in 64-bit mode.
A simple tester project with the following Powershell script can show the above 32-bit vs 64-bit nature:
$env:Path # Path will have the right Powershell available
[intptr]::size # outputs: 4 = 32-bit, 8 = 64-bit
Stop-WebAppPool FOOBAR # fails when 32-bit, succeeds when 64-bit
Console output example (extra blank lines for clarity):
[Powershell Test] $ powershell.exe -NonInteractive -ExecutionPolicy ByPass "& 'C:\Windows\TEMP\hudson123456789.ps1'"
C:\Windows\system32;C:\Windows;C:\Windows\System32\WindowsPowerShell\v1.0\
4
Stop-WebAppPool : Retrieving the COM class factory for component with CLSID
{688EEEE5-6A7E-422F-B2E1-6AF00DC944A6} failed due to the following error:
80040154 Class not registered (Exception from HRESULT: 0x80040154
(REGDB_E_CLASSNOTREG)).
At C:\Windows\TEMP\hudson123456789.ps1:7 char:1
Solution
tl;dr... Install 64-bit JRE, and configure Jenkins to be 64-bit.
I used chocolatey to install a fairly recent JRE, via "Administrator" PowerShell:
First, install chocolatey:
iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))
Looked for the latest version available https://chocolatey.org/packages?q=java (chocolatey has multiple packages for the same thing, often not kept fully up to date).
Then, install JRE (using the one with the higher JRE number):
choco install -y javaruntime
Or:
choco install -y jre8
Finally, I edited my jenkins.xml configuration so that it would run using the 64-bit JRE instead of the built-in JRE.
Changed:
<executable>%BASE%\jre\bin\java</executable>
To (set the path as appropriate for your instance):
<executable>C:\Program Files\Java\jre1.8.0_66\bin\java</executable>
This one should be an "always fresh" symlink (handled by system updates) that ought to allow your Jenkins instance to survive Restart and Update events:
<executable>C:\ProgramData\Oracle\Java\javapath\java.exe</executable>
Then I restarted Jenkins. Powershell execution woke up to the might of 64-bits. Note: I am using a single Jenkins instance that does double duty as the "server" and "execution slave" at the same time. For fully autonomous slaves, I would suppose doing whatever to get the slave-agents processes in 64-bit mode would result in a similar success.
Full automation? According to the chocolatey "jre8" package documentation, using command line switches, it's even be possible to force fixed destination paths for JRE, and exclude 32-bit and/or 64-bit editions, if fully automated non-interactive steps are needed. https://chocolatey.org/packages/jre8
I am not familiar with Jenkins, but it seems like it's a 32 bit process itself.
Can you specify the location of the PowerShell executable? If so, try to use this path:
C:\Windows\SysNative\WindowsPowerShell\v1.0\powershell.exe
If you can't do that, then you might be able to do it in code in your "execution sequence" with Invoke-Command:
Invoke-Command -ComputerName . -ScriptBlock { [Environment]::Is64BitProcess }
All the code in the scriptblock will be run in a separate 64 bit process and the results will be serialized and returned.
Explanations
Paths
On a 32 bit Windows OS, the system folder is C:\Windows\System32.
On a 64 bit Windows OS, the 64 bit system folder is also C:\Windows\System32. But the system folder for 32 bit processes on a 64 bit Windows installation is in fact C:\Windows\SysWOW64.
For compatibility, a 32 bit process on a 64 bit OS will have any calls to C:\Windows\System32 transparently redirected to C:\Windows\SysWOW64, unbeknownst to the process.
To enable a 32 bit process to reference the real System32 on a 64 bit OS, you can you use C:\Windows\SysNative.
Since PowerShell has a 32 bit and a 64 bit version, and it lives inside the system folders, you need to use the above rules to reference the correct executable depending on whether you're calling it from a 64 or 32 bit process.
The typical scenario (you want to call the version of the same bitness) is easiest (just call powershell.exe or reference it via System32), but it gets hairy if you want to reference the other version.
Invoke-Command Method
The Invoke-Command cmdlet lets you run code, typically on another computer, but you can run it on the same computer as well. This will spawn a completely separate process, and any output gets serialized and sent back to the calling process.
The caveat to this method is that you must enable PowerShell remoting on the machine, via Enable-PSRemoting or Group Policy (shameless self plug).
The default profile (Microsoft.PowerShell) that you connect to on a 64 bit machine will be a 64 bit version of PowerShell, regardless of the OS of the caller.
Incidentally, if you wanted to use Invoke-Command to connect to a 32 bit version, you could do so by explicitly specifying the profile Microsoft.PowerShell32.
OK, so the answer was pretty simple, yet maddening all at once. Basically, the module(s) didn't exist in both of the Powershell paths (x86 and x64), so copying the modules over to the 32-bit powershell environment fixed the issue.
further suggestions:
check path of 32bit-JRE, remove path or uninstall 32bit-JRE --
also swapping path-position with 64bit-JRE might work
check path(s) of PowerShell, remove path of 32bit PowerShell
(..\SysWOW64\..) and add the other one to the path (..\System32\..)
This worked for me!

Calling a protected Windows 7 executable with Perl

I'm trying to write a perl script that determines which users are currently logged into Windows by using query.exe (c:\Windows\system32\query.exe). Perl is unable to access this file, unable to execute it, even unable to see that it exists, as I've found with the following code:
print `dir c:\\windows\\system32\\query*`;
This produces the following output:
07/13/2009 05:16 PM 1,363,456 Query.dll
1 File(s) 1,363,456 bytes
0 Dir(s) 183,987,658,752 bytes free
I've checked the user executing the script using perl's getlogin function, and it returns the name of a member of the local Administrators group (specifically, me). I've also tried adding read/execute permissions for "Everyone", but windows keeps giving me access denied errors when I try to modify this file's permissions. Finally, I've tried running perl.exe as an administrator but that doesn't fix the problem either.
Is this something I can solve by changing some settings in Windows? Do I need to add something to my perl script? Or is there just no way to grant perl access to some of these processes?
On my 64 bit machine I can find query.exe in Windows\System32 (the 64 bit system directory) but not in Windows\SysWOW64 (the 32 bit system directory).
I suspect that you are running 64 bit Windows and 32 bit Perl and so, under WOW64 emulation, the 32 bit Perl process redirects system32 to SysWOW64.
Since system32 should be in your path you ought to be able to execute it by executing query.exe without any path. If you absolutely have to refer to the 64 bit system folder then you can do so from a 32 bit process with C:\Windows\sysnative.
If my suspicions are correct I recommend you do some reading up on WOW64 because it can be pretty confusing until you get on top of it. On the other hand, if you already know all about it I apologise for being patronising.