Second Life of a Hungarian SharePoint Geek

October 24, 2011

Getting the process ID of the IIS / SharePoint application pool using PowerShell

Filed under: Debugging, PowerShell, SP 2010 — Tags: , , — Peter Holpar @ 22:28

It is easy to attach your debugger to the right SharePoint application pool using Visual Studio, especially if specific extensions like CKS.Dev or my VS extension is installed.

Sometimes life is not so trivial, you don’t have Visual Studio installed on the server and / or you should use other tools to debug your application. It would be great to have the right process ID in this case as well.

For example, assume you have to attach WinDbg or other debugging tools like Deblector to the process of the IIS application pool of a selected SharePoint application.

Two years ago I wrote a post about how to attach the VS debugger from a VS macro. In that post I’ve illustrated how to get the right process based on the application pool name (see the GetProcessIdByAppPoolName and GetAppPoolNameFromCommandLine methods in that post). In the post about my VS extension mentioned above you can find a similar solution as well.

In this post I show you two PowerShell methods that can be used when there is no Visual Studio on the computer or you simply do not want to work with that.

You should specify the name of the application pool for the Get-AppPoolProcessIdByName method and it displays the process ID of the matching application pool:

function Get-AppPoolProcessIdByName($name) 

     Get-WmiObject Win32_Process |
       Where-Object { $_.CommandLine -like "*w3wp.exe*" } |
       ForEach-Object { [regex]::Matches($_.CommandLine, "-ap ""(.+)"" -v") |
       Add-Member NoteProperty -Name ProcessId -Value $_.ProcessId -PassThru } |
       Where-Object { $_.Success -and $_.Groups.Count -gt 1 -and $_.Groups[1].Value -eq $name } |
       ForEach-Object { Write-Host $_.ProcessId }
}

Usage:

Get-AppPoolProcessIdByName("SharePoint – 80")

The Get-SPAppPoolProcessIdByUrl method first determines the corresponding SharePoint web application, then calls the Get-AppPoolProcessIdByName method to get the process ID.

function Get-SPAppPoolProcessIdByUrl($url) 

     $app = Get-SPWebApplication $url
     Get-AppPoolProcessIdByName($app.DisplayName)
}

Usage:

Get-SPAppPoolProcessIdByUrl("http://sp2010")

You can use Get-AppPoolProcessIdByName for arbitrary IIS application pool, but as you can see from the code (and as I intended to sign with the Get-SP prefix) the Get-SPAppPoolProcessIdByUrl method is SharePoint specific.

October 21, 2011

How to deploy a custom field with custom properties from a feature – the missing second part

Filed under: Custom fields, Deployment, Features, PowerShell, SP 2010 — Tags: , , , , — Peter Holpar @ 14:49

Only readers following my blog from the beginning may remember my post I wrote about the deployment of custom fields having custom properties more than three years ago. Of course that post is about WSS 3.0.

At that time I described how to include the custom properties into the field definition even if these properties are not declared by the XSD validation schema. However, injecting the custom properties the way I described there did not have effect on the values of the custom properties the custom field actually deployed with. I promised then that in the next part I would show you how to resolve this issue. It’s better late than never, the second part comes now, updated for SharePoint 2010.

The trick to achieve our goal seemed originally to be really simple. The base idea behind  it was that an instance of the field must be created sooner or later before usage, and the values we would like to push into the custom properties were available on object creation in the field schema deployed.

I’ve modified the constructors of the custom field, adding a call to my custom InitializeProps method.

  1. public SPFieldRegEx(SPFieldCollection fields, string fieldName)
  2.     : base(fields, fieldName)
  3. {
  4.     InitializeProps();
  5. }
  6.  
  7. public SPFieldRegEx(SPFieldCollection fields, string typeName, string displayName)
  8.     : base(fields, typeName, displayName)
  9. {
  10.     InitializeProps();
  11. }

The InitializeProps method is responsible for reading up the deployed custom property values from the field schema and setting them to the standard custom location in the schema.

  1. // in the custom field feature definition
  2. private readonly string customNamespaceUri = "http://schemas.grepton.com/sharepoint/";
  3.  
  4. private void InitializeProps()
  5. {
  6.     String regEx = (String)GetCustomProperty("RegEx");
  7.  
  8.     // value of custom property is null if not yet set
  9.     if (regEx == null)
  10.     {
  11.  
  12.         // load the field schema into an XML document
  13.         XmlDocument schemaXml = new XmlDocument();
  14.         schemaXml.LoadXml(SchemaXml);
  15.  
  16.         XmlNode fieldNode = schemaXml.SelectSingleNode("Field");
  17.  
  18.         if (fieldNode != null)
  19.         {
  20.             InitCustomProperty(fieldNode, "RegEx");
  21.             InitCustomProperty(fieldNode, "ErrMsg");
  22.             InitCustomProperty(fieldNode, "MaxLen");
  23.         }
  24.  
  25.     }
  26.  
  27. }
  28.  
  29. private void InitCustomProperty(XmlNode fieldNode, String custPropName)
  30. {
  31.     XmlAttribute custPropOrigAttr = fieldNode.Attributes[custPropName, customNamespaceUri];
  32.     // should not be null, but we check it
  33.     if (custPropOrigAttr != null)
  34.     {
  35.         SetCustomProperty(custPropName, custPropOrigAttr.Value);
  36.     }
  37. }

After deploying my custom field, I’ve checked my field at the Change Site Column page and was happy to see my custom property values there:

image

However, it turned out quickly that the schema of my field was not updated as expected:

  1. <Field
  2.   ID="{54634385-A8AC-4898-BF24-E533EB23444F}"
  3.   Name="RegExField"
  4.   DisplayName="RegExField"
  5.   StaticName="RegExField"
  6.   Group="Grepton Fields"
  7.   Type="SPFieldRegEx"
  8.   Sealed="FALSE"
  9.   AllowDeletion="TRUE"
  10.   SourceID="http://schemas.microsoft.com/sharepoint/v3/fields"
  11.   Description="This is the RegEx field"
  12.   grp:RegEx="[0-9]"
  13.   grp:MaxLen="20"
  14.   grp:ErrMsg="Error!"
  15.   xmlns:grp="http://schemas.grepton.com/sharepoint/" />

Of course, pressing OK on the Change Site Column page updated the field schema with the custom properties:

  1. <Field
  2.   ID="{54634385-A8AC-4898-BF24-E533EB23444F}"
  3.   Name="RegExField"
  4.   DisplayName="RegExField"
  5.   StaticName="RegExField"
  6.   Group="Grepton Fields"
  7.   Type="SPFieldRegEx"
  8.   Sealed="FALSE"
  9.   AllowDeletion="TRUE"
  10.   SourceID="http://schemas.microsoft.com/sharepoint/v3/fields"
  11.   Description="This is the RegEx field"
  12.   grp:RegEx="[0-9]"
  13.   grp:MaxLen="20"
  14.   grp:ErrMsg="Error!"
  15.   xmlns:grp="http://schemas.grepton.com/sharepoint/"
  16.   Required="FALSE"
  17.   EnforceUniqueValues="FALSE"
  18.   Version="1">
  19.   <Customization>
  20.     <ArrayOfProperty>
  21.       <Property>
  22.         <Name>RegEx</Name>
  23.         <Value
  24.           xmlns:q1="http://www.w3.org/2001/XMLSchema"
  25.           p4:type="q1:string"
  26.           xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">[0-9]</Value>
  27.       </Property>
  28.       <Property>
  29.         <Name>MaxLen</Name>
  30.         <Value
  31.           xmlns:q2="http://www.w3.org/2001/XMLSchema"
  32.           p4:type="q2:double"
  33.           xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">20</Value>
  34.       </Property>
  35.       <Property>
  36.         <Name>ErrMsg</Name>
  37.         <Value
  38.           xmlns:q3="http://www.w3.org/2001/XMLSchema"
  39.           p4:type="q3:string"
  40.           xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">Error!</Value>
  41.       </Property>
  42.     </ArrayOfProperty>
  43.   </Customization>
  44. </Field>

That gave me the idea that I should try to deploy my custom field using this schema. I removed my custom attributes from the Field element and used the following XML to deploy the field.

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  3.   <Field
  4.     ID="{54634385-A8AC-4898-BF24-E533EB23444F}"
  5.     Name="RegExField"
  6.     DisplayName="RegExField"
  7.     StaticName="RegExField"
  8.     Group="Grepton Fields"
  9.     Type="SPFieldRegEx"
  10.     Sealed="FALSE"
  11.     AllowDeletion="TRUE"
  12.     SourceID="http://schemas.microsoft.com/sharepoint/v3/fields"
  13.     Description="This is the RegEx field"
  14.     Version="1">
  15.     <Customization>
  16.       <ArrayOfProperty>
  17.         <Property>
  18.           <Name>RegEx</Name>
  19.           <Value
  20.             xmlns:q1="http://www.w3.org/2001/XMLSchema"
  21.             p4:type="q1:string"
  22.             xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">[0-9]</Value>
  23.         </Property>
  24.         <Property>
  25.           <Name>MaxLen</Name>
  26.           <Value
  27.             xmlns:q2="http://www.w3.org/2001/XMLSchema"
  28.             p4:type="q2:double"
  29.             xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">20</Value>
  30.         </Property>
  31.         <Property>
  32.           <Name>ErrMsg</Name>
  33.           <Value
  34.             xmlns:q3="http://www.w3.org/2001/XMLSchema"
  35.             p4:type="q3:string"
  36.             xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">Error!</Value>
  37.         </Property>
  38.       </ArrayOfProperty>
  39.     </Customization>
  40.   </Field>
  41. </Elements>

I’ve removed my custom codes described above either, and tried to deploy the field.

Voilà! The field is deployed successfully, including the custom properties. At that point I really don’t understand if it would work for WSS 3.0 and if so, how I missed to find that solution three years ago. The main point it is working now and requires no hacking at all.

A mystical observation is that after deploying the field I was not able to get a reference for the new field using PowerShell:

$avFields = $web.AvailableFields
$avFields["RegExField"]

At the same time, the field is visible on the web UI, and the following C# code finds it:

SPFieldCollection avFields = web.AvailableFields;
SPField field = avFields["RegExField"];

If I create the field from code, PowerShell finds that either:

SPFieldCollection fields = web.Fields;
SPField fieldCode = new SPField(fields, "SPFieldRegEx", "RegExCode");
fields.Add(fieldCode);

If you know the reason for that, let me know, please!

September 20, 2011

How to copy files through a remote desktop connection using clipboard and PowerShell

Filed under: PowerShell — Tags: — Peter Holpar @ 21:22

Recently I’m working on a remote system that I should access through a remote desktop connection. The local file system cannot be shared neither through the RDP client nor using direct UNC paths.

Since both the local and remote computer have Internet connection, first I tried to transfer files through the web, created a mail draft at the local machine using Outlook Web Access, added the files as attachments, saved it, and accessed the draft mail from the remote system. It was rather cumbersome, especially since OWA blocked some of the files due to their extension, so I had to apply additional tricks, like renaming files.

Fortunately, I remembered that a few months ago I read a blog post from Mikael Svenson where he described how he copied files from remote to local machine in a similar situation via converting binary files to text files using Base64 encoding on the remote system and copying the text content manually via the clipboard to the local system, where he saved it as a text file and finally converted back the text to binary content. In his case both systems had Visual Studio installed so he created simple custom application using C# to do the job.

In my case the remote server has no VS installed, and although I could have created a custom application on my local machine and uploaded it through OWA, I preferred to create a more flexible solution using PowerShell. Since I did not like the idea to create text files manually as part of the file transfer, I decided to use only the clipboard to manipulate the content. You can read more about accessing clipboard from PowerShell here.

I used the following methods to achieve my goal:

function Get-FileContent($filePath) 

     $command = 
     { 
         Add-Type -an System
         Add-Type -an System.Windows.Forms 
         $filePathArg = $Args[0]
         $content = [System.IO.File]::ReadAllBytes($filePathArg)
         $encoded = [System.Convert]::ToBase64String($content)
         [System.Windows.Forms.Clipboard]::SetText($encoded)
     } 
     powershell -sta -noprofile -args $filePath -command $command
}

function Set-FileContent($filePath) 

     $command = 
     { 
         Add-Type -an System
         Add-Type -an System.Windows.Forms 
         $filePathArg = $Args[0]
         $encoded = [System.Windows.Forms.Clipboard]::GetText()
         $content = [System.Convert]::FromBase64String($encoded)
         [System.IO.File]::WriteAllBytes($filePathArg, $content)
     } 
     powershell -sta -noprofile -args $filePath -command $command
}

You can run Get-FileContent specifying the local path of the file and it places the Base64 encoded binary file content as text onto the shared clipboard. On the remote system, the Set-FileContent method decodes the text content from the shared clipboard and writes the output into the file path you specified. You can use the methods in the opposite order if you need to copy from remote to local machine.

August 11, 2011

Getting and setting language dependent field titles using PowerShell

Filed under: MUI, PowerShell, SP 2010 — Tags: , , — Peter Holpar @ 22:43

The Multilingual User Interface (MUI) in SharePoint 2010 makes it possible to set language dependent titles and descriptions for your fields, lists, content types and web sites. The list of all affected types and properties as well as a short introduction of MUI can be read on MSDN.

As you can read there, the localization of these objects is done through the SPUserResource class and its Value property, that gets / sets the localized value based on the CurrentUICulture property of the Thread.CurrentThread object. It means that instead of calling a method with a CultureInfo parameter to get / set for example a field title, you should first set the CurrentUICulture to the CultureInfo you would like to work with:

Thread.CurrentThread.CurrentUICulture = yourCulture;

Then get / set the Title property of your SPField object as you did in WSS 3.0:

field.Title = title;

The above procedure shows it is pretty straightforward in C#, but recently I had to do something similar using PowerShell. I found it to be not trivial, and would like to share the lessons I’ve learnt.

My first approach was based on this post to simply set the CurrentUICulture as I did using C#, but it does not work for me. After setting the CurrentUICulture to German (de-DE) I tried to get its value in the next command, but it was still (or again?) English (en-US), so when I set the Title, it was set for English, not for German.

The solution for this issue can be to run the second command in the same line like this:

[Threading.Thread]::CurrentThread.CurrentCulture = $culture;[Threading.Thread]::CurrentThread.CurrentCulture

But the above format is not so nice and hard to use and read if you need to work with a lot of fields / languages.

I found the following Using-Culture function for PowerShell on  Stack Overflow:

function Using-Culture (
   [System.Globalization.CultureInfo]   $culture = (throw "USAGE: Using-Culture -Culture culture -Script {…}"),
   [ScriptBlock]
   $script = (throw "USAGE: Using-Culture -Culture culture -Script {…}"))
   {
     $OldCulture = [Threading.Thread]::CurrentThread.CurrentCulture
     $OldUICulture = [Threading.Thread]::CurrentThread.CurrentUICulture
         try {
                 [Threading.Thread]::CurrentThread.CurrentCulture = $culture
                 [Threading.Thread]::CurrentThread.CurrentUICulture = $culture
                 Invoke-Command $script
         }
         finally {
                 [Threading.Thread]::CurrentThread.CurrentCulture = $OldCulture
                 [Threading.Thread]::CurrentThread.CurrentUICulture = $OldUICulture
         }
}

Using this function, it was easy to query the field titles (in this case for all fields with English Title begins with “title”):

$web = Get-SPWeb http://SP2010
$fields = $web.Fields
$fields | Where-Object { $_.Title -like "title*" } | ForEach-Object { Using-Culture de-DE { $_.TitleResource.Value } }

Or a more compact version of the last line:

$fields | ? { $_.Title -like "title*" } | % { Using-Culture de-DE { $_.TitleResource.Value } }

My first approach to set the value for multiple fields was the next one:

$fields | ? { $_.Title -like "title*" } | % { Using-Culture de-DE { $_.TitleResource.Value = "Titel"; $_.Update("True") } }

But it resulted the following error:

An error occurred while enumerating through a collection: Collection was modified; enumeration operation may not execute..

To create a solution for this task I assembled first a new array of fields using the IDs of matching fields, then iterated through this array, set the field title and updated the field:

$fields | ? { $_.Title -like "title*" } | % { $web.Fields[$_.Id] } | % { Using-Culture de-DE { $_.TitleResource.Value = "Titel"; $_.Update("True") } }

June 2, 2011

(Re-)enabling Flash content on SharePoint pages

Filed under: Fiddler, Flash, PowerShell, SP 2010 — Tags: , , , — Peter Holpar @ 22:22

Recently I was to migrate a MOSS 2007 solution to SP 2010. Everything was OK but I found that the Flash content – .swf files were stored in the SharePoint content DB – was displayed as static content, not like an animation, only the first frame was shown.

My first assumption was that there might be an issue with the Content-Type HTTP header sent by the server, but checking the traffic by Fiddler proved the header is therewith the correct value (see red marking below):

image

However, there were a few interesting headers there, like the X-Download-Options: noopen (see the yellow marking above).

After a quick search on the web I found this post that helped me to solve the issue:

Open PDF File in Browser from SharePoint 2010

Based on the article I run this PowerShell script on the server:

$webApp = Get-SPWebApplication http://yourserver
$webApp.AllowedInlineDownloadedMimeTypes.Add("application/x-shockwave-flash" )
$webApp.Update()

… and run an IISRESET.

Due to the change the X-Download-Options header disappeared and the Flash animations worked as designed.

Theme: Shocking Blue Green. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 42 other followers