SMSOTAP v1.2.1

WARNING: All the Cinterion related content from this blog will be removed to go to the javacint wiki soon. Please get used to going there.

I have just made a little update to the SMSOTAP program so that the little message generation wizzard can add the NETUSER and NETPWD parameters in the OTAP SM sent to the TC65. Thank you John for this little feature request. This pretty useful, without it you could end up adding a new SM and editing each message.

By the way, I have tested this program on Linux with mono and it works fine.

Mono Tools for Visual Studio : I have tested it !

Yes, I have tested MonoVS with the version 0.2.2641 (on both client and server). I installed OpenSuse 11.1 and added the MonoVS software repository, and everything worked ! I would have prefer to get it from SVN in order to use it in my Debian hosts but the mono development team seems to have removed it from their SVN repository.

So, the Mono Remote Debugger for Visual Studio works, but there still some bugs. Deployment is super fast and it copies all the required DLL.

Remote debugging can be used (launching it is really fast too), but has some bugs, here are the ones I could find :
– On the classical mouse hover debugging popup, the expandable element “Non-public members” is always empty if available. If not available, every private member is displayed like any other public member variable.
– In my first tests, the “watch” window wouldn’t allow any variable to be expanded.
– If you have a variable not yet declared (but available) in the watch window and try to expand it, debugging just stops without any warning
– Sometimes, when I stop debugging I get a message saying something went wrong and the debugger might be unstable
– Once, after making a pause and then a stop, it totally crashed Visual Studio (but it only happened once).

And this is not really a bug, but unhandled exception are displayed in the dirty popup. This isn’t pretty.

If do “Run remotely in Mono”, it will display the Console output in the server’s console. If you do “Debug remotely in Mono”, the Console output is redirect the Debug output window.

This tool is still in private beta (I guess anyone has a good chance to be accepted like I was), but it can already help a lot of people. Even if you just use the Remote Running (which includes deploying the assembly), it still worth using this tool.

.Net Reflector + File Disassembler

.Net reflector is a really good tool. You can see the content of any assembly very easily. But it’s not really easy to see the full content of a class, or a library with it.

The File Disassembler add-in is a totally crazy stuff. You can take any assembly and totally disassemble it. It even creates the .csproj so that you just have to open the project in Visual Studio. But don’t get too excited if the code is obfuscated you will get some “empty” methods with just this comment :

1
// This item is obfuscated and can not be translated.

By the way. As you can see from the .net reflector video, you can register in the context menu for any DLL and EXE assembly by executing it with the /register parameter.

Sharepoint : Using BaseFieldControl

What for ?
Sharepoint’s API provides some standard form controls to render each column. This is the controls used to render the standard add and edit forms. And they all inherit the BaseFieldControl class.

In other word : In any SPList, you have some SPField fields and each of these SPField has the super power to create a BaseFieldControl. Each BaseFieldControl is a CompositeControl containing ASP.Net controls.

For a single line field, you will just have a wrapped TextBox. But for some more advanced fields like a multi-select lookup field (SPFieldLookup) or rich text field, it can generate some “complex” controls and their related javascript code.

The BaseFieldControl can be directly connected to your Sharepoint’s SPListItem and the BaseFieldControl.Value will match the format required to fill the SPListItem.

You can create your own BaseFieldControl controls, and you can directly mess up the BaseFieldControl.Controls property if you like to (that can be useful). I personally had to create a variation of the MultipleLookupField (that’s the BaseFieldControl used for a SPLookupField with the AllowMultipleValues property enabled) to create a CheckBoxList of selected items (that what the client wanted).

How to use them to display data ?
You can get it by accessing the SPField.FieldRenderingControl property. It gives you everything you need. Each BaseFieldControl has a ControlMode property (SPControlMode enum) which can be set to New, Edit, Display or Invalid (I don’t know why this third one exists).

Here is a stripped down version of the necessary code :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
protected override CreateChildControls() {
 
     // The web used
     var web = SPContext.Current.Web;
 
     // This is the code you could have easily guessed :
     var list = web.Lists["MyList"];
     var item = list.GetItemById( 1 );
     var field = item["DateOfEvent"];
     var bfc = field.FieldRenderingControl;
 
     // This is when it becomes tricky :
     var renderContext = SPContext.GetContext( this.Context, 0, list.ID, web );
 
     bfc.ListId = list.ID;
     bfc.FieldName = field.InternalName;
     bfc.ID = field.InternalName;
     bfc.ControlMode = SPControlMode.Edit;
     bfc.RenderContext = renderContext;
     bfc.ItemContext = renderContext;
     bfc.EnableViewState = true;
     bfc.Visible = true;
 
     Controls.Add( bfc );
}

And if you want to use it as an input form, you have to get a non existing SPListItem :

1
2
var item = list.GetItemById( 1 );
var field = item["DateOfEvent"];

By this :

1
2
var item = list.Items.Add();
var field = list.Fields["DateOfEvent"];

And then later add this reflection code to force the render context to take into account the temporary created item :

1
2
var fiContextItem = ( typeof( SPContext ) ).GetField( "m_item", BindingFlags.Instance | BindingFlags.NonPublic );
fiContextItem.SetValue( renderContext, item );

If you use multiple SPWeb…
In your Sharepoint code, you might need to use more than one SPWeb. In that case, you must absolutely take care of creating the right SPContext (using the SPWeb of the SPList of the source SPField) for each BaseFieldControl used.

And if the SPWeb used doesn’t have the same language as your current site, you can change it easily by doing something like that :

1
web.Locale = SPContext.Current.Web.Locale;

If you create your own BaseFieldControl…
If you create your own BaseFieldControl, you should really take a look on the disassembled code of the default BaseFieldControls. It could make you save a lot of time and efforts. And please note that the LookupField (which directly inherits BaseFieldControl) isn’t sealed. Inheriting from it might be a good way to do your own custom lookup BaseFieldControl.

If you enable the ControlMode = SPControlMode.Display
If you want to use your BFC with the Display ControlMode and want to set a value, you must take care of setting the Value BEFORE setting the ControlMode. If you don’t do that it will work on the first render but fail on the first PostBack : Your value will not be used (and displayed) by the BFC.

If you check the value of the BFC
If you check the value of the BFC, you have to remember that each BFC create its value for the SPField from which it has been created. For instance, empty values can be returned as String.Empty, null or even the 0 integer.

Mono Tools for Visual Studio

Just a little post for all these people who seem to think Mono is just an other short-term open-source software.

I’ve used it for quite some time with a production “real time” network server, which is running for something like 6 months now, and it performs very well. I do everything on my Windows host and then copy and launch the final app on the Linux host. But there are still two problems :

  • Not all .Net classes are supported. WCF (the most powerfull to do two-way async/sync communication) isn’t one of them.
  • You can’t use the powerfull Visual Studio debugger and you can’t take advantage of the PDB files (as they are not compatibles with mono).

Well, the Mono team has solved this second problem with their Mono Tools for Visual Studio. I have already applied twice and haven’t received any inivitation for the private mono tools tryout. But I guess it will be released to the public pretty soon (within 6 months). The mono guys are working really fast (but not as fast as the Microsoft .Net development team).

Sometimes people should just consider using Mono for their (web) applications. In my opinion, an ASP.Net + DataBase Linux server is faster to manage than a equivalent Windows Server. It doesn’t slow down with uptime, it doesn’t have dozens of useless services, it doesn’t require to restart for updates and real problems are way easier to diagnose.
The real limitation for me are the super Microsoft APIs and tools like WCF, LinQ, SQL Server 2008 (with its Integration and Analysis services) that you can only run on Windows.

Debugging on Sharepoint 2007

Sharepoint debugging isn’t fully automated. So you should really know how to debug and diagnose your assemblies in any given situation.

1. Attaching to the process
It only applies to a debugging environnement.

This is the one that everybody knows (or should at least). You deploy your DLL in the GAC, restart your application pool, access your sharepoint web application in order to load the application pool and the DLL and then attach to the right w3wp.exe process (or every w3wp.exe process if you don’t really know which one to choose).

2. Displaying where the exception happens
It should be used everywhere.

Just after deploying your DLL into the GAC, you should deploy the PDB file with it. In your exception management code, you have the exact line where the exception was thrown. Wether your users report it (with the exact line number), you see it in the logs or you have an automatic reporting system, the point is : You will know exactly where it fails.

If you have a WSP deployment method, you will have :

1
2
rem This WSP File contains the MyCorp.MyApp.MyLib library with the 0x123456789 public key token
stsadm -o addsolution -filename %WSPFILE%

If you have a DLL deployment method, you will have :

1
gacutil /if GAC\MyCorp.MyApp.MyLib.dll

Either way, you need to add the PDB with this command :

1
2
subst X: c:\windows\assembly\gac_msil
copy GAC\MyCorp.MyApp.MyLib.pdb X:\MyCorp.MyApp.MyLib\1.0.0.0_123456789\

If you’re not willing to give away your PDB file (it contains you complete code source and consumes space), you can find out where you app exactly failed just from the offset of the stacktrace reported by Sharepoint (with the CustomError=”Off” and StackTrace=”true” in the web.config). Some people explain how to do it here. Answer “3” allows you to get the IL offset like ASP.Net does in its (non customized) error page.

3. Launching the debugger from the code
This is very useful for features deactivation/uninstallation/installation/activation code.

You just have to add this line when you want to ask the debugger to attach to the assembly.

1
Debugger.Launch();

4. Other options
This article focuses on hardcore problems : Problems that occur inside sharepoint or weird problem that only appear on your production servers.

The WinDBG method seems a little bit overkill to me. Mostly because you still can’t analyze the state of the local variables with our current tools (but I hope it will be made available in a short future).

Innovation

Google innovation chick, Marissa Mayer, says in her stanford show :

  • Innovation – Not instant perfection. Launch early and often get to the market.
  • Share everything you can – Information is KEY and powerful.
  • You’re brilliant, we’re hiring – Hire generalists than can speak to all areas of your organization. Specialists create silos.
  • A license to pursue your dreams – Give people a choice us to where they would like to invest their time and thinking.
  • Ideas come from everywhere – The power of many wins.
  • Don’t politic – Use data. Consensus of data wins not power or hierarchy.
  • Creavity loves constraint – Stay in the sandbox and focus on direction/output.
  • Users and usage are key – Money will follow.
  • Don’t kill projects – Morph them. Adjust or tweak and more on.

I really like what she says even if I don’t feel very concerned by the two last points.

For me one of the most interesting thing is the 20% time to work on personal projects. It’s the proof that google trusts their employee to make good use of their time.

This came from a friend’s powerpoint with the title “Google applies agile methodology”. Well, I’m really skeptical about that. I think it’s much wider than that. People do not need to apply any specific methodology if they’re smart and willing to do their work. They’ll get organized by themselves.

By the way, you should look at the other Stanford videos. They are really great. It’s not especially for “entrepreneurs” but for anyone willing to learm from other people experiences.

Sharepoint : SPWebConfigModification

I’ve seen lots of Sharepoint software having an installation manual of at least 20 pages (sometimes 60). Most of the operations they describe could be fully automated. And these software were made by freaking big companies. They should be ashamed of themselves. Maybe they just forgot that computer science is all about saving some time (and not only making money).

One good example is MOSS Faceted search 2.5 (I haven’t tested the 3.0). It takes at least 40 minutes to uninstall this crap. Why isn’t it just ONE WSP or at least one BAT file launching the WSP installation and the other steps ? Is there any real reason for that ?

The SPWebConfigModification solves this web.config modification problem. It’s a pretty interesting feature of Sharepoint. You can edit the web.config file without any complex XML parsing. It doesn’t even matter that you add XML or not. The SPWebConfigModification class manages your add/mod/del operations easily. The only restriction is that you have to add your first configuration elements using the SPWebConfigModification. You cannot modify existing elements this way.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Source : http://sharethelearning.blogspot.com/2008/01/adding-bindingredirect-to-webconfig.html
public static void AddBindingRedirect( SPWebApplication webApp, string libraryName, string libraryPublicToken, string oldVersion, string newVersion ) {
	var ownerName = String.Format( "BindingRedirect.{0}", libraryName );
 
	{ // We delete last bindingRedirect
 
		var list = new List<SPWebConfigModification>();
		foreach ( SPWebConfigModification mod in webApp.WebConfigModifications ) {
			list.Add( mod );
		}
 
		foreach ( var mod in list ) {
			if ( mod.Owner == ownerName ) {
				LoggerCommon.LogVerbose( String.Format( "Deleting: \"{0}\"", mod.Value ) );
				webApp.WebConfigModifications.Remove( mod );
			}
		}
	}
 
	{ // We add our redirection
		String path = "configuration/runtime/*[namespace-uri()='urn:schemas-microsoft-com:asm.v1' and local-name()='assemblyBinding']";
		String name = String.Format( "*[namespace-uri()='urn:schemas-microsoft-com:asm.v1' and local-name()='dependentAssembly']/*[namespace-uri()='urn:schemas-microsoft-com:asm.v1' and local-name()='assemblyIdentity'][@name='{0}']/parent::*", libraryName );
		String webConfigValue = String.Format( @"
	<dependentAssembly>
		<!-- Added automatically at {4} -->
		<assemblyIdentity name='{0}' publicKeyToken='{1}' culture='neutral' />
		<bindingRedirect oldVersion='{2}' newVersion='{3}' />
	</dependentAssembly>
", libraryName, libraryPublicToken, oldVersion, newVersion, DateTime.Now );
 
		SPWebConfigModification mod = new SPWebConfigModification( name, path );
		mod.Value = webConfigValue;
		mod.Owner = ownerName;
		mod.Sequence = 0;
		mod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
 
		webApp.WebConfigModifications.Add( mod );
 
	}
 
	{ // We save our changes
		webApp.Update();
		SPFarm.Local.Services.GetValue<SPWebService>().ApplyWebConfigModifications();
	}
}

If you do a binding redirect from 1.0.0.0 to 1.0.1.0 and your .webpart file references the 1.0.0.0 version, sharepoint will store your webpart as referencing the 1.0.1.0 assembly (and not 1.0.0.0 as you told him). So if you then chose to change the binding redirect from 1.0.0.0 to 1.0.2.0, without redirecting 1.0.1.0 to 1.0.2.0, your webpart will still be the 1.0.1.0 version.

I haven’t tested this for event receivers, but the way they are registered (Sharepoint doesn’t check the assembly you add to the event receivers of a list), I would guess Sharepoint doesn’t change the assembly version.

To solve this webpart updating problem, you can use ranged binding redirect (.Net rules) :

1
2
var site = new SPSite("http://localhost");
AddBindingRedirect( site.WebApplication, "MyCorp.MyApp.MyLib", "0x123456789", "1.0.0.0-1.0.3.5", "1.0.3.5" );

That means that any webpart using a previous version of the “MyCorp.MyApp.MyLib” assembly between 1.0.0.0 and 1.0.3.5 will be redirected to the 1.0.3.5 version.

If your assembly contains page code-behind classes, you should take care of updating the aspx files as well.

Reference :

Sharepoint 2010

Finally, Microsoft decided to communicate on Sharepoint 2010 on their website.

Wow, they really seem to have understood all of the sharepoint developers problems. Look at the full developer video. Everything is incredible : Sharepoint Extensions seem complete and cutomizable, there is the new developper dashboard which displays for instance the number of opened SPRequest the time spent on each loading stage of the page, the Business Data Catalog model allows to use any data inside sharepoint within “external lists”, the official LinQ for Sharepoint, the new WSP package which allow to specify which deployment actions to use. This runs on top of Windows Server 2008 and Visual Studio 2010.

The bad news is : CAML hasn’t be thrown away, non-generic types like ListItemCollection are still used. But I guess, they can’t change everything instantly.

Sharepoint – Updating webparts

I’ve talked about this before. When you create versions of webparts, you have two main solutions to update existing webparts :

  • Create a binding redirect within the web.config file.
  • Update each and every webpart instance of the collection site (or just the site).

The first one is really the best solution : It’s simple and very fast. But, it applies to the whole Web Application. Sometimes, the client might want to only update the site collection or even the website. In this case, we need to apply the second solution : update each webpart.

Updating each webpart requires you to create a solution that will go through all the pages of your website and replace each instance of a webpart by an other instance.

I can’t give the complete code (as it’s still not mine), but only some little hints that can help you solve this problem.

You have to analyse each an every file of your websites :

1
2
3
4
5
6
7
8
9
		private static void AnalyseFolder( List<SPFile> list, SPFolder folder ) {
			foreach ( SPFile file in folder.Files ) {
				var fileName = file.Name.ToLower();
				if ( fileName.EndsWith( ".aspx" ) || fileName.EndsWith( ".master" ) )
					list.Add( file );
			}
			foreach ( SPFolder subFolder in folder.SubFolders )
				AnalyseFolder( list, subFolder );
		}

You could restrict youself to the “pages” folder, but you would miss some pages like the layouts.

Then for each file of the class :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
		private void TreatPage( SPWeb web, SPFile file ) {
			try {
				if ( file.Name.EndsWith( ".master" ) ) {
					Logger.LogVerbose( "\"{0}\" is a masterpage...", file.Url );
					return;
				}
				SPWeb managerWeb = null;
				try {
					if ( file.Item == null ) {
						Logger.LogNotice( "\"{0}\" isn't part of a document library...", file.Url );
						return;
					}
 
					// Sometimes the code above works but this will still throw an exception
					file.Item.ToString();
				} catch ( SPException ex ) {
					Logger.LogNotice( "\"{0}\" isn't part of a document library...", file.Url );
					return;
				}
				try {
					using ( var manager = file.GetLimitedWebPartManager( PersonalizationScope.Shared ) ) {
 
						managerWeb = manager.Web;
 
						// we reload the same file but in the SPLimitedWebpartManager's web context
						file = managerWeb.GetFile( file.Item.UniqueId );
 
						var collection = manager.WebParts;
 
						var toDelete = new List<WebPart>();
 
						var previousWebparts = new List<WebPart>();
						foreach ( WebPart wp in collection )
							previousWebparts.Add( wp );
 
						Boolean madeChanges = false;
 
						var toDispose = new List<WebPart>();
 
						foreach ( WebPart wp in previousWebparts ) {
							Boolean deleteWebPart;
 
							var newWebPart = GetNewWebPart( wp, out deleteWebPart );
							if ( newWebPart != null || deleteWebPart ) {
 
								if ( !madeChanges ) {
									CheckOutPage( file );
									madeChanges = true;
								}
 
								Logger.LogVerbose( "Replacing webpart \"{0}\" by \"{1}\"...", wp.GetType().FullName, newWebPart != null ? newWebPart.GetType().FullName : "- none -" );
								if ( newWebPart != null )
									manager.AddWebPart( newWebPart, wp.Zone != null ? wp.Zone.ID : "0", wp.ZoneIndex );
 
								// it's either replaced or deleted : it will need to be removed from the page
								toDelete.Add( wp );
 
								if ( newWebPart != null )
									toDispose.Add( newWebPart );
							}
						}
 
						// If we made some changes to the file, we have to save it
						if ( madeChanges ) {
							foreach ( var wp in toDelete )
								manager.DeleteWebPart( wp );
 
							file.Update();
 
							CheckInPage( file, "Webpart update" );
						}
 
 
						// All the webparts found will need to be disposed
						toDispose.AddRange( previousWebparts );
 
						// We dispose everyone
						foreach ( var wp in toDispose )
							wp.Dispose();
					}
				} finally {
					if ( managerWeb != null && managerWeb != web )
						MemoryManagement.MightDispose( web );
				}
			} catch ( Exception ex ) {
				Logger.LogException( ex, "UpdateWebpartsInPage.TreatPage( SPFile.Url=\"{0}\" )", file.Url );
			}