# File lib/win32/process.rb, line 261
   def create(args)
      unless args.kind_of?(Hash)
         raise TypeError, 'Expecting hash-style keyword arguments'
      end
      
      valid_keys = %w/
         app_name inherit creation_flags cwd environment startup_info
         thread_inherit process_inherit
      /

      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 some default values
      hash = {
         'inherit'          => 0,
         'process_inherit'  => 0,
         'thread_inherit'   => 0,
         'creation_flags'   => 0,
         'cwd'              => 0
      }
      env = 0
      
      # 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 ProcessError, "invalid key '#{key}'"
         end
         
         # Convert true to 1 and nil/false to 0.
         case val
            when true, false
               hash[key] = val == false ? 0 : 1
            when nil
               hash[key] = 0 # Win32API sometimes doesn't like nil
            else
               hash[key] = val
         end
      }
      
      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 ProcessError, "invalid startup_info key '#{key}'"
            end
            si_hash[key] = val
         }
      end
      
      # The +app_name+ key is mandatory
      unless hash['app_name']
         raise ProcessError, 'app_name must be specified'
      end
      
      # The environment string should be passed as a string of ';' separated
      # paths.
      if hash['environment'] 
         env = hash['environment'].split(File::PATH_SEPARATOR) << 0.chr
         env = [env.join("\0")].pack('p*').unpack('L').first
      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] = [hash['process_inherit?']].pack('L')
      end

      # Thread SECURITY_ATTRIBUTE structure
      thread_security = 0
      if hash['thread_security?']
         thread_security = [0,0,0].pack('LLL')
         thread_security[0,4] = [12].pack('L') # sizeof(SECURITY_ATTRIBUTE)
         thread_security[8,4] = [hash['thread_inherit?']].pack('L')
      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 ProcessError, get_last_error
            end
            
            si_hash[io] = handle
         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

      bool = CreateProcess(
         0,                      # App name
         hash['app_name'],       # 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
      )
      
      unless bool
         raise ProcessError, "CreateProcess() failed: ", get_last_error
      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