PowerShell Script for Recreating the Server Cache (related to error in Windows Event Log, Event ID 6482: Application Server Administration job failed for service instance Microsoft.Office.Server.Search.Administration.SearchServiceInstance)

Today I’ve had the error:

Application Server Administration job failed for service instance Microsoft.Office.Server.Search.Administration.SearchServiceInstance (150CA1DD-02E3-47C0-AA55-005A2927751F).

Reason: An update conflict has occurred, and you must re-try this action. The object SearchDataAccessServiceInstance was updated by DOMAINspfarm, in the OWSTIMER (5040) process, on machine sps2010. 
View the tracing log for more information about the conflict.

Technical Support Details:
Microsoft.SharePoint.Administration.SPUpdatedConcurrencyException: An update conflict has occurred, and you must re-try this action.
The object SearchDataAccessServiceInstance was updated by domainuser, in the OWSTIMER (5040) process, on machine (server name). 
View the tracing log for more information about the conflict.
   at Microsoft.Office.Server.Search.Administration.SearchServiceInstance.Synchronize()
   at Microsoft.Office.Server.Administration.ApplicationServerJob.ProvisionLocalSharedServiceInstances(Boolean isAdministrationServiceJob)

 

There is a nice article on Jeff DeVerter’s blog:

Jeff references to this articles:

I had to clean up lots of servers. Because of that I’ve created a little PowerShell script.

The script will automatically process all steps descripted by the KB article, Chaitu Madala and Jeff.

You can execute the script on every server.

If your receive a yellow message at the end of the process that the resulting “cache.ini” file is diffrent from the original one. I think the cache.ini file contains a timestamp and it is OK if the new timestamp value is greater than the original one… But you use the script at your own rist – as always.

 

SCRIPT:

# Script created by Ingo Karstein (https://blog.kenaro.com)
Write-Host "Script created by Ingo Karstein (https://blog.kenaro.com)" -ForegroundColor DarkGreen

$cacheFolderRoot = Join-Path ${env:ProgramData} "MicrosoftSharePointConfig"
$cacheFileCopy = Join-Path ${env:temp} "cacheFileTmp.ini.ik"

Write-Host "" -ForegroundColor DarkBlue
Write-Host "Starting" -ForegroundColor DarkBlue

if( Test-Path $cacheFileCopy -PathType Leaf) {
    write-host "Cache File copy lopcation does already exist. Please remove it: $($cacheFileCopy)" -ForegroundColor Red
    return
}

Write-Host "Looking for cache folder" -ForegroundColor DarkBlue
$cacheFolder =  @(GEt-ChildItem -Path $cacheFolderRoot | ? {Test-Path $_.FullName -PathType Container} | ? {Test-Path -Path (Join-Path $_.FullName "cache.ini") -PathType Leaf})

if( $cacheFolder -ne $null -and $cacheFolder.Count -eq 1) {
    $cacheFolder0 = $cacheFolder[0].FullName
    Write-Host "Cache folder found: $($cacheFolder0)" -ForegroundColor DarkBlue
    $cacheFile = join-path $cacheFolder0 "cache.ini"
    Write-Host "Cache ini file: $($cacheFile)" -ForegroundColor DarkBlue

    Write-Host "Stop SharePoint timer service" -ForegroundColor DarkBlue
    stop-service sptimerv4

    Write-Host "Copy cache.ini to it's temp location ($($cacheFileCopy))" -ForegroundColor DarkBlue
    copy-item -path $cacheFile -Destination $cacheFileCopy -force

    Write-Host "Set the content of cache.ini to ""1""" -ForegroundColor DarkBlue
    "1" | set-content $cacheFile -encoding ascii -force

    Write-Host "Remove all .XML files from the cache folder" -ForegroundColor DarkBlue
    get-childitem $cacheFolder0 -filter "*.xml" | remove-item -force -confirm:$false

    Write-Host "Start SharePoint timer service" -ForegroundColor DarkBlue
    start-service sptimerv4

    $xmlFound = -1
    $xmlFoundLast = -1
    $eqCount = 0

    Write-Host "Now the cache will be recreated... Waiting... This will take a minute or so..." -ForegroundColor DarkBlue
    write-host "(every second a dot will appear on the end of the next line)" -ForegroundColor gray
    do {
        write-host "." -nonewline -ForegroundColor Magenta
        start-sleep -second 1
        $xmlFoundLast = $xmlFound
        $xmlFound = (@(get-childitem $cacheFolder0 -filter "*.xml")).Count
        if( $xmlFound -eq $xmlFoundLast ) {
            $eqCount++
        } else {
            $eqCount = 0
        }
    } while ($eqCount -lt 30)
    
    Write-Host ""
    Write-Host "Done" -ForegroundColor DarkBlue

    $a = get-content $cacheFileCopy
    $b = get-content $cacheFile

    if( $a -ne $b ) {
        if( [int]$a -gt [int]$b ) {
            write-host "An error occured. the content of the cache file is not identically to it's value before processing." -ForegroundColor Red
            write-host "Old: $($a)"
            write-host "New: $($b)"    
        } else {
            write-host "MAYBE an error occured. the content of the cache file is not identically to it's value before processing." -ForegroundColor DarkYellow
            write-host "Old: $($a)"
            write-host "New: $($b)"    
        }
    } else {
        write-host "Processing finished successfully! You need to execute the  script on every server!!" -foregroundcolor darkgreen
        remove-item $cacheFileCopy -Force -confirm:$false
    }
} else {
    write-host "Could not find the cache folder or found too much cache folders!" -foregroundcolor red
}

Deploying “Fantastic Fourty“ templates of MOSS 2007 on SharePoint 2010

There are the “Fantastic Fourty” templates you all now… They are create for MOSS 2007 and there are no update available for SharePoint 2010… Because of a migration of MOSS 2007 to SharePoint 2010 I need to deploy some of the site definitions to SharePoint 2010 to get the “old” sites working…

Do you know you can publish the Fantastic-40 on SharePoint 2010? – Here is a nice article about that: http://marijnsomers.blogspot.com/2010/06/fantastic-40-templates-on-sharepoint.html

On this page you’ll find updates solution packages: http://techsolutions.net/Blog/tabid/65/EntryId/17/Fab-40-Templates-for-MOSS-2010.aspx

 

BUT…

After deployment of the base solution “ApplicationTemplateCore.wsp” this is what I liked to do:

1. Create a Site Collection

image

2. Enter some data and choose the “Custom” category for the site template. Click “Select template later”…

SNAGHTMLc2b02f

3. Now create the site and open them. You get:

image

4. Now open the “Site Settings” from the “Site Actions” menu.

5. Click “Site Collection Features”.

6. Try to activate feature “Fields and Content Types for the Windows SharePoint Services Application Templates”

image

7. You get:

image

 

But as I tried to deploy and activate them I got this error:

  

Server Error in '/' Application.
--------------------------------------------------------------------------------

The field with Id {46b00cd2-bc51-45d8-87e1-68f1be35275f} defined in feature {75a0fea7-cd50-401e-af0e-782f3662a299} was found in the current site collection or in a subsite. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: Microsoft.SharePoint.SPException: The field with Id {46b00cd2-bc51-45d8-87e1-68f1be35275f} defined in feature {75a0fea7-cd50-401e-af0e-782f3662a299} was found in the current site collection or in a subsite.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below. 

 

Stack Trace:

[SPException: The field with Id {46b00cd2-bc51-45d8-87e1-68f1be35275f} defined in feature {75a0fea7-cd50-401e-af0e-782f3662a299} was found in the current site collection or in a subsite.]

   Microsoft.SharePoint.Utilities.SPUtility.ThrowSPExceptionWithTraceTag(UInt32 tagId, ULSCat traceCategory, String resourceId, Object[] resourceArgs) +27772295   

Microsoft.SharePoint.Administration.SPElementDefinitionCollection.ProvisionFieldsAndContentTypes(SPFeaturePropertyCollection props, SPSite site, SPWeb web, Boolean fForce) +23652209   

Microsoft.SharePoint.Administration.SPElementDefinitionCollection.ProvisionElements(SPFeaturePropertyCollection props, SPWebApplication webapp, SPSite site, SPWeb web, Boolean fForce) +138   

Microsoft.SharePoint.SPFeature.Activate(SPSite siteParent, SPWeb webParent, SPFeaturePropertyCollection props, Boolean fForce) +25437423   

Microsoft.SharePoint.SPFeatureCollection.AddInternal(SPFeatureDefinition featdef, Version version, SPFeaturePropertyCollection properties, Boolean force, Boolean fMarkOnly) +27496895   

Microsoft.SharePoint.SPFeatureCollection.AddInternalWithName(Guid featureId, String featureName, Version version, SPFeaturePropertyCollection properties, Boolean force, Boolean fMarkOnly, SPFeatureDefinitionScope featdefScope) +150   

Microsoft.SharePoint.SPFeatureCollection.Add(Guid featureId, Boolean force, SPFeatureDefinitionScope featdefScope) +83   

Microsoft.SharePoint.WebControls.FeatureActivator.ActivateFeature(Guid featid, SPFeatureDefinitionScope featdefScope) +699   

Microsoft.SharePoint.WebControls.FeatureActivatorItem.BtnActivateFeature_Click(Object objSender, EventArgs evtargs) +140   

System.Web.UI.WebControls.Button.OnClick(EventArgs e) +115    System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +140   

System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +29    System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2981

 

Okay… This is the solution that I’ve developed some minutes ago.

1. You need to decompress the “ApplicationTemplateCore.WSP” file. – It’s nothing else than a CAB file renamed to WSP. – Use 7zip to decompress it.

2. Open the file <fantastic40-folder>ApplicationTemplateCoreTSATypesFields.xml. Search for 46b00cd2-bc51-45d8-87e1-68f1be35275f and remove one of the double entries of this field definition:

SNAGHTML5a2ab1a

image

(Remove the code in the red frame.)

3. By the way: This is not the only bug in the WSP file. But for now we go ahead… We need to recreate the WSP file.

This is not easy.

a) First download “DDFGenerator.exe” from http://ddfgenerator.codeplex.com/

b) Open a command line prompt.

c) Type the path to “DDFGenerator.exe” and specify the path of the WSP content as parameter.

image

d) In the WSP content folder you’ll find a file named “solution.ddf”. Edit this file in Notepad. Insert “.Set CabinetNameTemplate=ApplicationTemplateCore.WSP” as 5th line. – Save and close the file.

image

e) Go into the folder and execute “MakeCab.exe /F solution.ddf”

SNAGHTMLda5e2a

Now you have a new WSP file in a sub folder of the WSP content folder.

image

4. Now run a solution upgrade.

5. Try to activate the feature again. You get another error:

image

The field with Id {26118c12-ea11-48ae-950b-a7219a9738f8} defined in feature {75a0fea7-cd50-401e-af0e-782f3662a299} was found in the current site collection or in a subsite. 

6. If your search for GUID “26118c12-ea11-48ae-950b-a7219a9738f8” you’ll find another field that is deployed twice:

<Field ID="{26118C12-EA11-48ae-950B-A7219A9738F8}" Name="TextFileName" DisplayName="$Resources:core,Name;" Type="Text" Group="_Hidden" Sealed="TRUE" AllowDeletion="FALSE"></Field>

…in file <fantastic40-folder>ApplicationTemplateCoreTSATypesFields.xml

Here you need to delete one of the entries…

7. The next try also results in an error:

image

The field with Id {ebb85cf8-047c-4ba7-9fd6-01bfa5ef126d} defined in feature {75a0fea7-cd50-401e-af0e-782f3662a299} was found in the current site collection or in a subsite.

8. Search for GUID “ebb85cf8-047c-4ba7-9fd6-01bfa5ef126d”. – Here it’s more complexe to resolve this issue. Because there are to diffrent fields with the same ID!

image

(It’s file <fantastic40-folder>ApplicationTemplateCoreTSATypesFields.xml again!)

You need to create a new ID for one of the field.

I’ve choosen “93B5756C-727F-4C4B-A081-E8654A234739” as new ID for field “AssetPurchasePrice”. You can use this GUID too!

Now you need to change every reference to field “AssetPurchasePrice” in all files of the project. Here is a list of files:

  • <fantastic40-folder>ApplicationTemplateCoreTSATypesFields.xml
  • <fantastic40-folder>ApplicationTemplateCoreTSATypesCTypes.xml
  • <fantastic40-folder>ApplicationTemplateCoreLendingLibraryListAssetsschema.xml
  • <fantastic40-folder>ApplicationTemplateCoreInventoryTrackingInventoryListInventoryschema.xml
  • <fantastic40-folder>ApplicationTemplateCoreAssetTrackingProposalsListProposalsschema.xml
  • <fantastic40-folder>ApplicationTemplateCoreAssetTrackingAssetsListAssetsschema.xml

Be sure to replace the ID of all references of “AssetPurchasePrice” with the new ID! Don’t touch the references of the other field “AssetDateRetired” with the same ID. Do not use Search and Replace to replace the GUID.

A sample for file <fantastic40-folder>ApplicationTemplateCoreAssetTrackingAssetsListAssetsschema.xml after replacing the ID of “AssetPurchasePrice”:

image

(In the red box I’ve replaced the ID. In the green oval I’ve left the ID untouched!)

BTW: The GUID of “AssetDateRetired” is “EBB85CF8-047C-4ba7-9FD6-01BFA5EF126D”: There are lower case letters and upper case letters in the GUID. I think they are edited by hand by someone and that’s the reason of the error…

9. Save your work and repack the solution as described above and deploy the package to SharePoint.

10. Now I’m able to activate the feature “Fields and Content Types for the Windows SharePoint Services Application Templates”:

image

12. That’s it so far.

13. Now I’d like to use a Fantastic-40 template. In my case “Helpdesk”. I deployed the “Helpdesk.WSP” of the Fantastic-40 bundle to SharePoint 2010. – Without any error.

14. Click the homepage of your site and you’ll reach the “Template Picker” application page if you did not select a template for the site before like me.

image

15. Now I have a working “Helpdesk” site – as far as I can see.

image

PowerShell Tool to Enumerate the Content Type Usage in SharePoint 2010

A customer of mine has the problem that he wants to remove a Web Content Type but SharePoint says the content type is still in use.

Therefore I created a tool for enumerating all web and list content types of all sites.

The following script scans the SharePoint farm and creates a Windows form with a Treeview like this:

image

(Web) = Web Content Types

(List) = List Content Types

The tool show the content type inheritence:

image

Here you see the List Content Type “Image” that based on the Web Content Type “Image” that based on the Web Content Type “Rich Media Asset” that based on Web Content Type “Document” that is inherited from the “System” Content Type.

It’s possible to open the content type management site by clicking “_open management site” or open the related list or web by clicking “_open list” or “_open web”.

This is the script:

#region Init
    Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
    [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint") | Out-Null
    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")  | Out-Null
    cls
#endregion

#region Form
    $form = New-Object System.Windows.Forms.Form

    $button = New-Object System.Windows.Forms.Button
    $treeview = New-Object System.Windows.Forms.TreeView

    $form.SuspendLayout()

    $button.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
    $button.Dock = [System.Windows.Forms.DockStyle]::Bottom
    $button.Location = (new-object System.Drawing.Point(0, 250))
    $button.Name = "button1"
    $button.Size = (new-object System.Drawing.Size(292, 23))
    $button.TabIndex = 0
    $button.Text = "Close"
    $button.UseVisualStyleBackColor = $true

    $treeview.Dock = [System.Windows.Forms.DockStyle]::Fill
    $treeview.Location = (new-object System.Drawing.Point(0, 0))
    $treeview.Name = "treeView1"
    $treeview.Size = (new-object System.Drawing.Size(292, 250))
    $treeview.TabIndex = 1

    $form.AutoScaleDimensions = new-object System.Drawing.SizeF(6.0, 13.0)
    $form.AutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Font
    $form.ClientSize = (new-object System.Drawing.Size(292, 273))
    $form.Controls.Add($button)
    $form.Controls.Add($treeview)
    $form.Name = "Content Type Usage "
    $form.Text = "Content Type Usage / https://blog.kenaro.com"
    $form.ResumeLayout($false)
#endregion

$list = @{}

$treeview.add_NodeMouseDoubleClick({
    param($sender, $eventArgs)
    if($eventArgs.Node.Tag -ne $null ) {
        [System.Diagnostics.Process]::Start($eventArgs.Node.Tag)
    }
});

function analyseWeb {
    param([Microsoft.SharePoint.SPWeb]$web) 
    
    $web.ContentTypes | % {
        $ct = $_
        $global:list += @{"$($ct.Id)"=$ct}
    }
    
    $web.lists | % {
        $_.ContentTypes | % {
            $ct = $_
            $global:list += @{"$($ct.Id)"=$ct}
        }
    }
    
    $web.Webs | % {
        analyseWeb -web $_
    }
}

function reorder {
    param([Microsoft.SharePoint.SPContentType]$p = $null, [int]$intend, [System.Windows.Forms.TreeNode]$parentNode)
    
    if( $p -eq $null ) {
        $ct = $list["0x"]

        $tn = New-Object System.Windows.Forms.TreeNode("System")
        $tn.ToolTipText = "0x"
        $parentNode.Nodes.Add($tn) | Out-Null
        
        reorder -p $ct -intend 0 -parentNode $tn
    } else {
        ($list.Values | ? {$_.Id.IsChildOf($p.Id) -and $_ -ne $p} | sort @{Expression={$_.Id.ToString().Length}}, $_.Id.ToString() ) | % {
            $ct = $_
            $type=""
            $url=""
            $tn = $null

            if( $ct.ParentList -ne $null ) {
                $type="(List) $($ct.Name) / List: $($ct.ParentList.Title) / Id: $($ct.Id.ToString())"            
                $url = $ct.parentweb.url + "/_layouts/ManageContentType.aspx?ctype=" + $ct.Id.ToString()+"&List="+$ct.ParentList.Id.ToString()
                
                #http://<domain>/sites/<web>/_layouts/ManageContentType.aspx?List=%7B111963EB%2DF504%2D49EC%2DA21B%2D814319718684%7D&ctype=0x010100644EE9F5F657474B92A2716207BB5DE2
                
                $tn = New-Object System.Windows.Forms.TreeNode($type)
                $tn1 = New-Object System.Windows.Forms.TreeNode("_open management site")
                $tn1.Tag = $url
                $tn.Nodes.Add($tn1) | Out-Null
                
                $tn2 = New-Object System.Windows.Forms.TreeNode("_open list")
                $tn2.Tag = $ct.ParentWeb.Site.MakeFullUrl($ct.ParentList.DefaultViewUrl)
                $tn.Nodes.Add($tn2) | Out-Null
            }else {
                $type="(Web) $($ct.Name) / Id: $($ct.Id.ToString())"
                $url = $ct.parentweb.url + "/_layouts/ManageContentType.aspx?ctype=" + $ct.Id.ToString()
            
                #http://<domain>/sites/<web>/_layouts/ManageContentType.aspx?ctype=0x0100CF8FCF53E9FA451CB145093F830B1762
                $tn = New-Object System.Windows.Forms.TreeNode($type)
                $tn1 = New-Object System.Windows.Forms.TreeNode("_open management site")
                $tn1.Tag = $url

                $tn.Nodes.Add($tn1) | Out-Null
                $tn2 = New-Object System.Windows.Forms.TreeNode("_open web")
                $tn2.Tag = $ct.ParentWeb.Url
                $tn.Nodes.Add($tn2) | Out-Null
            }

            $parentNode.Nodes.Add($tn) | Out-Null
        
            reorder -p $ct -intend ($intend+1) -parentNode $tn
        }
    }
}

if($fileName -ne $null ) { 
    Remove-Item -Path $fileName -Confirm:$false -ErrorAction SilentlyContinue
}

#you can filter the processed site collections in the following line
Get-SPSite -Limit all <#| select -first 1 -Skip 10#> | % {
    $site = $_
    $tn = New-Object System.Windows.Forms.TreeNode("Site: $($site.Url)")
    $treeview.Nodes.Add($tn)
    
    $rootWeb = $site.RootWeb
    
    analyseWeb -web $rootWeb
    
    reorder -parentNode $tn
    
    $rootWeb.Dispose()
    $site.Dispose()
}

$form.ShowDialog() | out-null

“SPWebPSConsole”: Web based PowerShell Console for SharePoint (A new project on Codeplex)

This project is a stand-alone clone of my project „Web based PowerShell Console” released on 2011-09-02 on Codeplex and in this blog article:

https://blog.kenaro.com/2011/09/02/webpsconsole-web-based-powershell-console-a-new-project-on-codeplex/

I’ve adapted the project to run in the SharePoint 2010 Central Administration.

The project is on Codeplex: http://spwebpsconsole.codeplex.com

To install the project just by deploying the solution. The SharePoint feature will automatically be activated in the Central Administration Web Application without need of further actions.

In the “System Settings” section of the CA you’ll find a new link:

image

Just click it and you will get this:

image

image

The SharePoint PowerShell SnapIn is loaded automatically so that you can start immediately to execute SharePoint Cmdlets!

Here is an example:

“Get-SPSite”

image

Or this one:

image

Request for value for missing parameter “Path” of Cmdlet “Backup-SPSite”

image

Final output:

image

The project is “BETA”. I need your help to improve the project. Please feel free to test it or to extend it. But if you do so please send me your results!

Have fun!

“LongPathSupport”: Operate with files and directories with “long paths” in .NET (CodePlex Project)

In my project RoboPowerCopy (http://robopowercopy.codeplex.com) I’ve implemented a basic set of classes and methods for use with files and directories with long paths. NTFS supports paths with up to 32000 characters. But .NET only supports 256 characters. So System.IO.FileInfo and System.IO.DirectoryInfo will not work with such a file:

c:usersikarsteinappdatalocal111112222233333444445555566666777778888899999000001111122222333334444455555666667777788888999990000011111222223333344444555556666677777888889999900000111112222233333444445555566666777778888899999000001111122222333334444455555666667777788888999990000011111222223333344444555556666677777888889999900000111112222233333444445555566666777778888899999000001111122222333334444455555666667777788888999990000011111222223333344444555556666677777888889999900000111112222233333444445555566666777778888899999000001111122222333334444455555666667777788888999990000011111222223333344444555556666677777888889999900000hello.txt

This path has 655 characters. No way in .NET without using the Win32 API.

I’ve wrapped the API calls into a set of basic .NET classes to provide a basic support for files and directors with long paths…

Here it is: http://longpathsupport.codeplex.com

Please feel free to contribute the project or post you experiences on my blog or on Codeplex!

For the project I’ve read/used some other source in the Internet. Especially this three I’d like to refer to:

Walkthrough: Deploy ClickOnce Application as SharePoint 2010 Solution Package

In some projects there was a need to run code on the client machine for interaction with SharePoint. If’ve realized this kind of applications as “ClickOnce” apps. It’s possible to deploy them as “SharePoint Solution Package”. If you do so the ClickOnce files can be deployed to every WFE. Updating the ClickOnce is easy.

Here I want to show you how to deploy a ClickOnce app als SharePoint Solution.

Let’s start.

1. Create your ClickOnce app. – If you have an existing one skip to step 5. – Otherwise continue reading. I’ll show you how to create a very simply ClickOnce.

Open  Visual Studio 2010. Create a new project of type “Windows Forms Application” or “WPF Application”. I’ll use the first one and name the project “MyClickOnce”.

image

Design you app. – I drag 2 Label controls, 2 Checkbox controls and 2 Textbox controls to the surface. I do not change their names but their fonts Smile

image

In my app I’d like to show the “running context”: locally started EXE or online started ClickOnce. Furthermore I’d like to show the URL if started online.

2. Open the Code view of the form.

First of all you need to add a assembly reference for “System.Web”!

Then insert this code. Maybe you need to correct the name of the controls.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections.Specialized;
using System.Deployment.Application;
using System.Web;

namespace MyClickonce
{
    public partial class Form1 : Form {
        public Form1()
        {
            InitializeComponent();

            ParseParams();

            checkBox1.Checked = !IsOnline;
            checkBox2.Checked = IsOnline;
            textBox1.Text = Url;
            textBox2.Text = Parameters["Param1"];
        }

        private bool _isOnline = false;
        private NameValueCollection parameters = null;
        private string _url = "";

        public bool IsOnline
        {
            get {
                return _isOnline;
            }
        }

        public string Url
        {
            get {
                return _url;
            }
        }

        public NameValueCollection Parameters
        {
            get {
                return parameters;
            }
        }

        private void ParseParams()
        {
            NameValueCollection nameValueTable = new NameValueCollection();

            if( ApplicationDeployment.IsNetworkDeployed )
            {
                _isOnline = true;
                _url = ApplicationDeployment.CurrentDeployment.ActivationUri.ToString();

                string queryString = ApplicationDeployment.CurrentDeployment.ActivationUri.Query;
                parameters = HttpUtility.ParseQueryString(queryString);
            }
            else {
                _isOnline = false;
                parameters = HttpUtility.ParseQueryString(string.Join("&", Environment.GetCommandLineArgs()));
            }
        }
    }
}

The method “ParseParams” will be useful while developing the app and later running the app: This method is able to parse both “command line parameter” sources: URL and native (EXE). During development you maybe need to pass parameters to the app for testing purpose. Than you define this parameters in the “Debug” tab of the project properties.

Here is a screenshot of the app’s debug settings:

image

You see there is a parameter “Param1” follwed by “=” and it’s value. This is similar to URL style query string parameters. But you do not separate them with “&”! Instead use a single whitespace. – Later in the URL you will use “&” as normal!

Now just run the app!

image

Nice, isn’t it?

3. Now you need to configure the application to become a ClickOnce.

Open the Project Properties of your Windows Form App project.

Select the “Signing” tab and create a self signed certificate. Or use a valid Code Signature certificate.

image

Now switch to the “Publish” tab of the project settings.

Here you can change the settings as you need it. – I’ll leave it as is EXCEPT one setting: Click the “Options…” button and select “Manifests”. Check “Allow URL parameters to be passed to application”:

image

[If you do not set this the app will fail in the following line because “ActivationUri” will be null: “_url = ApplicationDeployment.CurrentDeployment.ActivationUri.ToString(); “]

The other settings:

image

Click “Publish Now”.

Than the project will be build and stored to the sub folder “Publish” of your project folder in the file system.

image

4. If you now try to run the app locally you may get this error:

image

This is caused by your ClickOnce publishing in the step before.

The resolve this open the project settings, select the “Security” tab and clear the checkbox “Enable Clickonce security settings”:

image

This you need to do after each ClickOnce publishing!

5. Now we create the SharePoint Solution Package.

In the Solution Explorer click on the solution item, click “Add” and click “New project”.

image

Now chooce “Empty SharePoint Project” and name it “MyClickonceDeployment”.

As local site I use “http://sharepoint.local/sites/clickonce”. It has to be a “Farm Solution”!!

image

Now create a “Module” project item. Name it “MyClickonce”.

SNAGHTMLacb2fc

Remove the file “Sample.txt”

image

6. Now open the solutions path in Windows Explorer.

Go into the folder of the Windows Forms Application. There go into the “Publish” folder.

Select the “.application” file of your ClickOnce app and select the “Application Files” folder. This items you need to copy. Just select “Copy” from the context menu or press Ctrl+C.

No open the folder of “MyClickonceDeployment” and go into the folder “MyClickonce”. There insert (paste) the selected items.

Now you folder should look like this:

image

In the “Application Files” folder you’ll see another folder “1_0_0_0” or with another version number. This version number will be increased by every ClickOnce publishing if you did not disable this function.

7. Back in the Visual Studio go into the Solution Explorer and click this icon:

image

This will show you all files in the project folder, not even project items.

Select the “MyClickonce” module project item. Maybe you need to click the icon:

image

You should see this:

image

Right click on “MyClickonce.application” and select “Include in project”. Right click on “MyClickonce_1_0_0_0” and select “Include in project”.

Now you need to edit the “Elements.xml” file of you module project item.

First of all add an attribute named “Path” to the “Module” tag. This will define the URL of your ClickOnce app. Select a unique name so that the solution won’t get in conflict with other solutions. You could choose a GUID here or use date and time:

image

Now you can remove the string “MyClickonce/” of the beginning of every “Path” attribute of the child nodes of the “Module” tag.

image

After removing the string the file should have this content:

<?xml version="1.0" encoding="utf-8"?> <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <Module Name="MyClickonce" Url="MyClickonce20110815152200"> <File Path="MyClickonceMyClickonce.application" Url="MyClickonce.application" /> <File Path="MyClickonceApplication FilesMyClickonce_1_0_0_0MyClickonce.application" Url="Application Files/MyClickonce_1_0_0_0/MyClickonce.application" /> <File Path="MyClickonceApplication FilesMyClickonce_1_0_0_0MyClickonce.exe.deploy" Url="Application Files/MyClickonce_1_0_0_0/MyClickonce.exe.deploy" /> <File Path="MyClickonceApplication FilesMyClickonce_1_0_0_0MyClickonce.exe.manifest" Url="Application Files/MyClickonce_1_0_0_0/MyClickonce.exe.manifest" /> </Module> </Elements> 

8. Now deploy your SharePoint Solution Package!

9. To verify the ClickOnce was deployed correctly just open the site with SharePoint Designer 2010.

Open SPD.

Click “All Files”.

Now you should find your folder “MyClickonce20110815152200”.

image

9. Open the SharePoint site you deployed to.

Enter the complete URL of the ClickOnce app in the address bar of the browser. In my case the URL is:

http://sharepoint.local/sites/Clickonce/MyClickonce20110815152200/MyClickonce.application

image

Open the URL.

The ClickOnce should start after some seconds. During installation or update you’ll see these windows:

image

image

It will look like this:

image

10. Now lets add a Quicklaunch entry for this:

image

In the link I’ve used “Param1”: http://sharepoint.local/sites/Clickonce/MyClickonce20110815152200/MyClickonce.application?Param1=Hey, Ingo!

image

Just click the Quicklaunch entry.

You’ll get:

image

That’s it. – Please let me know if it worked for you!

Of course you could build query strings in JavaScript to pass dynamically generated parameters to the ClickOnce!! This is charming and makes ClickOnce apps to become usefull in SharePoint development. You could create custom Ribbon menu items and call a ClickOnce app on click. As parameters you could pass ListID, Web URL, ItemID, … to the app! Very cool!

Have fun! – Please post your comments on this!

SharePoint PowerShell Timer Jobs: Run PowerShell scripts in SharePoint Timer Service context.

I’ve created a new project that combines my two primary technical passions: SharePoint 2010 and PowerShell. Smiley

The project “SharePoint PowerShell Timer Jobs” lets you create, modify and delete SharePoint timer jobs that run PowerShell scripts in the context of the SharePoint Timer Service!

It’s on Codeplex – including source code:

I hope you will love it! (If so please let me know!) – Please feel free to extend the project. If you do so please share your extensions with the community!!!

PowerShell Timer Jobs could be very useful. I’ll use it for “SharePoint Warmup”. Therefore I create a PowerShell script some months ago. This I will port to a PowerShell Timer Job and post it on my blog…

 

Now let’s have a look…

 

Here’s a screenshot of the “System Settings” page of the SharePoint 2010 Central Administration:

pic01

Use “Manage PowerShell Jobs” to create, modify or delete PowerShell jobs.

On the admin page you can select an existing timer job or select “<new>” to create a new one.

If you create a new one you have to configure it and name it. Then save the timer job. After that you’ll be able to edit the script.

In this screenshot you see an existing timer job:

Here the “Edit” button is enabled. The script below the “Edit” button is read only!

Click “Edit”… Here is what you will see:

In the Edit dialog you can enter your PowerShell script and save it.

The Edit dialog does not validate your script! It have to be valid. Or you will see errors in the history list.

At the management page you can enable or disable existing jobs using the checkbox. Don’t forget to click the “Update” button after you changed something!

At the “System Settings” page of the Central Administration you see the link “Review PowerShell Job Execution History”. Using this link you will be redirected to the history list. All outputs of all of your PowerShell jobs will be collected here.

The list will not be cleared automatically! – But… You could create a SharePoint PowerShell Timer Job for this purpose 🙂 …

The last thing you should know: All the jobs you create are accessible at common SharePoint Job admin pages  like “Review Job definitions” and “Check job status” (Central Administration -> Monitoring)

Here you see a screenshot of the “Review Job definitions” page of SharePoint 2010:

This is a screenshot of the Job definitions detail page:

At least a screenshot of the “Check job status” page of SharePoint:

Some more details…

The PowerShell script of each timer job will be executed in a dedicated PowerShell runtime environment (“Runspace”).

For the PowerShell runspace I’ve created a PowerShell Host implementation. This implementation does not support any user interaction! Be sure your script does not need to interact with the user, e.g. for delete confirmation.

The script you enter will be surrounded with this code:

Add-PSSnapIn Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue | out-null  
$o = Invoke-Command -ScriptBlock 
{ 
<your code> 
} 
$p = $o | out-string 
$p
 

For script development you should use this code frame too! But if you copy the script code from the development tool, e.g. Windows PowerShell ISE, to PowerShell Timer Jobs, be sure only to copy <your code>.

Furthermore you need to enable the execution of unsigned PowerShell scripts in the context of the SharePoint Timer Service. This service normally runs under the Farm account. Have a look into the “Services” management console.

Before you use the tool be sure you know what you do! – PowerShell scripts can damage your farm! BE CAREFULL!!!!I’m not responsible for any damages.

You shoud test the tool in your own environment. There could be errors in the tool!!! I’ve tested it in my dev environment, but maybe in yours it does not work properly!

Feel free to extend the tool. But if you do so please publish your code to the community.

You must not remove my name or the link to the projects homepage from any file or page of the project. – Please don’t do that. Thanks 🙂

 TO USE THE PROJECT…

…you need to deploy the .WSP file to sharepoint and activate the SharePoint features in the Central Administration. The history list have to resist on the CA!!!

SharePoint Warm Up – Now with “Timeout”

In January 2011 I published a SharePoint Warm Up script based on PowerShell.

Some days ago blog reader Tamas published an comment: https://blog.kenaro.com/2011/01/27/sharepoint-warm-up-with-powershell/#comment-286

“How to set a connection timeout”.

 

Here we go now!

 

I’ve extended the Warm Up Script. I hope, this helps some of you, especially Tamas Smiley

# SharePoint Warmup Script
# by Ingo Karstein
# 2011/01/26
# 2011/08/02

#<---- Improvement starts here
#region MyWebClient
    Add-Type -ReferencedAssemblies "System.Net" -TypeDefinition @"
    using System.Net;

    public class MyWebClient : WebClient
    {
        private int timeout = 60000;
            
        public MyWebClient(int timeout)
        {
            this.timeout = timeout;
        }
        
        public int Timeout
        {
            get
            {
                return timeout;
            }
            set
            {
                timeout = value;
            }
        }
        
        protected override WebRequest GetWebRequest(System.Uri webUrl)
        {
            WebRequest retVal = base.GetWebRequest(webUrl);
            retVal.Timeout = this.timeout;
            return retVal;
        }
    }
"@
#endregion
#----> Improvement ends here

$urls= @("http://sharepoint.local", "http://another.sharepoint.local")

New-EventLog -LogName "Application" -Source "SharePoint Warmup Script" -ErrorAction SilentlyContinue | Out-Null

$timeout = 60000 #=60 seconds               #<--- Improvement!

$urls | % {
    $url = $_
    try {
        $wc = New-Object MyWebClient($timeout)        #<--- Improvement!
        $wc.Credentials = [System.Net.CredentialCache]::DefaultCredentials
        $ret = $wc.DownloadString($url)
        if( $ret.Length -gt 0 ) {
            $s = "Last run successful for url ""$($url)"": $([DateTime]::Now.ToString('yyyy.dd.MM HH:mm:ss'))" 
            $filename=((Split-Path ($MyInvocation.MyCommand.Path))+"lastrunlog.txt")
            if( Test-Path $filename -PathType Leaf ) {
                $c = Get-Content $filename
                $cl = $c -split '`n'
                $s = ((@($s) + $cl) | select -First 200)
            }
            Out-File -InputObject ($s -join "`r`n") -FilePath $filename
        }
    } catch {
          Write-EventLog -Source "SharePoint Warmup Script"  -Category 0 -ComputerName "." -EntryType Error -LogName "Application" `
            -Message "SharePoint Warmup failed for url ""$($url)""." -EventId 1001

        $s = "Last run failed for url ""$($url)"": $([DateTime]::Now.ToString('yyyy.dd.MM HH:mm:ss')) : $($_.Exception.Message)" 
        $filename=((Split-Path ($MyInvocation.MyCommand.Path))+"lastrunlog.txt")
        if( Test-Path $filename -PathType Leaf ) {
          $c = Get-Content $filename
          $cl = $c -split '`n'
          $s = ((@($s) + $cl) | select -First 200)
        }
        Out-File -InputObject ($s -join "`r`n") -FilePath $filename
    }
}

(Announcement:) PowerGUI 3.0 released!

The folks of Quest released PowerGUI 3.0!

http://powergui.org

It’s a famous tool for PowerShell development. Useful for complex scripts like “RoboPowerCopy” 🙂 – On live server systems I use Microsoft PowerShell Integrated Scripting Environment. This is available as Windows Feature in Windows Server 2008 R2. But for development on dedicated dev machines I use PowerGUI.

Thanks to Quest and to the community for this great tool!

Last but not least:

IT’S FREEWARE! 🙂

Error in InfoPath web browser form: “An error occured querying a data source.”

Yesterday I changed the Access mode of a SharePoint Web Application to “Only SSL”.

On the Web Application are some browser enabled InfoPath forms. Some of them consume a SharePoint Web Service to query user information. After switching the Web App to HTTPS there was an Error while loading the form in browser.

First error message: “An error occured querying a data source.”

Second error message: “You do not have permissions to access a Web service that provides data required for this form to function correctly.”

So far, so good.

I looked into SharePoint Log (with ULSViewer) and found this error message:

“An operation failed because the following certificate has validation errors:nnSubject Name: CN=sharepoint.localnIssuer Name: CN=domain.local CAnThumbprint: 0102030405060708090a0b0c0d0e0f0102030405nnErrors:nn The root of the certificate chain is not a trusted root authority…

My first thought was that the root of the (new) SSL certificate is not in the Computer Accounts certificate store in “Trusted Root Certification Authorities”.

But it was!

Some dozens of minutes later… 😉 I remembered that there are some SharePoint PowerShell cmdlets for this topic:

Get-SPTrustedRootAuthority
Set-SPTrustedRootAuthority
New-SPTrustedRootAuthority
Remove-SPTrustedRootAuthority

I forgot to register the root CA certificate of the new SSL certificate in the SHAREPOINT Trusted Root Authorities store !!! Grrrr.

The following script solved it:

$cert = Get-childItem cert:localmachineCA102030405060708090a0b0c0d0e0f0102030405
new-SPTrustedRootAuthority -Certificate $cert -Name "domain.local CA"

You need to have the CA certificate in the “Trusted Root Certification Authorities” store of Windows and you have to get it’s “thumbprint”. Just open “certmgr.msc” at the “Run” box from the Start menu of windows.

You get:

Open “Trusted Root Certification Authorities” -> “Certificates”

Double-click the certificate. In my case (faked) “domain.local CA”.

In the next windows select tab “Details” and look for “Thumbprint”. Insert the hex numbers in the PowerShell script.

That’s it. 🙂