Skip to content

Instantly share code, notes, and snippets.

@btm
Last active August 29, 2015 14:08
Show Gist options
  • Save btm/1d522c783e48850478fd to your computer and use it in GitHub Desktop.
Save btm/1d522c783e48850478fd to your computer and use it in GitHub Desktop.
DISM API in Ruby via FFI
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
# 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
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