/******************************************************************************* Copyright (c) 2010, Perforce Software, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *******************************************************************************/ /******************************************************************************* * Name : P4Server.cs * * Author : dbb * * Description : Classes used to wrap calls in the P4Bridge DLL in C#. * ******************************************************************************/ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Text; using System.IO; using System.Timers; using System.Threading; namespace Perforce.P4 { /// /// P4Server /// /// Represents the connection to a Perforce Server using the the P4 Bridge /// DLL. It wraps the calls exported by the DLL and transforms the data /// types exported by the DLL as native C#.NET data types. /// public partial class P4Server : IDisposable { internal object Sync = new object(); internal P4ClientErrorList _errorList = null; /// /// The errors (if any) of the command execution /// public P4ClientErrorList ErrorList { get { if (ConnectionError != null) { return new P4ClientErrorList(ConnectionError); } return _errorList; } internal set { _errorList = value; } } /// /// The results of the last command executed /// public P4CommandResult LastResults { get; internal set; } /// /// Get the version of the p4.net assembly /// public static System.Version Version { get { return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; } } /// /// Get the error message generated by the previous connection (if any) /// public static P4ClientError ConnectionErrorInt { get { IntPtr pErr = P4Bridge.GetConnectionError(); if (pErr != null) return new P4ClientError( pErr ); return null; } } /// /// Get the error message generated by the previous connection (if any) /// from the bridge dll. /// public P4ClientError ConnectionError { get; private set; } private void LogBridgeMessage( int log_level, String file, int line, String message ) { // remove the full path to the source, keep just the file name String fileName = Path.GetFileName( file ); string category = String.Format( "P4Bridge({0}:{1}", file, line ); LogFile.LogMessage( log_level, category, message ); } private string _server; private string _user; private string _pass; private string _ws_client; private string _prog_name; private string _prog_ver; private string _cwd; /// /// Create a P4BridgeServer used to connect to the specified P4Server /// /// Host:port for the P4 server. /// User name for the login. /// Can be null/blank if only running commands that do not require /// a login. /// Password for the login. Can be null/blank if /// only running commands that do not require a login. /// Workspace (client) to be used by the /// connection. Can be null/blank if only running commands that do /// not require a login. public P4Server(String server, String user, String pass, String ws_client) { _server = server; _user = user; _pass = pass; _ws_client = ws_client; CurrentEncodeing = P4Encoding.ASCII; P4CallBacks.LogMessageDelegate logfn = new P4CallBacks.LogMessageDelegate(LogBridgeMessage); // encode the username, password, and workspace name in UTF-8, we // won't know if the client supports Unicode until after connect // returns using (PinnedByteArray pUser = MarshalStringToIntPtr(user), pClient = MarshalStringToIntPtr(ws_client)) { IntPtr pLogFn = IntPtr.Zero; if (logfn != null) pLogFn = Marshal.GetFunctionPointerForDelegate(logfn); pServer = P4Bridge.ConnectW(server, pUser, IntPtr.Zero, pClient, pLogFn); } if (pServer == IntPtr.Zero) { ConnectionError = ConnectionErrorInt; if (ConnectionError == null) { IntPtr pObj = P4Bridge.GetErrorResults(pServer); _errorList = new P4ClientErrorList(this, pObj); if ((_errorList != null) && (_errorList.Count > 0)) { P4Exception.Throw(_errorList, GetInfoResults()); } ConnectionError = new P4ClientError(ErrorSeverity.E_FATAL, string.Format("Unknown error connecting to server, {0}", _server)); } P4Exception.Throw(ConnectionError); return; //throw new ApplicationException(connectionError); } else { ConnectionError = null; } if (isUnicode = P4Bridge.IsUnicode(pServer)) { CurrentEncodeing = P4Encoding.utf8; SetCharacterSet(Charset[(int)CurrentEncodeing], null); //Charset[(int)P4Encoding.utf16bom]); } apiLevel = P4Bridge.APILevel(pServer); requiresLogin = P4Bridge.UseLogin(pServer); if ((!requiresLogin) && (!string.IsNullOrEmpty(pass))) { using (PinnedByteArray pPass = MarshalStringToIntPtr(pass)) { P4Bridge.set_passwordW(pServer, pPass); } } // Link the callbacks from the bridge dll to their corresponding events SetInfoResultsCallback(); SetTaggedOutputCallback(); SetErrorCallback(); SetTextResultsCallback(); SetBinaryResultsCallback(); SetPromptCallback(); SetResolveCallback(); SetResolveACallback(); // If we were supplied a password, login into the server. If the // server requires a login (security level >= 3), this will prompt // for the password. If login is not required, the command will just // return with a result saying that login is not required. //Login(pass, null); } /// /// Create a P4BridgeServer used to connect to the specified P4Server /// /// Host:port for the P4 server. /// User name for the login. /// Can be null/blank if only running commands that do not require /// a login. /// Password for the login. Can be null/blank if /// only running commands that do not require a login. /// Workspace (client) to be used by the /// connection. Can be null/blank if only running commands that do /// not require a login. public P4Server(String server, String user, String pass, String ws_client, String trust_flag, String fingerprint) { _server = server; _user = user; _pass = pass; _ws_client = ws_client; CurrentEncodeing = P4Encoding.ASCII; P4CallBacks.LogMessageDelegate logfn = new P4CallBacks.LogMessageDelegate(LogBridgeMessage); // encode the username, password, and workspace name in UTF-8, we // won't know if the client supports Unicode until after connect // returns using (PinnedByteArray pUser = MarshalStringToIntPtr(user), pClient = MarshalStringToIntPtr(ws_client)) { IntPtr pLogFn = IntPtr.Zero; if (logfn != null) pLogFn = Marshal.GetFunctionPointerForDelegate(logfn); pServer = P4Bridge.TrustedConnectW(server, pUser, IntPtr.Zero, pClient, trust_flag, fingerprint, pLogFn); } if (pServer == IntPtr.Zero) { ConnectionError = ConnectionErrorInt; if (ConnectionError == null) { IntPtr pObj = P4Bridge.GetErrorResults(pServer); _errorList = new P4ClientErrorList(this, pObj); if ((_errorList != null) && (_errorList.Count > 0)) { P4Exception.Throw(_errorList, GetInfoResults()); } ConnectionError = new P4ClientError(ErrorSeverity.E_FAILED, string.Format("Unknown error connecting to server, {0}", _server)); } P4Exception.Throw(ConnectionError); return; //throw new ApplicationException(connectionError); } else { ConnectionError = null; } if (isUnicode = P4Bridge.IsUnicode(pServer)) { CurrentEncodeing = P4Encoding.utf8; SetCharacterSet(Charset[(int)CurrentEncodeing], Charset[(int)P4Encoding.utf16bom]); } apiLevel = P4Bridge.APILevel(pServer); requiresLogin = P4Bridge.UseLogin(pServer); if ((!requiresLogin) && (!string.IsNullOrEmpty(pass))) { using (PinnedByteArray pPass = MarshalStringToIntPtr(pass)) { P4Bridge.set_passwordW(pServer, pPass); } } // Link the callbacks from the bridge dll to their corresponding events SetInfoResultsCallback(); SetTaggedOutputCallback(); SetErrorCallback(); SetTextResultsCallback(); SetBinaryResultsCallback(); SetPromptCallback(); SetResolveCallback(); SetResolveACallback(); // If we were supplied a password, login into the server. If the // server requires a login (security level >= 3), this will prompt // for the password. If login is not required, the command will just // return with a result saying that login is not required. //Login(pass, null); } /// /// Run a login command on the server /// /// User's password /// Success/Failure /// /// If the server requires a login (security level >= 3), this will /// prompt for the password. If login is not required, the command will /// just return with a result saying that login is not required. /// public bool Login(string password, StringList options) { if (!requiresLogin) { // server does not support login command if (!string.IsNullOrEmpty(password)) { using (PinnedByteArray pPass = MarshalStringToIntPtr(password)) { P4Bridge.set_passwordW(pServer, pPass); } return true; } return false; } P4Command login = new P4Command(this, "login", false); login.Responses = new Dictionary(); login.Responses["DefaultResponse"] = password; bool results = false; try { results = login.RunInt(options); } catch //(Exception ex) { results = false; } if (!results) { ConnectionError = _errorList[0]; } else { ConnectionError = null; } return results; } /// /// Run a logout command on the server /// /// The -a flag invalidates the ticket on the server. /// Success/Failure /// /// If the server requires a login (security level >= 3), this will /// logout the user and remove the local ticket. /// public bool Logout(StringList options) { P4Command logout = new P4Command(this, "logout", false); bool results = false; try { results = logout.RunInt(options); } catch //(Exception ex) { results = false; } if (!results) { ConnectionError = _errorList[0]; } else { ConnectionError = null; } return results; } /// /// Finalizer /// ~P4Server() { Dispose(false); } bool _disposed = false; public void Dispose(bool disposing) { if (_disposed) { return; } if (!disposing) { } Close(); TaggedOutputCallbackFn_Int = null; pTaggedOutputCallbackFn = IntPtr.Zero; P4Bridge.SetTaggedOutputCallbackFn(pServer, IntPtr.Zero); ErrorCallbackFn_Int = null; pErrorCallbackFn = IntPtr.Zero; P4Bridge.SetErrorCallbackFn(pServer, IntPtr.Zero); InfoResultsCallbackFn_Int = null; pInfoResultsCallbackFn = IntPtr.Zero; P4Bridge.SetInfoResultsCallbackFn(pServer, IntPtr.Zero); TextResultsCallbackFn_Int = null; pTextResultsCallbackFn = IntPtr.Zero; P4Bridge.SetTextResultsCallbackFn(pServer, IntPtr.Zero); BinaryResultsCallbackFn_Int = null; pBinaryResultsCallbackFn = IntPtr.Zero; P4Bridge.SetBinaryResultsCallbackFn(pServer, IntPtr.Zero); PromptCallbackFn_Int = null; pPromptCallbackFn = IntPtr.Zero; P4Bridge.SetPromptCallbackFn(pServer, IntPtr.Zero); _disposed = true; } #region IDisposable Members /// /// For IDispose /// public void Dispose() { //LogOut(); Dispose(true); GC.SuppressFinalize(this); } #endregion /// /// Reconnect to the server in the event the connection is lost /// public void Reconnect() { lock (this) { if (pServer != IntPtr.Zero) { P4Bridge.CloseConnection(pServer); pServer = IntPtr.Zero; } CurrentEncodeing = P4Encoding.ASCII; P4CallBacks.LogMessageDelegate logfn = new P4CallBacks.LogMessageDelegate(LogBridgeMessage); // encode the username, password, and workspace name in UTF-8, we // won't know if the client supports Unicode until after connect // returns using (PinnedByteArray pUser = MarshalStringToIntPtr(_user), pPass = MarshalStringToIntPtr(_pass), pClient = MarshalStringToIntPtr(_ws_client)) { IntPtr pLogFn = IntPtr.Zero; if (logfn != null) pLogFn = Marshal.GetFunctionPointerForDelegate(logfn); pServer = P4Bridge.ConnectW(_server, pUser, pPass, pClient, pLogFn); } if (pServer == IntPtr.Zero) { ConnectionError = ConnectionErrorInt; P4Exception.Throw(ConnectionError); return; //throw new ApplicationException(connectionError); } else { ConnectionError = null; } if (isUnicode = P4Bridge.IsUnicode(pServer)) { CurrentEncodeing = P4Encoding.utf8; SetCharacterSet(Charset[(int)CurrentEncodeing], Charset[(int)P4Encoding.utf16bom]); } requiresLogin = P4Bridge.UseLogin(pServer); // Link the callbacks from the bridge dll to their corresponding events SetInfoResultsCallback(); SetTaggedOutputCallback(); SetErrorCallback(); SetTextResultsCallback(); SetBinaryResultsCallback(); SetPromptCallback(); SetResolveCallback(); SetResolveACallback(); CurrentWorkingDirectory = _cwd; ProgramName = _prog_name; ProgramVersion = _prog_ver; // *** Don't login using the login command. If we are reconnecting // *** after a timeout, doe not want to risk the login timing out and // *** throwing us in an infinite loop // We've theoretically already logged in, so if using tickets, _pass // should hold the ticket. //Login(_pass, null); } } /// /// Close the connection to a P4 Server /// /// /// Called by the Dispose() method /// public void Close() { lock (this) { if (pServer != IntPtr.Zero) { P4Bridge.CloseConnection(pServer); pServer = IntPtr.Zero; } } } /// /// Need to use Unicode when marshalling to/from the P4 server /// public bool UseUnicode { get { return isUnicode; } } /// /// What API level does the server support /// public int ApiLevel { get { return apiLevel; } } /// /// The server requires a client to use the login command to pass credentials. /// public bool ReqiresLogin { get { return requiresLogin; } } private System.Timers.Timer disconnectTrigger = null; private void OnDisconnectTrigger(object source, ElapsedEventArgs e) { P4Bridge.Disconnect(pServer); } private class RunCommandThreadParam { public string cmd; public bool tagged; public String[] args; public int argc; public bool Results; } /// /// Maximum time for a command to run before timing out; /// public TimeSpan RunCmdTimout = TimeSpan.FromSeconds(30); private int RunCmdCallbackCnt = 0; private DateTime RunCmdLastContact = DateTime.MinValue; private Exception RunCmdException = null; private void RunCommandThreadProc(object param) { RunCommandThreadParam CmdParams = param as RunCommandThreadParam; RunCmdException = null; if (pServer == IntPtr.Zero) { CmdParams.Results = false; RunCmdException = new P4Exception(ErrorSeverity.E_FATAL, "pServer is null"); return; } if (CmdParams == null) { RunCmdException = new ArgumentNullException("CmdParams"); return; } #if DEBUG_TIMEOUT if (CmdParams.cmd == "TimeOutTest") { double timeout = RunCmdTimout.TotalSeconds; double delta = 0; if ((CmdParams.argc > 0) && (CmdParams.args.Length >= 1)) { double.TryParse(CmdParams.args[0], out delta); timeout += delta; } Thread.Sleep(TimeSpan.FromSeconds(timeout)); CmdParams.Results = true; return; } else if (CmdParams.cmd == "LongTimeOutTest") { int timeout = (int) RunCmdTimout.TotalSeconds; int delta = 0; if ((CmdParams.argc > 0) && (CmdParams.args.Length >= 1)) { int.TryParse(CmdParams.args[0], out delta); delta *= 1000; } DateTime endTime = DateTime.Now + TimeSpan.FromSeconds(timeout*2); while (DateTime.Now < endTime) { Thread.Sleep(delta); InfoResultsCallback_Int(0, IntPtr.Zero); } CmdParams.Results = true; return; } #endif try { if (!isUnicode) { CmdParams.Results = P4Bridge.RunCommandA(pServer, CmdParams.cmd, CmdParams.tagged, CmdParams.args, CmdParams.argc); } else { using (PinnedByteArrays args_b = MarshalStringArrayToIntPtrArray(CmdParams.args, CmdParams.argc)) { CmdParams.Results = P4Bridge.RunCommandW(pServer, CmdParams.cmd, CmdParams.tagged, (IntPtr[])args_b, CmdParams.argc); } } } catch (ThreadAbortException) { return; } catch (Exception ex) { RunCmdException = ex; } return; } /// /// Run a P4 command on the P4 Server /// /// /// If the command fails, the error output will contain one or more /// errors generated by the P4 server. /// /// Command code /// Use tagged output for the results /// Arguments for the command /// Argument count /// Success/Failure public bool RunCommand( string cmd, bool tagged, String[] args, int argc) { lock (this) { if (_disposed) { throw new P4.P4Exception(ErrorSeverity.E_FATAL, "trying to run a command on a disposed server"); } if (disconnectTrigger != null) { disconnectTrigger.Stop(); } bool results = false; try { if (RunCmdTimout > TimeSpan.Zero) { RunCommandThreadParam CmdParams = new RunCommandThreadParam(); CmdParams.Results = false; CmdParams.cmd = cmd; CmdParams.tagged = tagged; CmdParams.args = args; CmdParams.argc = argc; ParameterizedThreadStart ts = new ParameterizedThreadStart(RunCommandThreadProc); Thread RunCommandThread = new Thread(ts); RunCommandThread.IsBackground = true; RunCommandThread.Start(CmdParams); RunCmdLastContact = DateTime.Now; RunCmdCallbackCnt = 0; while (RunCommandThread.Join(TimeSpan.FromSeconds(1)) == false) { if ((DateTime.Now - RunCmdLastContact) > RunCmdTimout) { CancelCommand(); if (RunCommandThread.Join(TimeSpan.FromSeconds(1)) == false) { RunCommandThread.Abort(); } else { P4Bridge.Disconnect(pServer); } string msg = string.Format("Command time out: {0} ", cmd); foreach (string arg in args) { if (arg != null) { msg += " " + arg; } } throw new P4CommandTimeOutException(ErrorSeverity.E_FATAL, msg); } } results = CmdParams.Results; } else { if (!isUnicode) { results = P4Bridge.RunCommandA(pServer, cmd, tagged, args, argc); } else { using (PinnedByteArrays args_b = MarshalStringArrayToIntPtrArray(args, argc)) { results = P4Bridge.RunCommandW(pServer, cmd, tagged, (IntPtr[])args_b, argc); } } } if (!results) { // error IntPtr pObj = P4Bridge.GetErrorResults(pServer); if (pObj == IntPtr.Zero) { // no errors from command, so check for a connection error ConnectionError = ConnectionErrorInt; if (ConnectionError == null) { P4Exception.Throw(ErrorSeverity.E_FATAL, "Unknown Problem, can't continue"); } else { P4Exception.Throw(ConnectionError); } } else { _errorList = new P4ClientErrorList(this, pObj); P4Exception.Throw(_errorList, GetInfoResults()); } return false; } else { // may be some warnings in the list, so fetch it if it is not null IntPtr pObj = P4Bridge.GetErrorResults(pServer); _errorList = null; if (pObj != IntPtr.Zero) { _errorList = new P4ClientErrorList(this, pObj); } ConnectionError = null; } } finally { if (disconnectTrigger == null) { disconnectTrigger = new System.Timers.Timer(5000); disconnectTrigger.AutoReset = false; disconnectTrigger.Elapsed += new ElapsedEventHandler(OnDisconnectTrigger); } disconnectTrigger.Start(); } return results; } } /// /// Cancel a running command /// public void CancelCommand() { P4Bridge.CancelCommand(pServer); } /// /// Delegate used to send tagged output as it is generated. /// /// /// This delegate will send a complete object after all of its fields /// have been received by the callback from the bridge dll. /// /// /// public delegate void TaggedOutputDelegate( int ObjId, TaggedObject Obj); /// /// Event to broadcast tagged output /// public event TaggedOutputDelegate TaggedOutputReceived; /// /// Get the tagged output generated by a command /// /// A list of TaggedObjects comprising the tagged output. /// public TaggedObjectList GetTaggedOutput() { IntPtr pData = P4Bridge.GetTaggedOutput( pServer ); if (pData == IntPtr.Zero) { return null; } TaggedObjectList objects = new TaggedObjectList(); // use a StrDictListIterator to return all of the objects and // their keys. StrDictListIterator data = new StrDictListIterator(this, pData); while (data.NextItem()) { TaggedObject currentObject = new TaggedObject(); objects.Add(currentObject); KeyValuePair kv = null; while( ( kv = data.NextEntry() ) != null ) { currentObject[kv.Key] = kv.Value; } } return objects; } /// /// Delegate used to send errors as they are generated. /// /// /// This delegate will send a block of data for each call received by /// the callback from the bridge dll. /// /// Severity of the error /// Error message public delegate void ErrorDelegate( int severity, String data ); /// /// Holds the call back passed to the bridge used to receive the /// raw data /// P4CallBacks.ErrorDelegate ErrorCallbackFn_Int = null; /// /// Broadcast errors received /// public event ErrorDelegate ErrorReceived; /// /// Get a list of errors (if any) generated by a command /// /// A list of P4ClientErrors, null if no errors public P4ClientErrorList GetErrorResults() { IntPtr pErr = P4Bridge.GetErrorResults( pServer ); if( pErr != IntPtr.Zero ) { return new P4ClientErrorList(this, pErr); } return null; } /// /// Delegate used to send Info Results as they are generated. /// /// /// This delegate will send a block of data for each call received by /// the callback from the bridge dll. /// /// Server supplied message level /// Server supplied message data public delegate void InfoResultsDelegate( int level, String data ); /// /// Broadcast event for info results /// public event InfoResultsDelegate InfoResultsReceived; /// /// Get the information messages generated by the previous command /// /// /// Each message is formatted as follows /// l:Message text /// where l is a single digit representing the message level /// /// List of messages public String[] GetInfoResults() { IntPtr pInfoOut = P4Bridge.GetInfoResults( pServer ); String info = MarshalPtrToString( pInfoOut ); if( String.IsNullOrEmpty( info ) ) return null; return info.Split( new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries ); } /// /// Delegate used to send Text Results as they are generated. /// /// /// This delegate will send a block of data for each call received by /// the callback from the bridge dll. /// /// Text results generated by the command public delegate void TextResultsDelegate(String data); /// /// Broadcast event for text results /// public event TextResultsDelegate TextResultsReceived; /// /// Get the complete text results for the last command /// /// public String GetTextResults() { IntPtr pTextOut = P4Bridge.GetTextResults( pServer ); return MarshalPtrToString( pTextOut ); } /// /// Delegate used to send binary output as it is generated. /// /// /// This delegate will send a block of data for each call received by /// the callback from the bridge dll. /// /// Binary data generated by a command public delegate void BinaryResultsDelegate( byte[] data ); /// /// Broadcast event for binary data /// public event BinaryResultsDelegate BinaryResultsReceived; /// /// Get the complete binary results for the last command /// /// The binary data public byte[] GetBinaryResults() { int byteCount = P4Bridge.GetBinaryResultsCount( pServer ); if( byteCount <= 0 ) return null; IntPtr pData = P4Bridge.GetBinaryResults( pServer ); if( pData == IntPtr.Zero ) return null; return MarshalPtrToByteArrary( pData, byteCount ); } /// /// Delegate used to commands as they are executed. /// /// Command line executed by the command public delegate void CommandEchoDelegate(String data); /// /// Broadcast event for text results /// public event CommandEchoDelegate CommandEcho; /// /// Broadcast a the command line (cmd and args) on the CommandEcho event /// /// /// Used to echo an executed command line back to the client /// /// The P4 command. /// The flags and parameters for the command. public void EchoCommand(string cmd, StringList args) { if (CommandEcho != null) { string commandLine = cmd; if (args != null) { for (int idx = 0; idx < args.Count; idx++) { if (args[idx] != null) { commandLine += " " + args[idx]; } } } CommandEcho(commandLine); } } /// /// Broadcast a string on the CommandEcho event /// /// /// Used to echo command data back to the client /// /// The string. public void EchoCommand(string str) { if (CommandEcho != null) { CommandEcho(str); } } /// /// The data set for use by a command /// /// /// If a command requires data not passed on the command line, such as /// a client spec, it is passed to the P$ server by setting the data /// set in the P4 api. /// public String DataSet { set { if( isUnicode ) { using( PinnedByteArray pData = MarshalStringToIntPtr( value ) ) { P4Bridge.SetDataSetW( pServer, pData ); } } else { P4Bridge.SetDataSetA( pServer, value ); } } get { IntPtr pData = P4Bridge.GetDataSet( pServer ); return MarshalPtrToString( pData ); } } /// /// Delegate used to provide a custom handler for input prompts from the p4api. /// /// /// /// public delegate String PromptHandlerDelegate(String msg, bool displayText); /// /// Delegate used to process prompts for input from the server. /// public PromptHandlerDelegate PromptHandler; /// /// Delegate used to provide a custom handler for Resolve callbacks passing a ClientMerge object from the p4api. /// /// public delegate P4ClientMerge.MergeStatus ResolveHandlerDelegate(P4ClientMerge Merger); /// /// Delegate used to provide a custom handler for Resolve callbacks passing a ClientMerge object from the p4api. /// public ResolveHandlerDelegate ResolveHandler; /// /// Delegate used to provide a custom handler for Resolve callbacks passing a ClientResolve object from the p4api. /// /// public delegate P4ClientMerge.MergeStatus ResolveAHandlerDelegate(P4ClientResolve Resolver); /// /// Delegate used to provide a custom handler for Resolve callbacks passing a ClientResolve object from the p4api. /// public ResolveAHandlerDelegate ResolveAHandler; /// /// The parameters used by the connection /// /// /// The properties, client, port, user, and password, /// represent the criteria used to connect to a P4 server. If one or /// more is changed, the bridge will drop the current connection if any /// and attempt to connect to the (possibly different) P4 server when /// the next command is executed. If it is desirable to validate the /// connection, execute a command. /// public void SetConnectionData(string port, string user, string password, string client) { _ws_client = client; if (isUnicode) { using (PinnedByteArray pPort = MarshalStringToIntPtr(port), pUser = MarshalStringToIntPtr(user), pPassword = MarshalStringToIntPtr(password), pClient = MarshalStringToIntPtr(client)) { P4Bridge.set_connectionW(pServer, pPort, pUser,pPassword, pClient); } } else { P4Bridge.set_connectionA(pServer, port, user, password, client); } } /// /// The client workspace used by the connection /// /// /// The properties, client, port, user, and password, /// represent the criteria used to connect to a P4 server. If one or /// more is changed, the bridge will drop the current connection if any /// and attempt to connect to the (possibly different) P4 server when /// the next command is executed. If it is desirable to validate the /// connection, execute a command. /// public String Client { get { IntPtr pval = P4Bridge.get_client( pServer ); return MarshalPtrToString( pval ); } set { _ws_client = value; if( isUnicode ) { using( PinnedByteArray pData = MarshalStringToIntPtr( value ) ) { P4Bridge.set_clientW( pServer, pData ); } } else { P4Bridge.set_clientA( pServer, value ); } } } /// /// The user name used by the connection /// /// /// The properties, client, port, user, and password, /// represent the criteria used to connect to a P4 server. If one or /// more is changed, the bridge will drop the current connection if any /// and attempt to connect to the (possibly different) P4 server when /// the next command is executed. If it is desirable to validate the /// connection, execute a command. /// public String User { get { IntPtr pval = P4Bridge.get_user( pServer ); return MarshalPtrToString( pval ); } set { _user = value; if( isUnicode ) { using( PinnedByteArray pData = MarshalStringToIntPtr( value ) ) { P4Bridge.set_userW( pServer, pData ); } } else { P4Bridge.set_userA( pServer, value ); } } } /// /// The hostname:port used by the connection /// /// /// The properties, client, port, user, and password, /// represent the criteria used to connect to a P4 server. If one or /// more is changed, the bridge will drop the current connection if any /// and attempt to connect to the (possibly different) P4 server when /// the next command is executed. If it is desirable to validate the /// connection, execute a command. /// public String Port { get { IntPtr pval = P4Bridge.get_port( pServer ); return MarshalPtrToString( pval ); } set { _server = value; if( isUnicode ) { using( PinnedByteArray pData = MarshalStringToIntPtr( value ) ) { P4Bridge.set_portW( pServer, pData ); } } else { P4Bridge.set_portA( pServer, value ); } } } /// /// The user's password used by the connection /// /// /// The properties, client, port, user, and password, /// represent the criteria used to connect to a P4 server. If one or /// more is changed, the bridge will drop the current connection if any /// and attempt to connect to the (possibly different) P4 server when /// the next command is executed. If it is desirable to validate the /// connection, execute a command. /// public String Password { get { IntPtr pval = P4Bridge.get_password(pServer); return MarshalPtrToString(pval); } set { _pass = value; if (isUnicode) { using (PinnedByteArray pData = MarshalStringToIntPtr(value)) { P4Bridge.set_passwordW(pServer, pData); } } else { P4Bridge.set_passwordA(pServer, value); } } } /// /// The program name used by the connection /// /// /// The program name and version are recorded in the server logs when /// accessed by the client /// public String ProgramName { get { IntPtr pval = P4Bridge.get_programName(pServer); return MarshalPtrToString(pval); } set { _prog_name = value; if (isUnicode) { using (PinnedByteArray pData = MarshalStringToIntPtr(value)) { P4Bridge.set_programNameW(pServer, pData); } } else { P4Bridge.set_programNameA(pServer, value); } } } /// /// The program version used by the connection /// /// /// The program name and version are recorded in the server logs when /// accessed by the client /// public String ProgramVersion { get { IntPtr pval = P4Bridge.get_programVer(pServer); return MarshalPtrToString(pval); } set { _prog_ver = value; if (isUnicode) { using (PinnedByteArray pData = MarshalStringToIntPtr(value)) { P4Bridge.set_programVerW(pServer, pData); } } else { P4Bridge.set_programVerA(pServer, value); } } } /// /// The current working directory (cwd) used by the p4 server /// /// /// The properties, client, port, user, and password, /// represent the criteria used to connect to a P4 server. If one or /// more is changed, the bridge will drop the current connection if any /// and attempt to connect to the (possibly different) P4 server when /// the next command is executed. If it is desirable to validate the /// connection, execute a command. /// public String CurrentWorkingDirectory { get { IntPtr pval = P4Bridge.get_cwd(pServer); return MarshalPtrToString(pval); } set { _cwd = value; if (isUnicode) { using (PinnedByteArray pData = MarshalStringToIntPtr(value)) { P4Bridge.set_cwdW(pServer, pData); } } else { P4Bridge.set_cwdA(pServer, value); } } } /// /// The character set used by the connection /// /// /// The character set used to connect to Unicode servers is set by the /// bridge dll automatically (possibly overridden by P4CHARSET) based /// on the current Windows code page. /// public String CharacterSet { get { IntPtr pval = P4Bridge.get_charset(pServer); return Marshal.PtrToStringAnsi(pval); } } /// /// The config file used by the connection /// public String Config { get { IntPtr pval = P4Bridge.get_config(pServer); return MarshalPtrToString(pval); } } } }