Unlist a package from Nuget with all it's history versions - visual-studio-2010

I'm looking for a simple solution, on how to Unlist (delete) a package from Nuget library with all of it's versions.
I'm asking this, because manually I can do this 1 version at a time for 1 package. And I have lots of packages with ~20+ versions each ..
Please help.

I know this is old, but the nuget "delete" command is still not implemented, so I needed a solution. Perhaps someone can still benefit from this. I created a Command-line program and added the Nuget package Headless. Then, the code is:
class Program
{
static void Main(string[] args)
{
List<PostEntry> parameters = new List<PostEntry>();
using (var browser = new Browser())
{
var loginPage = browser.GoTo<LoginPage>();
parameters.Clear();
parameters.Add(new PostEntry("SignIn.UserNameOrEmail", "yournugetusername"));
parameters.Add(new PostEntry("SignIn.Password", "yournugetpassword"));
parameters.Add(new PostEntry("ReturnUrl", "/"));
parameters.Add(new PostEntry("LinkingAccount", "false"));
parameters.Add(new PostEntry("__RequestVerificationToken", loginPage.Find<HtmlElement>().ById("signIn").Find<HtmlInput>().ByName("__RequestVerificationToken").Value));
browser.PostTo(parameters, new Uri("https://www.nuget.org/users/account/SignIn"));
Console.WriteLine("logged in");
var cont = true;
while (cont)
{
var packagesPage = browser.GoTo<PackagesPage>();
Console.WriteLine("at packages page");
var links = packagesPage.Find<HtmlElement>()
.ById("published")
.Find<HtmlLink>()
.AllByPredicate(o => o.GetAttribute("title") == "Delete"
&& (o.Href.Contains("/IdOfPackageIWantToDelete/")
|| o.Href.Contains("/IdOfAnotherPackageIWantToDelete/")));
cont = links.Count() >= 1;
if (links.Any())
{
var htmlLink = links.First();
var linkPage = htmlLink.Href;
Console.WriteLine(linkPage);
var deletePackagePage = htmlLink.Click();
parameters.Clear();
parameters.Add(new PostEntry("Listed", "false"));
parameters.Add(new PostEntry("__RequestVerificationToken", deletePackagePage.Find<HtmlElement>().ById("body").Find<HtmlInput>().ByName("__RequestVerificationToken").Value));
browser.PostTo(parameters, new Uri("https://www.nuget.org" + linkPage));
Console.WriteLine("delete submitted");
}
}
}
Console.WriteLine("Hit any key to quit");
Console.ReadKey();
}
public class LoginPage : HtmlPage
{
public override Uri TargetLocation
{
get { return new Uri("https://www.nuget.org/users/account/LogOn?returnUrl=%2F"); }
}
}
public class PackagesPage : HtmlPage
{
public override Uri TargetLocation
{
get { return new Uri("https://www.nuget.org/account/Packages"); }
}
}
}
It will take a little while to run, but it does the trick.

You could write a batch script to call nuget delete, iterating over all your packages - see http://docs.nuget.org/docs/reference/command-line-reference#Delete_Command
e.g.
nuget delete MyPackage 1.1 -NoPrompt
nuget delete MyPackage 1.2 -NoPrompt
etc. Pretty straightforward with grep or a search/replace in a text editor.

You can do this with PowerShell, here is a function:
function Remove-AllNuGetPackageVersions($PackageId, $ApiKey)
{
$lower = $PackageId.ToLowerInvariant();
$json = Invoke-WebRequest -Uri "https://api.nuget.org/v3-flatcontainer/$lower/index.json" | ConvertFrom-Json
foreach($version in $json.versions)
{
Write-Host "Unlisting $PackageId, Ver $version"
Invoke-Expression "nuget delete $PackageId $version $ApiKey -source https://api.nuget.org/v3/index.json -NonInteractive"
}
}

If you are hosting your own nuget gallery, you could execute the following SQL query against the nuget database:
select 'nuget delete ' + pr.Id + ' ' + p.Version + ' {ApiKey} -source {Source} -noninteractive' from Packages p
join PackageRegistrations pr
on p.PackageRegistrationKey = pr.[Key]
where pr.Id = '{PackageId}' AND
p.Listed = 1
Replace {ApiKey}, {Source} and {PackageId} with your own values, I then placed the results in a batch file adjacent to nuget.exe and executed.

You can use a combination of tools by creating a simple console application.
Create a new Console application and install the package "Nuget.Core".
Add the following method to get a list of all package versions:
private static IEnumerable<IPackage> GetListedPackages(string packageID)
{
var repo = PackageRepositoryFactory.Default.CreateRepository("https://packages.nuget.org/api/v2");
var packages = from package in repo.FindPackagesById(packageID)
where package.IsListed()
select package;
return packages;
}
Then copy nuget.exe from the ".nuget" folder in one of your projects into the console application project (add it in the solution explorer and make sure that it's copied to the output directory)
Next create a new method which uses nuget.exe to unlist the package version:
private static string UnlistPackage(IPackage package, string apiKey)
{
var arguments = $"delete {package.Id} {package.Version} -ApiKey {apiKey} -NonInteractive";
var psi = new ProcessStartInfo("nuget.exe", arguments)
{
RedirectStandardOutput = true,
WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory,
UseShellExecute = false
};
var process = Process.Start(psi);
process.WaitForExit();
return process.StandardOutput.ReadToEnd();
}
Finally change Main method so it gets a list and removes all versions:
private static void Main(string[] args)
{
var packageID = "yourPackageName";
var apiKey = "yourKeyFromNugetOrg";
var packages = GetListedPackages(packageID);
foreach (var package in packages)
{
Console.WriteLine("Unlisting package " + package);
var output = UnlistPackage(package, apiKey);
Console.WriteLine(output);
}
Console.Write("Completed. Press ENTER to quit.");
Console.ReadLine();
}
source: http://blog.gauffin.org/2016/09/how-to-remove-a-package-from-nuget-org/

Related

Error comparing current app version number with latest. Version name=production.release.1.0.0.5 and lastest version= [duplicate]

I need to check current version of Installed Application and Playstore application version. If it is not same app should navigate to Playstore .
im using xam.Plugin.LatestVersion(2.1.0) To get the latestversion number of application from play console. unfortunately not getting latest version number of application from play store. The below code im using.
private async void ChekAppVersion()
{
try
{
latestVersionNumber = await CrossLatestVersion.Current.GetLatestVersionNumber();
installedVersionNumber = CrossLatestVersion.Current.InstalledVersionNumber;
if (installedVersionNumber != latestVersionNumber)
{
await DisplayAlert("New Version", "There is a new version of this app available. Please update now?", "Ok");
await CrossLatestVersion.Current.OpenAppInStore();
ChekAppVersion();
}
else
{
}
}
catch (Exception ex)
{
}
}
Im getting the installedVersionNumber, but im unable to get the latestVersionNumber(Playstore).
Please help on this.
They have removed the version from div, now it's displayed with js, but data is still there inside a <script> tag. My current fixed code is:
private bool _lockCheckUpdates;
public async Task<bool> CheckNeedUpdate()
{
if (Connectivity.NetworkAccess != NetworkAccess.Internet || _lockCheckUpdates)
return false;
_lockCheckUpdates = true;
try
{
HttpClient myClient = CreateClient();
if (Device.RuntimePlatform == Device.Android)
{
var bundle = "com.todo.app"; //ANDROID BUNDLE NAME HERE
string url = $"https://play.google.com/store/apps/details?id={bundle}&hl=en";
string raw = await myClient.GetStringAsync(new Uri(url));
var doc = new HtmlDocument();
doc.LoadHtml(raw);
var scripts = doc.DocumentNode.Descendants()
.Where(n => n.Name == "script" && n.InnerText.Contains("AF_initDataCallback({key: 'ds:4'"))
.ToArray();
var script = scripts.First().InnerText;
var engine = new Jurassic.ScriptEngine();
var eval = "(function() { var AF_initDataCallback = function(p) { return p.data[1][2][140][0][0][0]; }; return " + script + " })()";
var result = engine.Evaluate(eval);
//var json = JSONObject.Stringify(engine, result); //for debug, check in browser console with JSON.parse(json)
var remote = $"{result}".ToDouble();
var local = App.Version.ToDouble();
return local < remote;
}
else if (Device.RuntimePlatform == Device.iOS)
{
var bundle = "com.todo.app";//iOS BUNDLE NAME HERE
string url = $"http://itunes.apple.com/lookup?bundleId={bundle}";
string raw = await myClient.GetStringAsync(new Uri(url));
var dto = JsonConvert.DeserializeObject<AppStoreRecord>(raw);
double local = App.Version.ToDouble();
if (dto.ResultCount > 0)
{
double remote = dto.Results[0].Version.ToDouble();
return remote > local;
}
}
}
catch (Exception e)
{
Logger.Error("CheckNeedUpdate", e);
}
finally
{
_lockCheckUpdates = false;
}
return false;
}
Using nugets
Jurassic to evaluate the script on page,
HtmlAgilityPack to parse html,
Xamarin.Essentials to check if we are online,
and AppoMobi.Specials for .ToDouble() etc
I hope this could also be useful to fix https://github.com/edsnider/latestversionplugin/issues/43 :)
The plugin you are using no longer works for Android https://github.com/edsnider/latestversionplugin/issues/43
You will need to find a new way to get the desired information.
PR has been made on this plugin... it works again ;-)

How to download a jar from artifactory with gradle to project directory?

What is the correct way to write a gradle task which downloads a .jar(or any other) file from artifactory to project directory(if file does not already exists)?
For example, I have a project with path /root/project with file /root/project/build.gradle inside. I want to download generator.jar from artifactory (com/company/project/generator/1.0) to /root/project directory if /root/project/generator.jar is not present.
tasks.register("downloadGeneratorJar") {
group = 'Download'
description = 'Downloads generator.jar'
doLast {
def f = new File('generator.jar')
if (!f.exists()) {
URL artifactoryUrl = new URL('myurl')
HttpURLConnection myURLConnection = (HttpURLConnection)artifactoryUrl.openConnection()
String userCredentials = gradle.ext.developerUser + ":" + gradle.ext.developerPassword
String basicAuth = "Basic " + new String(Base64.getEncoder().encode(userCredentials.getBytes()))
myURLConnection.setRequestProperty ("Authorization", basicAuth)
InputStream is = myURLConnection.getInputStream()
new FileOutputStream(f).withCloseable { outputStream ->
int read
byte[] bytes = new byte[1024]
while ((read = is.read(bytes)) != -1) {
outputStream.write(bytes, 0, read)
}
}
}
}
}

How can I create a MetadataWorkspace using metadata loading delegates?

I followed this example Changing schema name on runtime - Entity Framework where I can create a new EntityConnection from a MetaDataWorkspace that I then use to construct a DbContext with a different schema, but I get compiler warnings saying that RegisterItemCollection method is obsolete and to "Construct MetadataWorkspace using constructor that accepts metadata loading delegates."
How do I do that? Here is the code that is working but gives the 3 warnings for the RegsiterItemCollection calls. I'm surprised it works since warning says obsolete not just deprecated.
public static EntityConnection CreateEntityConnection(string schema, string connString, string model)
{
XmlReader[] conceptualReader = new XmlReader[]
{
XmlReader.Create(
Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(model + ".csdl")
)
};
XmlReader[] mappingReader = new XmlReader[]
{
XmlReader.Create(
Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(model + ".msl")
)
};
var storageReader = XmlReader.Create(
Assembly
.GetExecutingAssembly()
.GetManifestResourceStream(model + ".ssdl")
);
//XNamespace storageNS = "http://schemas.microsoft.com/ado/2009/02/edm/ssdl"; // this would not work!!!
XNamespace storageNS = "http://schemas.microsoft.com/ado/2009/11/edm/ssdl";
var storageXml = XElement.Load(storageReader);
foreach (var entitySet in storageXml.Descendants(storageNS + "EntitySet"))
{
var schemaAttribute = entitySet.Attributes("Schema").FirstOrDefault();
if (schemaAttribute != null)
{
schemaAttribute.SetValue(schema);
}
}
storageXml.CreateReader();
StoreItemCollection storageCollection =
new StoreItemCollection(
new XmlReader[] { storageXml.CreateReader() }
);
EdmItemCollection conceptualCollection = new EdmItemCollection(conceptualReader);
StorageMappingItemCollection mappingCollection =
new StorageMappingItemCollection(
conceptualCollection, storageCollection, mappingReader
);
//var workspace2 = new MetadataWorkspace(conceptualCollection, storageCollection, mappingCollection);
var workspace = new MetadataWorkspace();
workspace.RegisterItemCollection(conceptualCollection);
workspace.RegisterItemCollection(storageCollection);
workspace.RegisterItemCollection(mappingCollection);
var connectionData = new EntityConnectionStringBuilder(connString);
var connection = DbProviderFactories
.GetFactory(connectionData.Provider)
.CreateConnection();
connection.ConnectionString = connectionData.ProviderConnectionString;
return new EntityConnection(workspace, connection);
}
I was able to get rid of the 3 warning messages. Basically it wants you to register the collections in the constructor of the MetadataWorkspace.
There are 3 different overloads for MetadataWorkspace, I chose to use the one which requires to to supply a path (array of strings) to the workspace metadata. To do this I saved readers to temp files and reloaded them.
This is working for me without any warnings.
public static EntityConnection CreateEntityConnection(string schema, string connString, string model) {
var conceptualReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(model + ".csdl"));
var mappingReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(model + ".msl"));
var storageReader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(model + ".ssdl"));
XNamespace storageNS = "http://schemas.microsoft.com/ado/2009/11/edm/ssdl";
var storageXml = XElement.Load(storageReader);
var conceptualXml = XElement.Load(conceptualReader);
var mappingXml = XElement.Load(mappingReader);
foreach (var entitySet in storageXml.Descendants(storageNS + "EntitySet")) {
var schemaAttribute = entitySet.Attributes("Schema").FirstOrDefault();
if (schemaAttribute != null) {
schemaAttribute.SetValue(schema);
}
}
storageXml.Save("temp.ssdl");
conceptualXml.Save("temp.csdl");
mappingXml.Save("temp.msl");
MetadataWorkspace workspace = new MetadataWorkspace(new List<String>(){
#"temp.csdl",
#"temp.ssdl",
#"temp.msl"
}
, new List<Assembly>());
var connectionData = new EntityConnectionStringBuilder(connString);
var connection = DbProviderFactories.GetFactory(connectionData.Provider).CreateConnection();
connection.ConnectionString = connectionData.ProviderConnectionString;
return new EntityConnection(workspace, connection);
}
Not wanting to create temp files which slows the process down, I found an alternate answer to this is fairly simple. I replaced these lines of code -
//var workspace2 = new MetadataWorkspace(conceptualCollection, storageCollection, mappingCollection);
var workspace = new MetadataWorkspace();
workspace.RegisterItemCollection(conceptualCollection);
workspace.RegisterItemCollection(storageCollection);
workspace.RegisterItemCollection(mappingCollection);
with this one line of code -
var workspace = new MetadataWorkspace(() => conceptualCollection, () => storageCollection, () => mappingCollection);
and that works fine.

Transforming T4 from command line doesn't add generated files to csproj

I am trying to transform a T4 template from command line using TextTransform.exe with the following command line:
"%ProgramFiles(x86)%\Common Files\Microsoft Shared\TextTemplating\10.0\TextTransform.exe"
-out .\MyProj\MyT4.cs
-I "%ProgramFiles(x86)%\Microsoft Visual Studio 10.0\Common7\IDE\Extensions\Microsoft\Entity Framework Tools\Templates\Includes"
-a !NamespaceHint!MyNameSpace
-dp T4VSHost!Microsoft.Data.Entity.Design.VisualStudio.Directives.FallbackT4VSHostProcessor!"%ProgramFiles(x86)%\Microsoft Visual Studio 10.0\Common7\IDE\Microsoft.Data.Entity.Design.dll"
.\MyProj\MyT4.tt
Results:
No error messages
The %ERRORLEVEL% is 0 on completion.
The files are generated
The .csproj does not change
The problem is point 4. This may be expected, since the .csproj isn't a part of the above command line, however, I can't find any parameters which can accept it.
What am I doing wrong or what should I be doing instead?
P.S. When I use the button in Visual Studio the process works as excepted (new files are added to project).
Solved using the following method:
Added these parameters to command line:
-a !!ProjPath!.\MyProj\MyProj.csproj -a !!T4Path!.\MyProj\MyT4.tt
Changed the include directory parameter to a local path:
-I ".\Dependencies"
Copied EF.Utility.CS.ttinclude to that path and made the following changes:
3.1. Replaced:
public static EntityFrameworkTemplateFileManager Create(object textTransformation)
{
DynamicTextTransformation transformation = DynamicTextTransformation.Create(textTransformation);
IDynamicHost host = transformation.Host;
#if !PREPROCESSED_TEMPLATE
if (host.AsIServiceProvider() != null)
{
return new VsEntityFrameworkTemplateFileManager(transformation);
}
#endif
return new EntityFrameworkTemplateFileManager(transformation);
}
with
public static EntityFrameworkTemplateFileManager Create(object textTransformation)
{
DynamicTextTransformation transformation = DynamicTextTransformation.Create(textTransformation);
IDynamicHost host = transformation.Host;
#if !PREPROCESSED_TEMPLATE
if (host.AsIServiceProvider() != null)
{
return new VsEntityFrameworkTemplateFileManager(transformation);
}
#endif
return new EFTemplateFileManagerPlus(transformation);
}
(Last return has the change)
Add added this class to the file:
private sealed class EFTemplateFileManagerPlus : EntityFrameworkTemplateFileManager
{
private Action<IEnumerable<string>> projectSyncAction;
private readonly string _projPath;
private readonly string _t4Name;
public EFTemplateFileManagerPlus(object textTemplating)
: base(textTemplating)
{
var projPath = _textTransformation.Host.ResolveParameterValue("", "", "ProjPath");
var t4Path = _textTransformation.Host.ResolveParameterValue("", "", "T4Path");
_projPath = System.IO.Path.GetFullPath(projPath);
_t4Name = System.IO.Path.GetFileName(t4Path);
projectSyncAction = files => SyncCsProjFile(_projPath, _t4Name, files);
}
public static void SyncCsProjFile(string csProjFilePath, string t4FileName, IEnumerable<string> files)
{
files = files.Select(f => System.IO.Path.GetFileName(f)).Distinct().ToList();
var csProjDocument = new XmlDocument();
csProjDocument.Load(csProjFilePath);
var root = csProjDocument.DocumentElement;
XmlElement itemGroup = root.ChildNodes.OfType<XmlElement>()
.Where(n => n.Name == "ItemGroup")
.SelectMany(n => n.ChildNodes.OfType<XmlNode>()
.Where(c => c.Name == "Compile")
)
.Select(c => c.ParentNode)
.FirstOrDefault() as XmlElement;
if (itemGroup == null)
{
itemGroup = csProjDocument.CreateNode(XmlNodeType.Element, "ItemGroup", null) as XmlElement;
root.AppendChild(itemGroup);
}
var codeFiles = itemGroup.ChildNodes.OfType<XmlElement>()
.Where(c =>
c.Name == "Compile"
&& c.HasAttribute("Include") && !String.IsNullOrEmpty(c.GetAttribute("Include")))
.ToList();
var dependantFiles = codeFiles
.Where(f =>
f.ChildNodes.OfType<XmlElement>().Any(c =>
c.Name == "DependentUpon"
&& c.InnerText == t4FileName)
).ToList();
// Remove redundant files
foreach (var node in dependantFiles)
{
if (!files.Contains(node.GetAttribute("Include")))
itemGroup.RemoveChild(node);
}
// Add missing files
foreach (var name in files)
{
if (!dependantFiles.Any(node => node.GetAttribute("Include") == name))
{
var node = csProjDocument.CreateNode(XmlNodeType.Element, "Compile", null) as XmlElement;
node.SetAttribute("Include", name);
itemGroup.AppendChild(node);
var node2 = csProjDocument.CreateNode(XmlNodeType.Element, "DependentUpon", null) as XmlElement;
node2.InnerText = t4FileName;
node.AppendChild(node2);
}
}
SaveClean(csProjDocument, csProjFilePath);
}
static private void SaveClean(XmlDocument doc, string path)
{
StringBuilder sb = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.UTF8;
settings.Indent = true;
settings.IndentChars = " ";
settings.NewLineChars = "\r\n";
settings.NewLineHandling = NewLineHandling.Replace;
settings.NamespaceHandling = NamespaceHandling.OmitDuplicates;
using (XmlWriter writer = XmlWriter.Create(sb, settings))
{
doc.Save(writer);
}
var newXml = sb.ToString().Replace("encoding=\"utf-16\"", "encoding=\"utf-8\"").Replace(" xmlns=\"\"", string.Empty);
System.IO.File.WriteAllText(path, newXml, Encoding.UTF8);
}
public override IEnumerable<string> Process(bool split)
{
var generatedFileNames = base.Process(split);
projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(generatedFileNames, null, null));
return generatedFileNames;
}
}
Now the project file sync works using TextTransform.exe too.
I believe the command line host cannot change the .csproj. Only the VS host can do it, through the access to the DTE object.

How do I enumerate a list of Source Files with Pending Changes and get their server location?

I'm trying to write a macro that will generate a plain-text list of files changed based on the list of files in the Pending Changes pane but I can't figure out how to do it. The server location of a file is the property that is formatted like this:
$/TfsName/SomeSolution/Web/SomeFolder/SomeFile1.aspx
$/TfsName/SomeSolution/Web/SomeFolder/SomeFile2.aspx
The closest I can get is opening the properties of the selected item in the pane, which isn't very useful:
DTE.ExecuteCommand ("TeamFoundationContextMenus.SourceControlPendingChangesSourceFiles.TfsContextPendingCheckinsPendingCheckinsProperties")
Edit: here's the entire code for the macro I have so far, the TODOs are where I need help:
Public Class Pending
Public Shared Sub Pending()
OutputClear()
OutputWriteLine("Files Changed:")
Dim outInfo As String = ""
DTE.Windows.Item("{2456BD12-ECF7-4988-A4A6-67D49173F564}").Activate() 'Pending Changes - Source Files
'TODO: loop through each changed file
'TODO: get TFS server location of each file
outInfo &= "some file name"
OutputWriteLine(outInfo)
End Sub
' snip: other supporting functions
End Class
Well I haven't been able to figure out how to do it with a macro yet, but thanks to Bob Hardister on twitter, I can use this command to get what I'm looking for:
"C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\tf.exe" status $/ProjectName/SubDir/ /recursive
...but what works even better is a command-line app that uses this code:
const string TFSSERVER = "http://TfsServer:8080";
static void Main(string[] args)
{
//http://blogs.msdn.com/b/buckh/archive/2006/03/15/552288.aspx
//http://blogs.msdn.com/b/jmanning/archive/2005/12/01/499033.aspx
string projectName = args[0];
TeamFoundationServer tfs = new TeamFoundationServer(TFSSERVER);
VersionControlServer versionControl = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));
PendingSet[] sets = versionControl.GetPendingSets(new String[] { "$/Projects/" + projectName }, RecursionType.Full);
Console.WriteLine(versionControl.AuthenticatedUser + " pending changes for " + projectName + ":");
foreach (PendingSet set in sets)
{
if (set.Type == PendingSetType.Workspace && set.OwnerName == versionControl.AuthenticatedUser)
{
foreach (PendingChange pc in set.PendingChanges)
{
Console.WriteLine(pc.ServerItem);
}
}
}
}
Then I just added the compiled EXE call to the External Tools menu and use it within VS there.
Bonus Edit: Here's the VSS version (not as nice):
const string SSDIR = #"\\VssServer\VssShare";
static void Main(string[] args)
{
string projectName = args[0];
string userName = "user";
VSSDatabaseClass vss = new VSSDatabaseClass();
vss.Open(SSDIR + #"\srcsafe.ini", userName, userName);
VSSItem sourceItem = vss.get_VSSItem("$/Projects/" + projectName, false);
Console.WriteLine(userName + " pending checkins for " + projectName + ":");
int total = GetItems(sourceItem);
Console.WriteLine(total.ToString() + " total changes.");
}
const int VSSFILE_CHECKEDOUT_ME = 2;
const int VSSITEM_PROJECT = 0;
const int VSSITEM_FILE = 1;
public static int GetItems(IVSSItem originalItem)
{
int total = 0;
foreach (IVSSItem subItem in originalItem.get_Items(false))
{
if (subItem.Type == VSSITEM_FILE && subItem.IsCheckedOut == VSSFILE_CHECKEDOUT_ME)
{
Console.WriteLine(subItem.Spec);
total++;
}
else if (subItem.Type == VSSITEM_PROJECT)
{
total += GetItems(subItem);
}
}
return total;
}

Resources