Last active
August 29, 2015 14:08
-
-
Save btm/1d522c783e48850478fd to your computer and use it in GitHub Desktop.
DISM API in Ruby via FFI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 32bit run: | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 DismApi.dll: - DismInitializeInternal | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 DismApi.dll: <----- Starting DismApi.dll session -----> - DismInitializeInternal | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 DismApi.dll: - DismInitializeInternal | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 DismApi.dll: Version 6.3.9600.17031 - DismInitializeInternal | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 DismApi.dll: Parent process command line: "C:\opscode\chef\embedded\bin\ruby.exe" dism.rb - DismInitializeInter nal | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 Enter DismInitializeInternal - DismInitializeInternal | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 Input parameters: LogLevel: 2, LogFilePath: btm-dism.log, ScratchDirectory: (null) - DismInitializeInternal | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 Initialized GlobalConfig - DismInitializeInternal | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 Initialized SessionTable - DismInitializeInternal | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 Lookup in table by path failed for: DummyPath-2BA51B78-C7F7-4910-B99D-BB7345357CDC - CTransactionalImageTable::L ookupImagePath | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 Waiting for m_pInternalThread to start - CCommandThread::Start | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8584 Enter CCommandThread::CommandThreadProcedureStub - CCommandThread::CommandThreadProcedureStub | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8584 Enter CCommandThread::ExecuteLoop - CCommandThread::ExecuteLoop | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 CommandThread StartupEvent signaled - CCommandThread::WaitForStartup | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 m_pInternalThread started - CCommandThread::Start | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 Created g_internalDismSession - DismInitializeInternal | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 Leave DismInitializeInternal - DismInitializeInternal | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 Enter DismOpenSessionInternal - DismOpenSessionInternal | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 Input parameters: ImagePath: DISM_{53BFAE52-B167-4E2F-A258-0A37B57FF845}, WindowsDirectory: (null), SystemDrive: (null) - DismOpenSessionInternal | |
2014-10-24 16:31:54, Error DISM API: PID=8872 TID=8096 Running the DISM API online under WoW64 is not supported - DismOpenSessionInternal(hr:0x8007000b) | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 Leave DismOpenSessionInternal - DismOpenSessionInternal | |
2014-10-24 16:31:54, Info DISM API: PID=8872 TID=8096 Session id is: 0 - DismOpenSessionInternal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Author:: Bryan McLellan <[email protected]> | |
# Author:: Adam Edwards <[email protected]> | |
# Copyright:: Copyright 2013-2014 Chef Software, Inc. | |
# License:: Apache License, Version 2.0 | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
# Work in progress code to access DISM via FFI in Ruby | |
# Only works with 64-bit ruby right now, the Wow64 redirection code doesn't appear to help | |
require 'rubygems' | |
require 'ffi' | |
require 'wmi-lite' | |
require 'win32/api' | |
# From dismapi.h | |
DISM_ONLINE_IMAGE = "DISM_{53BFAE52-B167-4E2F-A258-0A37B57FF845}\0".encode("UTF-16LE") | |
module Win32 | |
extend FFI::Library | |
ffi_lib 'dismapi' | |
# DismLogLevel: 0 Errors, 1 Warnings, 2 WarningsInfo | |
# buffer_in instead of string because we pass unicode | |
# HRESULT WINAPI DismInitialize( | |
# _In_ DismLogLevel LogLevel, | |
# _In_opt_ PCWSTR LogFilePath, | |
# _In_opt_ PCWSTR ScratchDirectory | |
# ); | |
attach_function :dism_initialize, :DismInitialize, [ :int, :buffer_in, :buffer_in ], :int | |
# HRESULT WINAPI DismOpenSession( | |
# _In_ PCWSTR ImagePath, | |
# _In_opt_ PCWSTR WindowsDirectory, | |
# _In_opt_ WCHAR* SystemDrive, | |
# _Out_ DismSession* Session | |
# ); | |
attach_function :dism_open_session, :DismOpenSession, [ :buffer_in, :string, :string, :pointer ], :int | |
# HRESULT WINAPI DismCloseSession( | |
# _In_ DismSession Session | |
# ); | |
attach_function :dism_close_session, :DismCloseSession, [ :pointer ], :int | |
# HRESULT WINAPI DismGetPackages ( | |
# _In_ DismSession Session, | |
# _Outptr_result_buffer_(*Count) DismPackage** Package, | |
# _Out_ UINT* Count | |
# ); | |
attach_function :dism_get_packages, :DismGetPackages, [ :pointer, :pointer, :pointer ], :int | |
ffi_lib 'kernel32' | |
attach_function :IsWow64Process, [ :pointer, :pointer ], :int | |
attach_function :GetCurrentProcess, [], :pointer | |
end | |
# returns :x86_64 or :i386 | |
def windows_architecture | |
wmi = WmiLite::Wmi.new | |
host = wmi.first_of('Win32_ComputerSystem') | |
case host['SystemType'] | |
when "x64-based PC" | |
return :x86_64 | |
when "X86-based PC" | |
return :i386 | |
end | |
raise "Unable to determine architecture" | |
end | |
def disable_wow64_file_redirection | |
original_redirection_state = ['0'].pack('P') | |
if windows_architecture == :x86_64 | |
win32_wow_64_disable_wow_64_fs_redirection = | |
::Win32::API.new('Wow64DisableWow64FsRedirection', 'P', 'L', 'kernel32') | |
succeeded = win32_wow_64_disable_wow_64_fs_redirection.call(original_redirection_state) | |
if succeeded == 0 | |
raise Win32APIError "Failed to disable Wow64 file redirection" | |
end | |
end | |
original_redirection_state | |
end | |
def restore_wow64_file_redirection(original_redirection_state ) | |
if windows_architecture == :x86_64 | |
win32_wow_64_revert_wow_64_fs_redirection = | |
::Win32::API.new('Wow64RevertWow64FsRedirection', 'P', 'L', 'kernel32') | |
succeeded = win32_wow_64_revert_wow_64_fs_redirection.call(original_redirection_state) | |
if succeeded == 0 | |
raise Win32APIError "Failed to revert Wow64 file redirection" | |
end | |
end | |
end | |
def is_i386_process_on_x86_64_windows? | |
is_64_bit_process_result = FFI::MemoryPointer.new(:int) | |
# The return value of IsWow64Process is nonzero value if the API call succeeds. | |
# The result data are returned in the last parameter, not the return value. | |
call_succeeded = Win32.IsWow64Process(Win32.GetCurrentProcess(), is_64_bit_process_result) | |
# The result is nonzero if IsWow64Process's calling process, in the case here | |
# this process, is running under WOW64, i.e. the result is nonzero if this | |
# process is 32-bit (aka :i386). | |
result = (call_succeeded != 0) && (is_64_bit_process_result.get_int(0) != 0) | |
end | |
def with_os_architecture | |
wow64_redirection_state = nil | |
if is_i386_process_on_x86_64_windows? | |
puts "Detected that we're running under WOW64, disabling WOW64 redirection" | |
wow64_redirection_state = disable_wow64_file_redirection | |
end | |
begin | |
yield | |
ensure | |
if wow64_redirection_state | |
restore_wow64_file_redirection(wow64_redirection_state) | |
end | |
end | |
end | |
def HRESULT_CODE(hr) | |
hr & 0xFFFF | |
end | |
def HRESULT_FACILITY(hr) | |
(hr >> 16) & 0x1fff | |
end | |
def HRESULT_SEVERITY(hr) | |
(hr >> 31) & 0x1 | |
end | |
def check_result(result, message) | |
if result == 0 | |
puts "#{message} successful" | |
else | |
raise "#{message} failed: #{HRESULT_CODE(result)}, #{HRESULT_FACILITY(result)}, #{HRESULT_SEVERITY(result)}" | |
end | |
end | |
with_os_architecture do | |
log_level = 2 | |
log_location = "btm-dism.log\0".encode("UTF-16LE") | |
scratch_directory = nil | |
check_result(Win32.dism_initialize(log_level, log_location, scratch_directory), "dism_initialize") | |
dism_session_ptr = FFI::MemoryPointer.new(:pointer) | |
check_result(Win32.dism_open_session(DISM_ONLINE_IMAGE, nil, nil, dism_session_ptr), "dism_open_session") | |
unknown_ptr = FFI::MemoryPointer.new(:pointer) | |
count_ptr = FFI::MemoryPointer.new(:pointer) | |
check_result(Win32.dism_get_packages(dism_session_ptr.get_pointer(0), unknown_ptr, count_ptr), "dism_get_packages") | |
check_result(Win32.dism_close_session(dism_session_ptr.get_pointer(0)), "dism_close_session") | |
puts "Found #{count_ptr.get_int(0)} packages." | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
64bit Ruby: | |
PS C:\ruby> C:\Ruby200-x64\bin\ruby dism.rb | |
dism_initialize successful | |
dism_open_session successful | |
dism_get_packages successful | |
dism_close_session successful | |
Found 109 packages. | |
32bit Ruby: | |
PS C:\ruby> ruby dism.rb | |
Detected that we're running under WOW64, disabling WOW64 redirection | |
dism_initialize successful | |
dism.rb:160:in `check_result': dism_open_session failed: 11, 7, 1 (RuntimeError) | |
from dism.rb:173:in `block in <main>' | |
from dism.rb:136:in `with_os_architecture' | |
from dism.rb:164:in `<main>' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment