<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1252" />
<meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5" />
<title>P4.Net: Getting Started</title>
<link rel="stylesheet" type="text/css" href="MSDN.css" />
</head>
<body id="bodyID" class="dtBODY">
<div id="nsbanner">
<div id="bannerrow1">
<table class="bannerparthead" cellspacing="0" id="Table1">
<tr id="hdr">
<td class="runninghead">
Perforce API for the .Net CLR</td>
<td class="product">
<img alt="P4.Net" src="p4net.GIF" /></td>
</tr>
</table>
</div>
<div id="TitleRow">
<h1 class="dtH1">
Getting Started</h1>
</div>
</div>
<div id="nstext">
<h4 class="dtH4">
Connecting to the Server</h4>
<p>
The P4Connection class is the main player in P4.Net. This represents a connection
to the Perforce server. Every utility that uses P4.Net will have some variation
of the following code:
</p>
<pre class="code" language="C#" escaped="true">
P4Connection p4 = new P4Connection();
p4.Connect();
// Run some commands
p4.Disconnect();</pre>
<p>
Rule number 1: Always remember to disconnect. This frees unmanaged memory, and cleanly
disconnects from the Perforce server. P4Connection implements IDisposable, and the
Dispose and Disconnect methods can be used interchangeably.
</p>
<p>
P4.Net is based off the command-line syntax (as are most other Perforce APIs). Almost
all of the commands you issue in P4.Net will use the same arguments as the p4 client
executable. For example, say you need to find the latest submitted changelist under
a given path (//depot/path). From the command line:</p>
<pre class="code" escaped="true">
c:\> p4 changes -m1 -s submitted //depot/path/...</pre>
<p>
From P4.Net:</p>
<pre class="code" language="C#" escaped="true">
P4Connect p4 = new P4Connection();
p4.Connect();
P4Recordset changes = p4.Run("changes", "-m1", "-s", "submitted", "//depot/path/...");
p4.Disconnect();</pre>
<p>
If you dont know what all the arguments for p4 changes mean, then p4 help changes
is your best friend. The first step in building anything with P4.Net, is to know
the exact command lines youd run manually.
</p>
<h4 class="dtH4">
Interpreting Output</h4>
<p>
Although the arguments to run Perforce commands are similar to the command line
interface, the way we interpret the output can be dramatically different. This is
where the power of the API comes in. In the example above, the result changes is
of type P4Recordset. This is a rich object in P4.Net that provides a parsed version
of the command output. At the core of the P4Recordset is the enumerable collection
of P4Records. Each P4Record generally represents all the data on one lines output
from the command-line. P4Records are dictionary-like objects allowing you to access
fields by a key:</p>
<pre class="code" language="C#" escaped="true">
// we used the -m1 switch, so we know there is just one record returned.
P4Record change = changes[0];
int changeNumber = int(change["change"]);</pre>
<p>
It can also be accessed using the Fields property:
</p>
<pre class="code" language="C#" escaped="true">
int changeNumber = int(change.Fields["change"]);</pre>
<p>
So, how do you know what keys are available? Well, it depends on the command run,
the server version, and sometimes the arguments to the command. The RecordsetViewer
sample application is a great tool for determining the keys that are returned from
a command. In addition to RecordsetViewer, you can use the -Ztag global option of
the p4 command line client to see how the output is parsed. At runtime, you can
access all of the keys returned from the Fields.Keys property:
</p>
<pre class="code" language="C#" escaped="true">
foreach (string key in change.Fields.Keys)
{
Console.WriteLine("{0} : {1}", key, change[key]);
}</pre>
<p>
In addition to the normal parsed output, there may be warnings, errors, and informational
messages from the command that are not parsed (i.e. it will be the same English
message returned at the command line). Again, the RecordsetViewer sample application
can help you identify all the elements returned in a P4Recordset for a particular
command. At runtime, we can access those messages as shown below:
</p>
<pre class="code" language="C#" escaped="true">
foreach (string e in changes.Errors) Console.WriteLine(e);
foreach (string e in changes.Warnings) Console.WriteLine(e);
foreach (string e in changes.Messages) Console.WriteLine(e);</pre>
<p>
Not all fields have a single string for a value. Some commands return arrays of
strings in a field. To access these values at runtime, you can use the ArrayFields
property of the P4Record object.
</p>
<pre class="code" language="C#" escaped="true">
P4Recordset describes = p4.Run("describe", "-s", "1234");
//One changelist, one record... at least if that changelist exists
P4Record describe = describes[0];
Console.WriteLine("Changelist: {0}", describe["Change"]);
Console.WriteLine("Files:");
foreach( int i=0; i< describe.ArrayFields["depotFile"].Length; i++)
{
Console.WriteLine(" {0}#{1}", describe.ArrayFields["depotFile"][i], describe.ArrayFields["rev"][i]);
}</pre>
<p>
In the preceding examples, we only looked at the first record in the recordset.
However many commands will return multiple records. We can enumerate them as follows:
</p>
<pre class="code" language="C#" escaped="true">
// This example enumerates all the files in a folder hierarchy, and prints the ones
// that are deleted at the head revision. (Note, theres a more efficient way to do this.)
P4Recordset fstats = p4.Run("fstat", "//depot/path/...");
foreach( P4Record stat in fstats)
{
if (stat["headAction"] == "delete")
{
Console.WriteLine(stat["depotFile"]);
}
}</pre>
<h4 class="dtH4">
Unparsed Output</h4>
<p>
Not all commands support the parsed output. In that case, all of the output will
be simple English statements, just as the command line outputs. Again, this is highly
dependent on the version of the Perforce server. You can see the Perforce C++ API
release notes for your version to see if the parsed output is supported for a given
command (referred to as tagged in the C++ documentation).
</p>
<p>
While you can access these messages from the P4Recordset.Messages property, its
often easier to use the RunUnParsed method of the P4Connection class, which returns
a P4UnParsedRecordset object. This is similar to the P4Recordset object, except
there are no Fields and ArrayFields properties, and the default enumerator is the
Messages array.
</p>
<p>
RunUnParsed can also help ensure forward compatibility with newer server versions.
In recent releases, many commands that previously only supported the unparsed output,
now support parsed output. If the server is upgraded, code that called Run, would
still look at the P4Recordset.Messages. However the output from the upgraded server
would now be available only in Fields and ArrayFields. By using RunUnParsed, you
are guaranteed to get the raw strings from the server.
</p>
<p />
<h4 class="dtH4">
Forms</h4>
<p>
Another major object in P4.Net is the P4Form object. Perforce forms are the text
files that pop up in an editor when a "form" command is run. They have fields that
are specially formatted in the file, and you can view or change these fields by
following the formatting standards. You can see this behavior in the command line:
</p>
<pre class="code" escaped="true">
c:\> p4 user</pre>
<p>
In P4.Net, you do not have to worry about this special formatting. The fields are
read/modified using Fields and ArrayFields properties (P4Form inherits P4Record).
The only trick is you need to use the methods Fetch_Form and Save_Form:</p>
<pre class="code" language="C#" escaped="true">
// Change the root of the current workspace.
P4Connection p4 = new P4Connection();
p4.Connect();
P4Form client = p4.Fetch_Form("client");
client["Root"] = @"c:\p4";
p4.Save_Form(client);
p4.Disconnect();</pre>
<p />
<p />
<h4 class="dtH4">
Submitting Files</h4>
<p>
In P4.Net, theres no straight-forward way to submit the default pending changelist.
This is by design. If the client workspace has opened files in the default changelist
before any P4.Net automation runs, those files will "come along for the ride" when
you submit the default changelist. If the user has a JobView set, all jobs in that
JobView will automatically be fixed when you submit the default changelist. Both
of those behaviors are almost never desired, and Ive found many scripts that have
those bugs.
</p>
<p>
So, how do you submit files in P4.Net? The key is to create a named pending changelist
before opening any files. Then add the switches "-c", and "1234" (1234 is the changelist
number) to all commands that are opening files. This is quite simple using the P4PendingChangelist
object:
</p>
<pre class="code" language="C#" escaped="true">
P4Connection p4 = new P4Connection();
p4.Connect();
P4PendingChangelist cl = p4.CreatePendingChangelist("My New Changelist\nVery, Very bad description!\nShame on me!");
p4.Run("edit", "-c", cl.Number.ToString(), "//depot/path/foo.cs", "//depot/path/bar.cs");
// Do something to manipulate the files
cl.Submit();
p4.Disconnect();</pre>
<h4 class="dtH4">
Connections Revisited.</h4>
<p>
Finally, lets look at the connection properties. You may be wondering how the P4Connection
object knows which Port/Client/User to use. It turns out P4.Net will use the default
properties using the same logic as all Perforce clients (see Technote 36 and the
P4 User Guide). Of course, any of these can be explicitly overridden on the P4Connection
object. However, Ive found using the built-in Perforce configuration system to
be much more portable and maintainable than explicitly defining the configuration
for each custom tool.
</p>
<hr />
<div id="footer">
<p>
<a href="Copyright.html">Copyright 2006 Shawn Hladky</a>
</p>
<p>
</p>
</div>
</div>
</body>
</html>