/* * P4.Net * Copyright (c) 2007 Shawn Hladky Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using System; using System.Text; using p4dn; using System.Diagnostics; using P4API.Exceptions; using System.IO; namespace P4API { /// /// Delegate to handle the OnPrompt event. /// /// Sender /// P4PromptEventArgs /// Handle Perforce "prompts". Prompts are from commands that ask the user to respond, such as: /// ///
  • login
  • ///
  • passwd
  • ///
  • resolve (without an -a* switch)
  • ///
    ///
    /// public delegate void OnPromptEventHandler(object sender, P4PromptEventArgs args); /// /// A connection to a Perforce server instance. /// /// public class P4Connection : IDisposable { #region Private Variables private ClientApi m_ClientApi; private bool _tagged = true; private bool _Initialized; private System.Collections.Specialized.StringDictionary cachedSpecDefs; private string _CallingProgram; private string _CallingProgramVersion; private string _Client = null; private string _Port = null; private string _User = null; private string _Host = null; private string _CWD = null; private string _Charset = null; private DateTime _p4epoch = new DateTime(1970, 1, 1); private P4ExceptionLevels _exceptionLevel = P4ExceptionLevels.NoExceptionOnWarnings ; private bool _runMergeTool = false; private int _maxScanRows = 0; private int _maxResults = 0; private int _maxLockTime = 0; private int _ApiLevel = 0; #endregion #region Events /// /// Raised when Perforce is prompting for a response. /// /// Handle Perforce "prompts". Prompts are from commands that ask the user to respond, such as: /// ///
  • login
  • ///
  • passwd
  • ///
  • resolve (without an -a* switch)
  • ///
    ///
    /// public event OnPromptEventHandler OnPrompt; /// /// Raised from P4PrintStreamEvents before a file is printed. /// /// Handle this event to initialize a stream that P4API will write to. public event OnPrintStreamEventHandler OnPrintStream; /// /// Raised from P4PrintStreamEvents after a file is printed. /// /// /// Use this event to close any streams that were created by the OnPrintStreamEventHandler event. /// public event OnPrintEndEventHandler OnPrintEndFile; #endregion #region Contructors /// /// Initializes a new instance of the class. /// public P4Connection() { this._Initialized = false; this._CallingProgram = "P4.Net API"; this._CallingProgramVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); } #endregion #region Public Properties /// /// Gets/Sets a value that overrides the defined MaxScanRows. /// /// Servers may set a limit for MaxScanRows for certain users. Set this value to override. /// A value of 0 indicates no override. /// Overrides the MaxScanRows defined at the server. public int MaxScanRows { get { return _maxScanRows ; } set { _maxScanRows = value; //_ClientAPI.SetMaxScanRows(value); } } /// /// Gets/Sets a value that overrides the defined MaxLockTime. /// /// Servers may set a limit for MaxLockTime for certain users. Set this value to override. /// A value of 0 indicates no override. /// Overrides the MaxLockTime defined at the server. public int MaxLockTime { get { return _maxLockTime; } set { _maxLockTime = value; //_ClientAPI.SetMaxScanRows(value); } } /// /// Gets/Sets a value that overrides the defined MaxResults. /// /// Servers may set a limit for MaxResults for certain users. Set this value to override. /// A value of 0 indicates no override. /// Overrides the MaxResults defined at the server. public int MaxResults { get { return _maxResults; } set { _maxResults = value; //_ClientAPI.SetMaxResults(value); } } /// /// Gets/Sets the Host-name of the client. /// /// Forcing the Host to a different name is useful when using a client that /// was defined on a different host. Of course, that can cause issues with the client's /// have list, so use with caution. /// The client Host name. public string Host { get { return _ClientAPI.Host; } set { _Host = value; _ClientAPI.SetHost(value); } } /// /// Gets/Sets the Perforce Server port. /// /// The port. /// Defaults to the value set in your Perforce environment. Port can not be /// changed after running Connect (not even after a Disconnect). /// To connect to a server on another port, create a new instance of P4Connection. /// public string Port { get { return _ClientAPI.Port; } set { if (_Initialized) throw new ServerAlreadyConnected(); _Port = value; _ClientAPI.SetPort(value); } } /// /// Gets/Sets the User login used to connect to Perforce. /// /// The user. /// Defaults to the value set in your Perforce environment. /// After you've set User, you can unset (revert to the default) by setting to User to null. public string User { get { return _ClientAPI.User; } set { _User = value; _ClientAPI.SetUser(value); } } /// /// Gets/Sets the client character set. /// /// Defaults to the value set in your Perforce environment. /// After you've set Charset, you can unset (revert to the default) by setting to Charset to null. /// The client's charset. public string Charset { get { return _ClientAPI.Charset; } set { _Charset = value; _ClientAPI.SetCharset(value); } } /// /// Get/Sets the name of the calling program. /// /// This value is seen in p4 monitor and in the server log files. /// Defaults to "P4.Net API". public string CallingProgram { get { return _CallingProgram; } set { if (_Initialized) throw new ServerNotConnected_SetVar_AfterInit(); _CallingProgram = value; // Set these in the initialize section } } /// /// Get/Set whether an external merge tool is executed during a resolve. /// /// /// If true, will wait for the external merge tool to execute before returning. /// P4MERGE must be set in the Perforce environment. /// /// /// bool indicating if an external merge tool is executed during a resolve /// //public bool RunMergeTool //{ // get // { // return _runMergeTool; // } // set // { // _runMergeTool = value; // } //} /// /// Gets/Sets the version of the calling program's version. /// /// This value is seen in p4 monitor and the server log files. /// Defaults to the assembly version of the P4API.dll. public string CallingVersion { get { return _CallingProgramVersion; } set { if (_Initialized) throw new ServerNotConnected_SetVar_AfterInit(); _CallingProgramVersion = value; // Set these in the initialize section } } /// /// Sets the password to conenct with. /// /// /// Do not set the literal password with a server running security level 2 or higher. /// For those servers, Use the Login method and/or a ticket in place of the Password. /// /// The user's password. public string Password { set { _ClientAPI.SetPassword(value); } } /// /// Sets the ticket file used for Authentication. /// /// /// Use this to override the default location of the TicketFile. /// /// Overrided the ticket file location. public string TicketFile { set { _ClientAPI.SetTicketFile(value); } } /// /// Gets/Sets the client workspace. /// /// Many Perforce commands require a valid client spec to be set in order to run. /// Defaults to the value set in your Perforce environment. After you've set Client, you /// can unset (revert to the default) by setting to Client to null. /// The client name. public string Client { get { return _ClientAPI.Client; } set { _Client = value; _ClientAPI.SetClient(value); } } /// /// Gets/Sets the current working directory. /// /// Setting CWD can be used so that relative paths are specified. Keep in mind that changing /// the CWD also might cause P4CONFIG settings to change. /// The current working directory for the Perforce connection. public string CWD { get { return _ClientAPI.Cwd; } set { _CWD = value; _ClientAPI.SetCwd(value); } } /// /// Sets the client API protocol level. /// /// Sets the API compatibility level desired. /// This is useful when writing scripts using Perforce commands that do not yet support tagged output. /// In these cases, upgrading to a later server that supports tagged output for the commands in question /// can break your script. Using this method allows you to lock your script to the /// output format of an older Perforce release and facilitate seamless upgrades. /// Note that this method must be called prior to calling Connect. ///
    /// See the http://kb.perforce.com/P4dServerReference/ProtocolLevels/PerforceClientLevels for the API integer levels that correspond to each Perforce release. ///
    /// The Perforce API level. public int Api { get { return _ApiLevel; } set { _ApiLevel = value; _ClientAPI.SetProtocol("api", value.ToString()); } } /// /// Gets/Sets the Exception level when running Perforce commands. /// /// This property controls when P4.Net will throw exceptions /// when the underlying Perforce commands raise errors and warnings. /// The default is /// The exception level for the connection. public P4ExceptionLevels ExceptionLevel { get { return _exceptionLevel; } set { _exceptionLevel = value; } } /// /// Checks the server level (version) of the Perforce server. /// /// The server's version level. /// See http://kb.perforce.com/P4dServerReference/ProtocolLevels/PerforceServerLevels for more informantion. /// The server's version level. public int ServerLevel { get { if (!_Initialized) throw new P4API.Exceptions.ServerNotConnected(); string serverLevel = _ClientAPI.GetProtocol("server2"); if (serverLevel == null) { return 0; } else { return int.Parse(serverLevel); } } } #endregion #region Public Methods #region Date Converion Methods /// /// Converts Perforce date (integer) to .Net DateTime. /// /// /// The Perforce API returns most dates as an integer representing the number of seconds /// since 1/1/1970 in UTC. The command line client generally returns dates as strings /// in the Perforce server's time zone. The ConvertDate methods use the client's time zone for /// the offset. /// /// DateTime in .Net format. public DateTime ConvertDate(int p4Date) { DateTime utc = _p4epoch.AddSeconds(p4Date); return TimeZone.CurrentTimeZone.ToLocalTime(utc); } /// /// Converts Perforce date (integer value as a string) to .Net DateTime. /// /// /// The Perforce API returns most dates as an integer representing the number of seconds /// since 1/1/1970 in UTC. The command line client generally returns dates as strings /// in the Perforce server's time zone. The ConvertDate methods use the client's time zone for /// the offset. /// public DateTime ConvertDate(string p4Date) { return ConvertDate(int.Parse(p4Date)); } /// /// Converts .Net DateTime to Perforce date (integer). /// /// /// The Perforce API returns most dates as an integer representing the number of seconds /// since 1/1/1970 in UTC. The command line client generally returns dates as strings /// in the Perforce server's time zone. The ConvertDate methods use the client's time zone for /// the offset. /// public int ConvertDate(DateTime date) { DateTime utc = TimeZone.CurrentTimeZone.ToUniversalTime(date); TimeSpan ts = utc.Subtract(_p4epoch); return ts.Days*24*60*60 + ts.Hours*60*60 + ts.Minutes*60 + ts.Seconds; } #endregion #region General Methods /// /// Checks the case-sensitivity of the Perforce server. /// /// True when the connected server is case-sensitive. /// This must be called after connecting to the server. public bool IsServerCaseSensitive() { if (!_Initialized) throw new P4API.Exceptions.ServerNotConnected(); string serverCase = _ClientAPI.GetProtocol("nocase"); if (serverCase == null) { return true; } else { return false; } } /// /// Connect to the Perforce server /// public void Connect() { // reset the cached SpecDefs cachedSpecDefs = new System.Collections.Specialized.StringDictionary(); EstablishConnection(_tagged); } /// /// Disconnect from the Perforce Server /// public void Disconnect() { CloseConnection(); } /// /// Login to the Perforce Server /// /// The password. public void Login(string password) { if (password == null) throw new ArgumentNullException(); // if password is empty, then don't do anything if (password == string.Empty) return; P4ExceptionLevels oldLevel = _exceptionLevel; try { P4UnParsedRecordSet r = new P4UnParsedRecordSet(); r.LoginPassword = password; EstablishConnection(false); string[] Args = { }; _exceptionLevel = P4ExceptionLevels.NoExceptionOnErrors; RunIt("login", Args, r.ResultClientUser); //for good measure delete the password r.LoginPassword = null; if (r.HasErrors()) { throw new P4API.Exceptions.InvalidLogin(r.ErrorMessage); } } finally { _exceptionLevel = oldLevel; } } /// /// Creates a new pending changelist. /// /// The description. /// P4PendingChangelist object representing the named pending changelist public P4PendingChangelist CreatePendingChangelist(string Description) { if (Description == null) throw new ArgumentNullException(); if (Description == string.Empty) throw new ArgumentException("Descrition can not be empty!"); EstablishConnection(false); return new P4PendingChangelist(Description, this); } /// /// Determines if the Perforce connection is valid. /// /// if set to true it will check if the user is logged in. /// if set to true it will verify the client exists. /// /// true if the connection is valid; otherwise, false. /// /// /// IsValidConnection performs the following 3 checks (depending on the arguments passed). /// ///
  • Runs p4 info, and verifies the server is valid.
  • ///
  • If checkClient is true, verifies that p4 info returns a clientName that is not "*unknown*.
  • ///
  • If checkLogin is true, verifies that p4 login -s does not have errors.
  • ///
    ///
    public bool IsValidConnection(bool checkLogin, bool checkClient) { P4ExceptionLevels oldExLevel = this.ExceptionLevel; try { this.ExceptionLevel = P4ExceptionLevels.NoExceptionOnErrors; P4RecordSet r = Run("info"); if (r.HasErrors()) return false; if (r.Records.Length != 1) return false; if (checkClient) { if (!r.Records[0].Fields.ContainsKey("clientName")) return false; if (r.Records[0].Fields["clientName"] == "*unknown*") return false; } if (checkLogin) { P4UnParsedRecordSet ur = RunUnParsed("login", "-s"); if (ur.HasErrors()) return false; } } catch { // something went way wrong return false; } finally { // set the ExceptionLevel back this.ExceptionLevel = oldExLevel; } return true; } /// /// Executes a Perforce command in tagged mode. /// /// The command. /// The arguments to the Perforce command. Remember to use a dash (-) in front of all switches /// A P4Recordset containing the results of the command. public P4RecordSet Run(string Command, params string[] Args) { P4RecordSet r = new P4RecordSet(); EstablishConnection(true); RunIt(Command, Args, r.ResultClientUser); if (((_exceptionLevel == P4ExceptionLevels.ExceptionOnBothErrorsAndWarnings || _exceptionLevel == P4ExceptionLevels.NoExceptionOnWarnings) && r.HasErrors()) || (_exceptionLevel == P4ExceptionLevels.ExceptionOnBothErrorsAndWarnings && r.HasWarnings()) ) { throw new RunException(r); } if (r.ResultClientUser.DeferedException != null) { throw r.ResultClientUser.DeferedException; } return r; } /// /// Executes a Perforce command in non-tagged mode. /// /// The command. /// The args. /// public P4UnParsedRecordSet RunUnParsed(string Command, params string[] Args) { P4UnParsedRecordSet r = new P4UnParsedRecordSet(); P4BaseRecordSet.OnPromptEventHandler handler = new P4BaseRecordSet.OnPromptEventHandler(this.HandleOnPrompt); r.OnPrompt += handler; EstablishConnection(false); RunIt(Command, Args, r.ResultClientUser); r.OnPrompt -= handler; if (((_exceptionLevel == P4ExceptionLevels.ExceptionOnBothErrorsAndWarnings || _exceptionLevel == P4ExceptionLevels.NoExceptionOnWarnings) && r.HasErrors()) || (_exceptionLevel == P4ExceptionLevels.ExceptionOnBothErrorsAndWarnings && r.HasWarnings()) ) { throw new RunUnParsedException(r); } // always throw a defered exception (means something went WAY wrong) if (r.ResultClientUser.DeferedException != null) { throw r.ResultClientUser.DeferedException; } return r; } #endregion #region Form Methods /// /// Parse the raw text of a Perforce form into a P4Form object /// /// The form command. /// Raw contents of the form spec. /// /// A P4Form object. The fields of the form can be read or updated. If you update a filed, you can save it with Save_Form. /// /// public P4Form Parse_Form(string formCommand, string formContents) { // logic stolen from P4Ruby. Cache the spec defs, and load a form from the cached specdefs. // If we don't have a cached Spec def, we need to create a dummy form to get it in the cache if (!cachedSpecDefs.ContainsKey(formCommand)) { string bogusSpec = "__p4net_bogus_spec__"; P4Form outputForm; // // For specs of the following types we need the bogus spec name // if (formCommand == "branch" || formCommand == "label" || formCommand == "depot" || formCommand == "group") { outputForm = Fetch_Form(formCommand, bogusSpec); } else { outputForm = Fetch_Form(formCommand); } } return P4Form.LoadFromSpec(formCommand, cachedSpecDefs[formCommand], formContents, m_ClientApi.Encoding); } /// /// Fetch a form object from Perforce. /// /// The form command. /// The args. /// /// A Form object. The fields of the form can be read or updated. If you update a filed, you can save it with Save_Form. /// /// public P4Form Fetch_Form(string FormCommand, params string[] Args) { if (FormCommand == null) throw new ArgumentNullException(); if (Args == null) throw new ArgumentNullException(); string[] AllArgs = new string[Args.Length + 1]; AllArgs[0] = "-o"; for (int i = 0; i < Args.Length ; i++) { if (Args[i] == null) { throw new ArgumentNullException(); } if (Args[i] == "-o") { throw new InvalidFormArgument(); } AllArgs[i+1] = Args[i]; } P4FormRecordSet r = new P4FormRecordSet(FormCommand, m_ClientApi.Encoding); EstablishConnection(true); RunIt(FormCommand, AllArgs, r.ResultClientUser); // The Fetch_Form command must always throw an error // because there will be no form to return if there's a // problem if (r.HasErrors()) { throw new FormFetchException(FormCommand, r.ErrorMessage); } // save the spec def, in case Parse_Form is called in the future cachedSpecDefs[FormCommand] = r.Form.SpecDef; return r.Form; } /// /// Saves the form to Perforce. /// /// The P4Form object retrieved from Fetch_Form. /// /// P4UnParsedRecordSet. Output can be parsed to verify the form was processed correctly. public P4UnParsedRecordSet Save_Form(P4Form Form) { return Save_Form(Form, false); } /// /// Saves the form to Perforce. /// /// The form command to run. /// The formatted spec. /// P4UnParsedRecordSet. Output can be parsed to verify the form was processed correctly. public P4UnParsedRecordSet Save_Form(string formCommand, string formSpec) { return Save_Form(formCommand, formSpec, false); } /// /// Saves the form to Perforce. /// /// The form command to run. /// The formatted spec. /// True to pass the '-f' flag when saving. /// P4UnParsedRecordSet. Output can be parsed to verify the form was processed correctly. public P4UnParsedRecordSet Save_Form(string formCommand, string formSpec, bool Force) { if (formCommand == null) throw new ArgumentNullException("formCommand"); if (formCommand == string.Empty) throw new ArgumentException("Parameter 'formCommand' can not be empty"); if (formSpec == null) throw new ArgumentNullException("formSpec"); if (formSpec == string.Empty) throw new ArgumentException("Parameter 'formSpec' can not be empt"); P4UnParsedRecordSet r = new P4UnParsedRecordSet(); r.InputData = formSpec; EstablishConnection(false); if (Force) { string[] Args = { "-i", "-f" }; RunIt(formCommand, Args, r.ResultClientUser); } else { string[] Args = { "-i" }; RunIt(formCommand, Args, r.ResultClientUser); } if (((_exceptionLevel == P4ExceptionLevels.ExceptionOnBothErrorsAndWarnings || _exceptionLevel == P4ExceptionLevels.NoExceptionOnWarnings) && r.HasErrors()) || (_exceptionLevel == P4ExceptionLevels.ExceptionOnBothErrorsAndWarnings && r.HasWarnings()) ) { throw new RunUnParsedException(r); } return r; } /// /// Saves the form to Perforce. /// /// The P4Form object retrieved from Fetch_Form. /// True to pass the '-f' flag when saving. /// /// P4UnParsedRecordSet. Output can be parsed to verify the form was processed correctly. public P4UnParsedRecordSet Save_Form(P4Form Form, bool Force) { if (Form == null) throw new ArgumentNullException("Form"); return Save_Form(Form.FormCommand, Form.FormatSpec(), Force); } #endregion #region Print Methods /// /// Print the contents of a file in Perforce to a byte array. /// /// Perforce path to the file. /// /// Contents of the file. /// Text files will be encoded with ANSI encodinge. /// Unicode files will be encoded as UTF-8 (regardless of P4CHARSET setting). /// public byte[] PrintBinary(string depotPath) { // use utf-16 encoding. In theory this will be more effient, b/c // it will do only one conversion. MemoryStream ms = new MemoryStream(); PrintStream(ms, depotPath); byte[] ret = new byte[ms.Position]; ms.Position = 0; ms.Read(ret, 0, ret.Length); ms.Close(); return ret; } /// /// Print the contents of a file in Perforce to a string. /// /// Perforce path to the file. /// Contents of the file. /// /// Attempting to call PrintText on a binary file will likely cause corruption.
    /// If a file is does not exist, a FileNotFound exception will be thrown (regardless /// of the ExceptionLevel setting).
    /// Depot Path, Client Path or Local Path can generally be used for the argument /// depotPath (so long as the argument is valid for the p4 print command line. ///
    public string PrintText(string depotPath) { // use utf-16 encoding. In theory this will be more effient, b/c // it will do only one conversion. MemoryStream ms = new MemoryStream(); StreamReader sr = new StreamReader(ms, Encoding.Unicode); PrintStream(ms, depotPath, Encoding.Unicode); ms.Position = 0; string ret = sr.ReadToEnd(); sr.Close(); ms.Close(); return ret; } /// /// Prints the contents of a Perforce file to a Stream. /// /// Writable stream to write the contents to. /// Perforce path of the file to print. /// Text encoding of the Stream. /// /// If a file is does not exist, a FileNotFound exception will be thrown (regardless /// of the ExceptionLevel setting).
    /// Depot Path, Client Path or Local Path can generally be used for the argument /// depotPath (so long as the argument is valid for the p4 print command line.
    /// Encoding will only be applied to files with a Perforce type of 'text' or 'unicode'.
    /// The stream argument can be any valid stream, so long as it is initialized and writable.
    ///
    public void PrintStream(Stream stream, string depotPath, Encoding encoding) { if (stream == null) throw new ArgumentNullException(); if (depotPath == null) throw new ArgumentNullException(); if (depotPath == string.Empty) throw new ArgumentException("Argument depotPath can not be empty."); if (!stream.CanWrite) throw new StreamNotWriteable(); PrintStreamHelper ph = new PrintStreamHelper(this); int filesPrinted = ph.PrintStream(stream, depotPath, encoding); if (filesPrinted < 1) { throw new Exceptions.FileNotFound(depotPath); } } /// /// Prints the contents of a Perforce file to a Stream. /// /// Writable stream to write the contents to. /// Perforce path of the file to print. /// /// If a file is does not exist, a FileNotFound exception will be thrown (regardless /// of the ExceptionLevel setting).
    /// Depot Path, Client Path or Local Path can generally be used for the argument /// depotPath (so long as the argument is valid for the p4 print command line.
    /// The stream argument can be any valid stream, so long as it is initialized and writable.
    ///
    public void PrintStream(Stream stream, string depotPath) { PrintStream(stream, depotPath, null); } /// /// Runs a print command, and raises events for each file printed. /// /// Arguments to the p4 print command. /// OnPrintStream /// OnPrintEndFile /// /// This method is useful when you need to print several files with a single call /// to the Perforce server. /// /// public void PrintStreamEvents(params string[] args) { P4PrintClientUser cu = new P4PrintClientUser(this, OnPrintStream, OnPrintEndFile); EstablishConnection(true); RunIt("print", args, cu); } #endregion #endregion #region Private Helper Methods private ClientApi _ClientAPI { get { if (m_ClientApi == null) { m_ClientApi = new ClientApi(); } //if (!_Initialized) throw new Exceptions.ServerNotConnected(); return m_ClientApi; } //set { m_ClientApi = value; } } private void EstablishConnection(bool tagged) { // If we're switching contexts from tagged to un-tagged, // then we need to reset the connection if ((m_ClientApi != null) && tagged != _tagged) { CloseConnection(); } if (m_ClientApi == null) { m_ClientApi = new ClientApi(); } if (m_ClientApi.Dropped()!=0) { // I can't figure out how to force this artificially, so currently untested :-( CloseConnection(); } if (!_Initialized) { Error err = null; try { _tagged = tagged; err = m_ClientApi.CreateError(); if (tagged) m_ClientApi.SetProtocol("tag", ""); if (tagged) m_ClientApi.SetProtocol("specstring", ""); //May have lost our settings... reset here if (_Client != null) m_ClientApi.SetClient(_Client); if (_User != null) m_ClientApi.SetUser(_User); if (_CWD != null) m_ClientApi.SetCwd(_CWD); if (_Charset != null) m_ClientApi.SetCharset(_Charset); if (_Host != null) m_ClientApi.SetHost(_Host); if (_Port != null) m_ClientApi.SetPort(_Port); if (_maxResults != 0) m_ClientApi.SetMaxResults(_maxResults); if (_maxScanRows != 0) m_ClientApi.SetMaxScanRows(_maxResults); if (_maxLockTime != 0) m_ClientApi.SetMaxLockTime(_maxLockTime); if (_ApiLevel != 0) m_ClientApi.SetProtocol("api", _ApiLevel.ToString()); m_ClientApi.Init(err); if (err.Severity == Error.ErrorSeverity.Failed || err.Severity == Error.ErrorSeverity.Fatal) { //Trace.WriteLine(err.Fmt()); //Trace.WriteLine("Unable to connect to Perforce!"); throw new Exception("Unable to connect to Perforce!"); } if (_CallingProgram != null) m_ClientApi.SetProg(_CallingProgram); if (_CallingProgramVersion != null) m_ClientApi.SetVersion(_CallingProgramVersion); _Initialized = true; err.Dispose(); } catch (Exception e) { //Trace.WriteLine(e.Message); m_ClientApi.Final(err); err.Dispose(); m_ClientApi.Dispose(); m_ClientApi = null; throw new PerforceInitializationError(e.Message); } } } private void CloseConnection() { // Need to reset the connection if (_Initialized) { Error err = m_ClientApi.CreateError(); m_ClientApi.Final(err); err.Dispose(); } if (m_ClientApi != null) { m_ClientApi.Dispose(); m_ClientApi = null; } _Initialized = false; } private void RunIt(string command, string[] args, ClientUser cu) { // validate that none of the args are null if (args == null) { throw new System.ArgumentNullException(); } foreach (string arg in args) { if (arg == null) { throw new System.ArgumentNullException(); } } cu.RunMergeTool = _runMergeTool; m_ClientApi.SetArgv(args); m_ClientApi.Run(command, cu); } private void HandleOnPrompt(object sender, P4PromptEventArgs e) { e.Response = RaiseOnPromptEvent(e.Message); } // Wrap event invocations inside a protected virtual method // to allow derived classes to override the event invocation behavior private string RaiseOnPromptEvent(string Message) { // Make a temporary copy of the event to avoid possibility of // a race condition if the last subscriber unsubscribes // immediately after the null check and before the event is raised. OnPromptEventHandler handler = OnPrompt; // Event will be null if there are no subscribers if (handler != null) { P4PromptEventArgs e = new P4PromptEventArgs(Message); // Use the () operator to raise the event. handler(this, e); return e.Response; } else { return string.Empty; } } #endregion #region IDisposable Members /// /// Calls Disconnect. /// public void Dispose() { this.Disconnect(); } #endregion } }