    # File lib/chef/shell_out/windows.rb, line 243
      def create(args)
        unless args.kind_of?(Hash)
          raise TypeError, 'Expecting hash-style keyword arguments'
        end

        valid_keys = %w/
          app_name command_line inherit creation_flags cwd environment
          startup_info thread_inherit process_inherit close_handles with_logon
          domain password
        /

        valid_si_keys = %/
          startf_flags desktop title x y x_size y_size x_count_chars
          y_count_chars fill_attribute sw_flags stdin stdout stderr
        /

        # Set default values
        hash = {
          'app_name'       => nil,
          'creation_flags' => 0,
          'close_handles'  => true
        }

        # Validate the keys, and convert symbols and case to lowercase strings.
        args.each{ |key, val|
          key = key.to_s.downcase
          unless valid_keys.include?(key)
            raise ArgumentError, "invalid key '#{key}'"
          end
          hash[key] = val
        }

        si_hash = {}

        # If the startup_info key is present, validate its subkeys
        if hash['startup_info']
          hash['startup_info'].each{ |key, val|
            key = key.to_s.downcase
            unless valid_si_keys.include?(key)
              raise ArgumentError, "invalid startup_info key '#{key}'"
            end
            si_hash[key] = val
          }
        end

        # The +command_line+ key is mandatory unless the +app_name+ key
        # is specified.
        unless hash['command_line']
          if hash['app_name']
            hash['command_line'] = hash['app_name']
            hash['app_name'] = nil
          else
            raise ArgumentError, 'command_line or app_name must be specified'
          end
        end

        # The environment string should be passed as an array of A=B paths, or
        # as a string of ';' separated paths.
        if hash['environment']
          env = hash['environment']
          if !env.respond_to?(:join)
            # Backwards compat for ; separated paths
            env = hash['environment'].split(File::PATH_SEPARATOR)
          end
          # The argument format is a series of null-terminated strings, with an additional null terminator.
          env = env.map { |e| e + "\0" }.join("") + "\0"
          if hash['with_logon']
            env = env.multi_to_wide(e)
          end
          env = [env].pack('p*').unpack('L').first
        else
          env = nil
        end

        startinfo = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
        startinfo = startinfo.pack('LLLLLLLLLLLLSSLLLL')
        procinfo  = [0,0,0,0].pack('LLLL')

        # Process SECURITY_ATTRIBUTE structure
        process_security = 0
        if hash['process_inherit']
          process_security = [0,0,0].pack('LLL')
          process_security[0,4] = [12].pack('L') # sizeof(SECURITY_ATTRIBUTE)
          process_security[8,4] = [1].pack('L')  # TRUE
        end

        # Thread SECURITY_ATTRIBUTE structure
        thread_security = 0
        if hash['thread_inherit']
          thread_security = [0,0,0].pack('LLL')
          thread_security[0,4] = [12].pack('L') # sizeof(SECURITY_ATTRIBUTE)
          thread_security[8,4] = [1].pack('L')  # TRUE
        end

        # Automatically handle stdin, stdout and stderr as either IO objects
        # or file descriptors.  This won't work for StringIO, however.
        ['stdin', 'stdout', 'stderr'].each{ |io|
          if si_hash[io]
            if si_hash[io].respond_to?(:fileno)
              handle = get_osfhandle(si_hash[io].fileno)
            else
              handle = get_osfhandle(si_hash[io])
            end

            if handle == INVALID_HANDLE_VALUE
              raise Error, get_last_error
            end

            # Most implementations of Ruby on Windows create inheritable
            # handles by default, but some do not. RF bug #26988.
            bool = SetHandleInformation(
              handle,
              HANDLE_FLAG_INHERIT,
              HANDLE_FLAG_INHERIT
            )

            raise Error, get_last_error unless bool

            si_hash[io] = handle
            si_hash['startf_flags'] ||= 0
            si_hash['startf_flags'] |= STARTF_USESTDHANDLES
            hash['inherit'] = true
          end
        }

        # The bytes not covered here are reserved (null)
        unless si_hash.empty?
          startinfo[0,4]  = [startinfo.size].pack('L')
          startinfo[8,4]  = [si_hash['desktop']].pack('p*') if si_hash['desktop']
          startinfo[12,4] = [si_hash['title']].pack('p*') if si_hash['title']
          startinfo[16,4] = [si_hash['x']].pack('L') if si_hash['x']
          startinfo[20,4] = [si_hash['y']].pack('L') if si_hash['y']
          startinfo[24,4] = [si_hash['x_size']].pack('L') if si_hash['x_size']
          startinfo[28,4] = [si_hash['y_size']].pack('L') if si_hash['y_size']
          startinfo[32,4] = [si_hash['x_count_chars']].pack('L') if si_hash['x_count_chars']
          startinfo[36,4] = [si_hash['y_count_chars']].pack('L') if si_hash['y_count_chars']
          startinfo[40,4] = [si_hash['fill_attribute']].pack('L') if si_hash['fill_attribute']
          startinfo[44,4] = [si_hash['startf_flags']].pack('L') if si_hash['startf_flags']
          startinfo[48,2] = [si_hash['sw_flags']].pack('S') if si_hash['sw_flags']
          startinfo[56,4] = [si_hash['stdin']].pack('L') if si_hash['stdin']
          startinfo[60,4] = [si_hash['stdout']].pack('L') if si_hash['stdout']
          startinfo[64,4] = [si_hash['stderr']].pack('L') if si_hash['stderr']
        end

        if hash['with_logon']
          logon  = multi_to_wide(hash['with_logon'])
          domain = multi_to_wide(hash['domain'])
          app    = hash['app_name'].nil? ? nil : multi_to_wide(hash['app_name'])
          cmd    = hash['command_line'].nil? ? nil : multi_to_wide(hash['command_line'])
          cwd    = multi_to_wide(hash['cwd'])
          passwd = multi_to_wide(hash['password'])

          hash['creation_flags'] |= CREATE_UNICODE_ENVIRONMENT

          process_ran = CreateProcessWithLogonW(
            logon,                  # User
            domain,                 # Domain
            passwd,                 # Password
            LOGON_WITH_PROFILE,     # Logon flags
            app,                    # App name
            cmd,                    # Command line
            hash['creation_flags'], # Creation flags
            env,                    # Environment
            cwd,                    # Working directory
            startinfo,              # Startup Info
            procinfo                # Process Info
          )
        else
          process_ran = CreateProcess(
            hash['app_name'],       # App name
            hash['command_line'],   # Command line
            process_security,       # Process attributes
            thread_security,        # Thread attributes
            hash['inherit'],        # Inherit handles?
            hash['creation_flags'], # Creation flags
            env,                    # Environment
            hash['cwd'],            # Working directory
            startinfo,              # Startup Info
            procinfo                # Process Info
          )
        end

        # TODO: Close stdin, stdout and stderr handles in the si_hash unless
        # they're pointing to one of the standard handles already. [Maybe]
        if !process_ran
          raise_last_error("CreateProcess()")
        end

        # Automatically close the process and thread handles in the
        # PROCESS_INFORMATION struct unless explicitly told not to.
        if hash['close_handles']
          CloseHandle(procinfo[0,4].unpack('L').first)
          CloseHandle(procinfo[4,4].unpack('L').first)
        end

        ProcessInfo.new(
          procinfo[0,4].unpack('L').first, # hProcess
          procinfo[4,4].unpack('L').first, # hThread
          procinfo[8,4].unpack('L').first, # hProcessId
          procinfo[12,4].unpack('L').first # hThreadId
        )
      end