Welcome to MSDN Blogs Sign in | Join | Help

System.MissingMethodException: Method Not Found when running unit Tests

Lately, I have been doing Test Driven development in my development projects. I have noticed a strange issue which occurs especially when the code coverage is enabled for the assemblies. The result is , unit tests works normally in Debug mode and do not behave as expected when run in Normal mode.

I have noticed the bug logged for this on Connect website:

https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=123306

 

Meanwhile, the workaround for this is to do a full release build of the solution before running unit tests in Normal mode.I guess this is because, Normal run uses the release version of assemblies as opposed to Debug versions, and if the Release assemblies are either old or missing, they are bound to fail.

 

[Update 6/01/09]

Rory had left a comment on this post and provided the excellent and absolute solution for this. This is quoted in his own words and here is the link to his blog post. Thanks Rory !!!

The issue is encountered because the testrunconfig points code coverage at assemblies using a relative path that includes the build configuration output directory.

If the testrunconfig points to bin\Debug\YourAssembly.dll and you make a change to the methods in Debug and a test for it, then change to Release without a Debug build and run the unit tests, then the new tests won't find the methods in the assembly identified in the code coverage configuration.

See http://www.neovolve.com/post/2008/01/30/incorrect-code-coverage-references-in-testrunconfig.aspx

The fix is to manually edit the testrunconfig to replace the build configuration part of the path with $(OutDir). This will mean that the assemblies will work with any build configuration.

Posted by Madhur | 3 Comments
Filed under:

Adding Client script to TreeNode Click Event

Most of the ASP.NET 2.0 controls provide way to add client events to them. For example, TextBoxes, checkbox are simply rendered as <input> elements and can be easily hooked to JavaScript functions either programmatically or declaratively.

However, TreeNode is a special case in that. There is no straightforward way to hookup client events to it. This is simply because they are rendered as Anchor <a> elements and its href and onClick attributes are itself consumed by ASP.NET framework.

However, with a little trick, the TreeNode element can be easily hooked up to client events.

The key to this solution is TreeNode’s PreRenderText and PostRenderText methods. The framework describes it as :


RenderPostText: Allows control developers to add additional rendering to the node.


RenderPreText: Allows control developers to add additional rendering to the node.

Using these methods, we can inject a simple <div> tag and enclose our Nodes(<a>) in those. For example:

public class CTreeNode : TreeNode
{
    protected override void RenderPreText(HtmlTextWriter writer)
    {
 
        writer.Write("<div onclick='CheckUnsavedStatus(this);'>");
        base.RenderPreText(writer);
    }
 
    public CTreeNode(string label, string value)
    {
        this.Text = label;
        this.Value = value;
 
    }
 
    protected override void RenderPostText(HtmlTextWriter writer)
    {
        writer.Write("</div>");
        base.RenderPostText(writer);
    }
 
}

The code above will call the CheckUnsavedStatus client function passing it the reference of <div> element. Using this <div> element, we can alter our Node(<a> element) at the client side.

For example, We might want to call the server side event SelectedNodeChanged, only if user has saved its changes. The client script for this kind of scenario would be:

function CheckUnsavedStatus(divObj)
 {
                var HiddenSaveDataCheckObj=document.getElementById('<%=hiddenSaveDataCheck.ClientID %>');
                if(HiddenSaveDataCheckObj==null)
                    return;
                if(HiddenSaveDataCheckObj.value=='1')
                {
                    var response=confirm('There are pending changes. Click Ok to continue and lose the changes');
                    if(response==false)
                    {
                         divObj.children[0].href='#';
                         return true;
                    }
                        
                }    
}

The function above changes the target link of node to # which suppresses the server side call and subsequently the postback. Here, hiddenSaveDataCheck is a HiddenField which tracks the save/unsaved status.

Note that:

To preserve the custom script generation on postback, the CreateNode method of TreeView control must be overrideen to return our CustomNode rather than default TreeNode implementation. This is easy:

public class CustomTreeView:TreeView
    {
        protected override TreeNode CreateNode()
        {
            return new CTreeNode();
        }
    }

Another important thing to be kept in mind is SelectAction property of TreeNode. This property determines, what happens when you click a TreeNode. This is an enumeration described as :

TreeNodeSelectAction.Expand : Toggles the node between expanded and collapsed. Raises the TreeNodeExpanded event or the TreeNodeCollapsed event as appropriate.

TreeNodeSelectAction.None: Raises no events when a node is selected.

TreeNodeSelectAction.Select: Raises the SelectedNodeChanged event when a node is selected.

TreeNodeSelectAction.SelectExpand: Raises both the SelectedNodeChanged and the TreeNodeExpanded events when a node is selected. Nodes are only expanded, never collapsed.

This solution will work for all the cases irrespective of the setting, however it hardly makes it any sense to set it as TreeNodeSelectAction.None, since in this case, node is not rendered as hyperlink. For only client event, it should  preferably be set to TreeNodeSelectAction.Select. Since this also causes  a server side event SelectedNodeChanged, we can suppress the postback in our client function as discussed above.

Posted by Madhur | 1 Comments
Filed under: ,

Resolving Common issues while Applying Site Templates (.stp) on MOSS 2007

Lately, I have been working a lot with site templates in MOSS 2007. I have seen common issues which keep occurring now and then. I am posting the resolution of those since you might also run across them frequently.

 

File Cannot be found

  • This one is easy. In 99% of the cases, this usually occurs due to missing master page. If the site template has been created out of a site having a custom master page and if that master page is not present in the target machine, be ready to face this error. It can be simply resolved by uploading the required master page in Master page gallery of the target site collection.

  • Note that, if the site template contains custom webparts or user controls, this error would also come if the assembly for those controls is not found. Resolution would be to simply put the correct assemblies in bin directory of the target application and update the web.config with <safecontrol> entries.

  • You would also face the same error if the site template is a Publishing site and one of the pages in its Pages Library have been built out of a custom Page Layout. The reason is simple, page layouts are not packaged in the site template. There is no resolution to this error. You might get away by uploading the required page layout in the page layouts gallery of the target site collection , but I recommend against it. That’s one of the reasons, Microsoft does not officially support saving Publishing Sites as Template (The link in Site Settings disappears when you activate Publishing feature :)).

 

The template you have chosen is invalid or cannot be found

  • If you are trying to apply the custom site template (programmatically) and if the template is not present.

Resolution:

Add the new template by using stsadm –o addtemplate –filename <filename.stp> –title <title>

  • You can also get this error when applying the template through GUI. Obviously, the template is present but sharepoint is somehow finding it to be invalid. One of the reasons for this could be version mismatch. You cannot restore the template created out of SP1 to a non-SP1 installation of MOSS.

Resolution: Upgrade the source server and re-create the template.

(I am not sure if the template created from non-SP version can be applied to SP1 installation of MOSS).

  • Thirdly, this error also comes when you have feature dependency missing. Basically the site from which the template was created has one or more custom feature activated on it which are not present on the target installation.

Resolution:

If you know the missing feature, install the feature on the target machine.

However, even if you have made sure that you have all the required features present and are still facing this error, it can be easily debugged.

Rename the .stp file to .cab file and extract manifest.xml out of it. If we open the manifest.xml, it would contain the list of dependent feature ids in a section like this:

<SiteFeatures>
<Feature ID="a392da98-270b-4e85-9769-04c0fde267aa" /> 
<Feature ID="f6924d36-2fa8-4f0b-b16d-06b7250180fa" /> 
<Feature ID="695b6570-a48b-4a8e-8ea5-26ea7fc1d162" /> 
<Feature ID="89e0306d-453b-4ec5-8d68-42067cdbf98e" /> 
<Feature ID="ca7bd552-10b1-4563-85b9-5ed1d39c962a" /> 
<Feature ID="d3f51be2-38a8-4e44-ba84-940d35be1566" /> 
<Feature ID="aebc918d-b20f-4a11-a1db-9ed84d79c87e" /> 
<Feature ID="00bfea71-1c5e-4a24-b310-ba51c3eb7a57" /> 
<Feature ID="bf120348-ef6f-4526-a9a8-d4f4b897c790" /> 
</SiteFeatures>
<WebFeatures>
<Feature ID="00bfea71-c796-4402-9f2f-0eb9a6e71b18" /> 
<Feature ID="29d85c25-170c-4df9-a641-12db0b9d4130" /> 
<Feature ID="00bfea71-5932-4f9c-ad71-1557e5751100" /> 
<Feature ID="00bfea71-4ea5-48d4-a4ad-305cf7030140" /> 
<Feature ID="00bfea71-f600-43f6-a895-40c0de7b0117" /> 
</WebFeatures>

The feature IDs listed under SiteFeatures tag  are the site collection features on which the site  was dependent upon when the site template was saved as a template.

Similarly, As you might have guessed, features IDs listed under WebFeatures tag are Site features on which the template was dependent.

Armed with this information, you can simply do a text search on Features folder of your target installation to see which feature is missing and deploy-activate the missing feature on the target machine. Usually, I have seen custom feature are at the bottom of the list and out of the box features are at the top, but don’t take this observation to be granted in your case :).

If you know of any other resolutions, or fighting/fought with any similar issue , do leave me a comment and I’ll update my post (along with credits ofcourse).

Cheers,

Madhur

Posted by Madhur | 0 Comments
Filed under:

Use right tools for x64 machines

If you are setting up a x64 server for MOSS, there are some things to be taken care of. I ran into couple of issues while setting up my development machine and had a hard time debugging it.

  • While registering ASP.NET 2.0 framework with IIS, make sure to use 64 bit version of aspnet_regiis.exe. This binary would be located at

%systemdrive%\WINDOWS\Microsoft.NET\Framework64\v2.0.50727

If you are in habit of using 32 bit version for long time, you might mistakenly use the one located at %systemdrive%\WINDOWS\Microsoft.NET\Framework\v2.0.50727.

Note that this directory is present even if you only install X64 version of .NET Framework for compatibility reasons.  But running this command would only give you error unless you enable 32 bit worker process on the IIS. This can be done by setting the following value in IIS metabase from the command prompt.

cscript %SYSTEMDRIVE%\inetpub\adminscripts\adsutil.vbs SET W3SVC/AppPools/Enable32bitAppOnWin64

 

  • Even the Visual Studio SDK tools are different for x64 versions. On a 64 bit machine, running sn.exe –Vr or Sn.exe –Vl  to skip the signature verification on assemblies will not have any effect.  Use the correct versions located at:

%systemdrive%\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\x64.

This point is worth mentioning because, VS 2005/2008 by default sets the PATH environment variable to 32 bit binaries regardless of the architecture.

Posted by Madhur | 0 Comments
Filed under: , ,

Managed Debugging and inspecting Jitted code With WinDbg

WinDbg is a powerful tool to debug applications. Lately, its use has been extended to managed debugging as well through an extension called sos.dll (Son of Strike). Sos.dll is shipped with both .NET Framework 2.0 and .NET 1.1 Framework and is a WinDbg extension which allows WinDbg to read managed data structures such as Method Tables, Method Descriptors etc. The power of WinDbg is ultimate in detecting memory leaks, deadlocks and unexpected application crashes where Visual Studio debugger cannot reach.

In this post, we will try to debug and inspect the Jitted code for a very simple .NET console application. Note that WinDbg is new to me as well, so if there is any better or shorter way to do this, kindly leave a comment to me.

namespace ConsoleApplication1
{
    class Test
    {
        public int i;
        public int ri()
        {
            i++;
            return i;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Test t = new Test();
 
            int y = t.ri();
 
            Console.Write(y);
        }
    }
}

Upon loading this ConsoleApplication1.exe into debugger, WinDbg breaks at the default debug breakpoint. At this point .NET runtime is not loaded into memory, so SOS cannot be loaded at this moment. We will set a breakpoint on Module load of mscorlib.dll, so that we can load SOS.

Entering the command sxe ld:mscorlib sets this breakpoint. Once it is set, we will go ahead with F5 to run the application. WinDbg will break upon loading on mscorlib.dll :

ModLoad: 790c0000 79bf6000   C:\WINDOWS\assembly\NativeImages_v2.0.50727_32\mscorlib\32e6f703c114f3a971cbe706586e3655\mscorlib.ni.dll
eax=00000001 ebx=00000000 ecx=00000001 edx=00000000 esi=7ffdf000 edi=20000000
eip=7c82ed54 esp=0012e9d0 ebp=0012ea14 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!KiFastSystemCallRet:
7c82ed54 c3              ret

At this point, SOS can be loaded into memory, with the command .loadby sos mscorwks

Once the SOS is loaded, we can inspect the running threads using !threads

!threads

PDB symbol for mscorwks.dll not loaded ThreadCount: 2 UnstartedThread: 1 BackgroundThread: 1 PendingThread: 1 DeadThread: 0 Hosted Runtime: no PreEmptive GC Alloc Lock ID OSID ThreadOBJ State GC Context Domain Count APT Exception 0 1 1c4 00181fd0 220 Enabled 00000000:00000000 0014cf28 1 Ukn XXXX 2 9d4 001533b8 1400 Enabled 00000000:00000000 0014cf28 0 Ukn (Finalizer)

We can see from the output above that there is one AppDomain at 0014cf28 loaded for the current running process, which makes sense since it is a very simple application.

Lets dump the domain information using !dumpdomain 014cf28

!dumpdomain 014cf28

Domain 1: 0014cf28 LowFrequencyHeap: 0014cf4c HighFrequencyHeap: 0014cfa4 StubHeap: 0014cffc Stage: OPEN SecurityDescriptor: 0014e258 Name: None

Dumping the domain gives us information about the locations of heaps. Note that it also gives the managed loaded modules in the process, but there is none listed since no managed module has been loaded yet (mscorlib is about to be loaded). We can set another breakpoint in WinDbg till the jitting engine (mscorjit.dll) is loaded using sxe ld:mscorjit

The breakpoint is hit as soon as mscorjit.dll is loaded:

ModLoad: 79060000 790b6000   C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorjit.dll
eax=00000001 ebx=00000000 ecx=00000001 edx=00000000 esi=7ffdf000 edi=20000000
eip=7c82ed54 esp=0012e554 ebp=0012e598 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!KiFastSystemCallRet:
7c82ed54 c3              ret

Now, if we dump the domain again,

Domain 1: 0014cf28
LowFrequencyHeap: 0014cf4c
HighFrequencyHeap: 0014cfa4
StubHeap: 0014cffc
Stage: OPEN
SecurityDescriptor: 0014e258
Name: ConsoleApplication1.exe
Assembly: 00190558 [C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll]
ClassLoader: 001905f0
SecurityDescriptor: 0018cdc8
  Module Name
790c2000 C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll

Assembly: 00194388 [C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\ConsoleApplication1\ConsoleApplication1\bin\Release\ConsoleApplication1.exe]
ClassLoader: 00193720
SecurityDescriptor: 001942f0
  Module Name
008f2c3c C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\ConsoleApplication1\ConsoleApplication1\bin\Release\ConsoleApplication1.exe

We can see that it lists, the mscorlib and our application(ConsoleApplication1.exe) as one of the loaded modules. Lets proceed onto setting the breakpoint on the Main() function. First we will need to get the address of method table of class Program by first dumping our module using !dumpmodule command. Once we get the method table, we can dump it using !dumpmt command to get the method descriptor for the Main() function.

!dumpmodule -mt 008f2c3c

!dumpmodule -mt 008f2c3c

Name: C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\ConsoleApplication1\ConsoleApplication1\bin\Release\ConsoleApplication1.exe Attributes: PEFile Assembly: 00194388 LoaderHeap: 00000000 TypeDefToMethodTableMap: 008f00c0 TypeRefToMethodTableMap: 008f00cc MethodDefToDescMap: 008f0118 FieldDefToDescMap: 008f012c MemberRefToDescMap: 008f0134 FileReferencesMap: 008f017c AssemblyReferencesMap: 008f0180 MetaData start address: 00402098 (1548 bytes) Types defined in this module MT TypeDef Name ------------------------------------------------------------------------------ 008f3038 0x02000003 ConsoleApplication1.Program Types referenced in this module MT TypeRef Name ------------------------------------------------------------------------------ 790fd0f0 0x01000001 System.Object

We can dump the method table of ConsoleApplication1.Program using !dumpmt -md 008f3038. The parameter -md is for dumping MethodDesc table.

!dumpmt -md 008f3038

EEClass: 008f1278 Module: 008f2c3c Name: ConsoleApplication1.Program mdToken: 02000003 (C:\Documents and Settings\Administrator\My Documents\Visual Studio 2005\Projects\ConsoleApplication1\ConsoleApplication1\bin\Release\ConsoleApplication1.exe) BaseSize: 0xc ComponentSize: 0x0 Number of IFaces in IFaceMap: 0 Slots in VTable: 6 -------------------------------------- MethodDesc Table Entry MethodDesc JIT Name 79371278 7914b928 PreJIT System.Object.ToString() 7936b3b0 7914b930 PreJIT System.Object.Equals(System.Object) 7936b3d0 7914b948 PreJIT System.Object.GetHashCode() 793624d0 7914b950 PreJIT System.Object.Finalize() 008fc010 008f3028 NONE ConsoleApplication1.Program.Main(System.String[]) 008fc01c 008f3030 NONE ConsoleApplication1.Program..ctor()

We can see that method descriptor for Main resides at 008f3028.

We can set the breakpoint on Program.Main() method using !bpmd -md 008f3028

MethodDesc = 008f3028
Adding pending breakpoints...

As we run the application, the breakpoint is hit and instruction pointer is placed on the first instruction of the jitted Main() method.

(a0c.1c4): CLR notification exception - code e0444143 (first chance)
JITTED ConsoleApplication1!ConsoleApplication1.Program.Main(System.String[])
Setting breakpoint: bp 00C20070 [ConsoleApplication1.Program.Main(System.String[])]
Breakpoint 0 hit
eax=008f3028 ebx=0012f4ac ecx=01241ec0 edx=00000000 esi=00181fd0 edi=00000000
eip=00c20070 esp=0012f484 ebp=0012f490 iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000212
00c20070 56              push    esi

Finally, to see the generated machine code, use the Unassemble instruction to unassemble the instructions starting from current.

!u eip

   1: Normal JIT generated code
   2: ConsoleApplication1.Program.Main(System.String[])
   3: Begin 00c20070, size 35
   4: >>> 00c20070 56              push    esi
   5: 00c20071 b9b0308f00      mov     ecx,8F30B0h (MT: ConsoleApplication1.Test)
   6: 00c20076 e8a11f7dff      call    003f201c (JitHelp: CORINFO_HELP_NEWSFAST)
   7: 00c2007b 8bd0            mov     edx,eax
   8: 00c2007d ff4204          inc     dword ptr [edx+4]
   9: 00c20080 8b7204          mov     esi,dword ptr [edx+4]
  10: 00c20083 833d8c10240200  cmp     dword ptr ds:[224108Ch],0
  11: 00c2008a 750a            jne     00c20096
  12: 00c2008c b901000000      mov     ecx,1
  13: *** WARNING: Unable to verify checksum for C:\WINDOWS\assembly\NativeImages_v2.0.50727_32\mscorlib\32e6f703c114f3a971cbe706586e3655\mscorlib.ni.dll
  14: 00c20091 e8ce587478      call    mscorlib_ni+0x2a5964 (79365964) (System.Console.InitializeStdOutError(Boolean), mdToken: 06000770)
  15: 00c20096 8b0d8c102402    mov     ecx,dword ptr ds:[224108Ch]
  16: 00c2009c 8bd6            mov     edx,esi
  17: 00c2009e 8b01            mov     eax,dword ptr [ecx]
  18: 00c200a0 ff5074          call    dword ptr [eax+74h]
  19: 00c200a3 5e              pop     esi
  20: 00c200a4 c3              ret

Here are some interesting observations of this jitted code:

  • The call at line number 6 (call 003f201c (JitHelp: CORINFO_HELP_NEWSFAST) is a call to create the instance of any class. The parameter to this method is the method table address which we see being passed in previous line number in ecx. If we dump out that method table using !dumpmt , we will see that it points to class Test.

  • One might be wondering, once the Test Class is instantiated, where is the call to ri()? Interestingly, there is no call and the instructions are inlined as we see in line numbers 7 and 8. inc instruction is equivalent to i++. I do not exactly know why it has been inlined , perhaps may be because compiler optimizations were enabled in Visual Studio. Or if there are any rules governing the inlining, I do not know them yet.

  • Lines number 14 - 16 are simply initialization of Console to prepare it for emitting text using Write() method. This Write() method is finally called at line 18. If you set the breakpoint on line 18 and step through it, you will see 1 being outputted on your screen :)
Posted by Madhur | 1 Comments
Filed under: , ,

Resolving Very Slow Symbol Loading with VS 2008 during debugging

Recently, I was encountering insanely slow project loading times during debugging in Visual Studio 2008. Interestingly, this only happened while loading the project only in debug mode. Also, during the slow symbol loading time, the status bar at Visual Studio 2008 always showed Loading Symbols For ____.Dll.

To track it down, I enabled logging for the VS 2008 IDE. This can be done by adding the following entries to its configuration file.

1) Navigate to: C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE  in Windows Explorer

2) Load devenv.exe.config in text editor.

3) Add the following entries into it

  <system.diagnostics>
        <trace autoflush="true" indentsize="4">
            <listeners>
               <add name="myListener" 
type="System.Diagnostics.TextWriterTraceListener, System version=1.0.3300.0, 
Culture=neutral, PublicKeyToken=b77a5c561934e089" 
initializeData="c:\myListener.log" />
               <remove name="Default" />
            </listeners>
        </trace>
    </system.diagnostics>
As per this configuration, the trace log would be saved to C:\myListener.Log. Now I fired up VS 2008 and loaded to project in debug mode to reproduce the issues.
I also ran couple of unit test cases in debug mode which were also not able to load. I opened the trace log and found these exceptions:

[V, 5820, 7, 2008/07/04 14:00:33.412] devenv.exe: DIA thew in retrieving symbols: System.Runtime.InteropServices.COMException (0x806D0005): Exception from HRESULT: 0x806D0005

at Microsoft.VisualStudio.TestTools.Common.Dia2Lib.IDiaDataSource.loadDataForExe(String executable, String searchPath, Object pCallback)

at Microsoft.VisualStudio.TestTools.Common.DiaHelper.GetSymbolsFileName(String imagePath)

Confirmed with the issue was related to loading of Symbols. I realized that I had once set up a dedicated symbol directory on machine at C:\symbols to enable debugging with WinDbg.

I checked the Debugging Options in VS 2008 to see if its trying to load symbols from somewhere else.

Debug

 

Then suddenly, I was reminded that I had once set up a environment variable _NT_SYMBOL_PATH to an Internet location while debugging in WinDbg to enable automatic downloading of symbols.

I originally thought that this setting was only applicable to WinDbg. But I was mistaken. In fact, this setting will be used across by all debuggers (atleast Microsoft debuggers). This is documented here. I was pretty sure that VS 2008 was trying to load symbols from the Internet location and since many of the assemblies in my application didn't had symbols were them, it must have been failing.

To confirm, I fired My Computer -> Properties -> Advanced -> Environment Variables and cleared out the line

_NT_SYMBOL_PATH=srv*c:\mysymbols*http://msdl.microsoft.com/download/symbols;cache*c:\mysymbols

I reloaded the my project in VS 2008 in debug mode and Bingo, the problem was solved :)

While investigating this problem, I came across many other sources which could cause this issue , so I thought I can post it here for the benefit of community. The credit for these tips goes to Azeem Khan who is working with VSTS team at Microsoft.

  • Make sure that you don't have a symbol path specified in VS under Tools | Options | Debugging | Symbols and also, that you don't have the NT_SYMBOL_PATH environment variable set on your machine.
  • Verify that you haven't specified any network shares under the same setting to server that do not exist anymore. This will require timeouts.
  • Specify a local cache for symbols under the same setting. After you have downloaded symbols once from network shares you can disable those locations. Symbol loading will go a lot faster after the first attempt.
  • Do not specify any symbol lookup paths at all either in environment variable and specify paths in the options page but specify that they be used for manual loading. You can then manually load symbols for modules you care about either via the context menu in call stack or the modules window.
  • VS 2008 SP1 has made a few improvements in this area. It allows for canceling loading of symbols as it is happening. This will allow you to get to your debug session much faster. Note that this is currently in Beta.
  • Clearing the breakpoints also serve to solve this problem for some people.
Posted by Madhur | 9 Comments
Filed under: ,

Showing Exchange Inbox in MOSS 2007

Recently, I have seen a lot of discussions on forums about showing user's mailbox on a portal homepage. This is very similar to the case in My Site, but in this case the portal is not My Site and the requirement is to let each user view his/her mailbox using the My Inbox Web Part.

Interestingly, I have seen developers writing custom Web Part for this requirement. In-fact, this is available totally out of the box.

If you are using Exchange 2007, you can just fill in the Exchange Server field in your Outlook Web Parts for MOSS 2007 and the Web Part will pickup the current user's mailbox automatically.

If you have a Exchange 2003 and SharePoint in your organization, this is one of the strong reason alone for going into Exchange 2007 upgrade, along with other benefits which Exchange 2007 provides.

However, If you are using Exchange 2003 and want to achieve this functionality, its easy to write the custom Web Part which automatically assigns the current user and exchange server to Web Part programmatically. But there are limitations of this approach which I will outline at the end of this post. This is simple code for the Web Part which can be used to assign the username and server name.

 

using System;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.Portal.WebControls;

namespace SharePoint.Webparts
{
    public class MyInboxEx : System.Web.UI.WebControls.WebParts.WebPart
    {
         protected override void CreateChildControls()
        {

                     //Create the instance of My Inbox Web Part
                    OWAInboxPart inbox = new OWAInboxPart();

                   //Get and Assign the server name from web.config
                    inbox.OWAServerAddressRoot = ConfigurationSettings.AppSettings["SERVER_NAME"];
                    string userName = Page.Request.ServerVariables["LOGON_USER"];
                    string mailboxName = userName;
                    int pos = userName.IndexOf("\\");
                    if (pos > -1)
                    {
                        mailboxName = userName.Substring(pos + 1);
                    }
                   

                    //Assign the mailbox name

                    inbox.MailboxName = mailboxName;
                    Controls.Add(inbox);

         }

     }
}

 

In the above code, we can retrieve the server name of exchange server from any configuration store, I am using web.config <AppSettings> section here. Before using this code in any environment, its important to realize its limitations:

  • The Web Part makes the assumption that the name of the mailbox is same as the username. Although, this is true for most of Exchange deployments, this is not true for all. I have seen deployments, where FirstName.LastName is used as name of the mailbox while username is entirely different.
  • In large Exchange deployments, there are multiple servers being used, and user's mailbox is located to geographically closest server. The Web Part does not take into account of this as we are using single hard coded server name for all the users.

Due to the reasons cited above, the usage of this approach is very limited to small deployments. I  have seen this approach being used in one of the deployment at a University :)

Cheers,

Madhur

Posted by Madhur | 2 Comments
Filed under: , ,

Switch the sharepoint webpart page Display Mode into Edit Mode and Vice Versa

Recently one of my clients had a requirement to go into Edit mode of a sharepoint page using a manual link displayed in quick launch. He was lazy of going to Site Actions -> Edit Page and then again going somewhere else for Exit Edit Mode :)

To give some background for beginners, whenever we click Edit Page on Site Actions menu,
the pages switches into a different display mode called Design Mode. Its not a feature of
Sharepoint, but ASP.NET 2.0 Webparts Framework.

Background


According to the framework, the page can have 4 different modes:

  • BrowseDisplayMode : Represents the default display mode.
  • CatalogDisplayMode : Used for adding controls from a catalog of controls to a Web page
  • ConnectDisplayMode : Displays a special UI for users to manage connections between Web Part controls.
  • DesignDisplayMode : Used for changing the layout of Web pages containing Web Part controls.
  • EditDisplayMode : Displays a UI from which end users can edit and modify server controls

The WebPartManager control provides a programmatic interface, making it possible to switch the Web Part Page between browse, design, and edit display modes.

For example, to programmatically switch the current page into design mode, you can simply add a link control with an event handler that sets the DisplayMode property to DesignDisplayMode.


WebPartManager1.DisplayMode = WebPartManager.DesignDisplayMode;
Although this would work technically, but it would not give up the visible changes to page, like visibilty of Page Editing toolbar, webpart zones etc.

This visual magic is done by lot of Javascript which is executed when we click Edit Page on Site Actions menu.

To build this link control, we need to figure out the javascript code which causes this behaviour.

This would be present in default.master. If we open Site Actions menu and do the View Source on IE.
Here is the code of our interest :

<td class="ms-siteactionsmenu" id="siteactiontd">
<span><a href="javascript:MSOLayout_ToggleLayoutMode();">Edit Page</a><;/span>

As you must have figured out, its the MSOLayout_ToggleLayoutMode() function which does all the magic of turning the current page into Edit page. This javascript also calls the server side code which executes this statement


WebPartManager1.DisplayMode = WebPartManager.DesignDisplayMode;
//(This can also be demonstrated, but its beyond scope)

Building the Webcontrol

Armed with this knowledge, we can build a simple webcontrol which we will switch the page into Edit mode and vice versa.
Below is the code for the same. Its the simplest webcontrol you will see ever.


using System;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Web.UI;
namespace SPUtil
{
public class SPEditMode:System.Web.UI.WebControls.WebControl
{
HtmlAnchor btnLink;

protected override void CreateChildControls()
{
WebPartManager wp = WebPartManager.GetCurrentWebPartManager(Page);

const string url="javascript:MSOLayout_ToggleLayoutMode();";

btnLink = new HtmlAnchor();

if (wp.DisplayMode == WebPartManager.BrowseDisplayMode)

btnLink.InnerText = "Edit Page";
else if (wp.DisplayMode == WebPartManager.DesignDisplayMode)
btnLink.InnerText = "Exit Edit Mode";
else
btnLink.Visible = false;

btnLink.HRef = url;

Controls.Add(btnLink);
base.CreateChildControls();
}

}
}

I have used HtmlAnchor rather than LinkButton or SPLinkButton since its lightweight on the server and We are not performing any special processing which is present in server controls.

One point to be worth noting: This link would be visible to all including visitors. For used in practical scenarious, the control should be hidden for other than members and Administrators.

I was using this link in user's MySite and hence I did not have that case :)
Posted by Madhur | 6 Comments

Passing multiple filter values to EWA pivot table filter cell

Filter webparts in WSS are a great way to provide filtering in many different webparts like List View, Business Data Catalog, Excel Web Access etc.

They are also great way to provide data view personalization when used with Excel services coupled with Analysis Services of SQL server.

This is described in detail on Excel Team blog here

In this post, we will see how to create a simple filter webart which provides values to pivot table filter cell.

I am mentioning pivot table filter cell exclusively because the filter webpart examples on MSDN has to be slightly modified to provide values
to filter cell.

Basically, we are building a filter provider which


  • Implements the ITransformableFilterValues interface.

  • The interface required few properties to be overridden, the most important of them is public virtual ReadOnlyCollection&lt;string&gt; ParameterValues.
    This parameter contains read only collection of strings which are passed to the consumer webpart.

  • The webpart returns the instance of itself through a public method SetConnectionInterface()

using System;
using System.Collections.Generic;
using System.Web.UI;
using System.Web.UI.WebControls;
using aspnetwebparts = System.Web.UI.WebControls.WebParts;
using wsswebparts = Microsoft.SharePoint.WebPartPages;
using System.Collections.ObjectModel;

namespace ExcelFilters
{
public class ExcelFilters : aspnetwebparts.WebPart, wsswebparts.ITransformableFilterValues
{
CheckBoxList cblRegionList;
ListItem cbitemRegion;
string[] countries = new string[]
{ "Canada", "Central America", "Mexico", "South America", "United States",
"Albania", "Andora", "Austria", "Aizerbejan", "Belarus", "belgium",
"Bosnia and Hersegovina", "Bulgaria" };

public virtual bool AllowMultipleValues
{
get
{
return false;
}
}
public virtual bool AllowAllValue
{
get
{
return true;
}
}

public virtual bool AllowEmptyValue
{
get
{
return false;
}
}
public virtual string ParameterName
{
get
{
return "Geography";
}
}

public virtual ReadOnlyCollection ParameterValues
{
get
{
string[] values = this.GetCurrentlySelectedGeographies();
List param=new List();

foreach (string str in values)
{
if(!string.IsNullOrEmpty(str))
param.Add(str);
}
return values == null ?null :new ReadOnlyCollection(param);
}
}

protected override void CreateChildControls()
{
cblRegionList = new CheckBoxList();
cblRegionList.AutoPostBack = true;
Controls.Add(cblRegionList);

foreach (string region in countries)
{
cbitemRegion = new ListItem();
cbitemRegion.Text = region;
cblRegionList.Items.Add(cbitemRegion);
cbitemRegion = null;
}

base.CreateChildControls();
}

[aspnetwebparts.ConnectionProvider("Region Filter", "ITransformableFilterValues", AllowsMultipleConnections = true)]
public wsswebparts.ITransformableFilterValues SetConnectionInterface()
{
return this;
}

public string[] GetCurrentlySelectedGeographies()
{
String[] choices = new String[countries.Length];
bool anythingSelected = false;

for (int i = 0; i < cblRegionList.Items.Count; i++) { if (cblRegionList.Items[i].Selected) { anythingSelected = true; choices[i] = cblRegionList.Items[i].Text; } } if (!anythingSelected) choices = null; return choices; } protected override void RenderContents(HtmlTextWriter output) { this.EnsureChildControls(); RenderChildren(output); } } }

Now, rather than explaining the simple webpart, which is already explained by Microsoft, I will point out key points which differentiates it from the examples at many places.

If we notice GetCurrentlySelectedGeographies() method, it returns the string array of selected geographies. However, the length of this string array is constant and is equal to number of selectable items, which causes the other items in string array to become null.

In the MSDN example here the string array is directly passed by converting it to ReadOnlyCollection of strings. This will not work since it includes the null items
in the array as well.

string[] values = this.GetCurrentlySelectedGeographies();
return values == null ?null :new ReadOnlyCollection(values);

Pivot table Report filter cell, expects a Collection of values only which are selected. Thus we need to modify the code as follows

string[] values = this.GetCurrentlySelectedGeographies();      
List param=new List();
foreach (string str in values)
{
if(!string.IsNullOrEmpty(str))
param.Add(str);
}
return values == null ? null :new ReadOnlyCollection(param);

This code snippets, creates a new List which includes all the string objects except the null items from the original string array which is returned by GetCurrentlySelectedGeographies()

Posted by Madhur | 1 Comments

RSS Reader webpart with tab support and asynchronous periodic data refresh using AJAX

I have recently written my first article on Codeproject.

The article describes the development of AJAX enabled webpart on MOSS 2007 SP1.

The cool thing about it is the that it also utilizes the AJAX Control Toolkit including programmatically adding and styling TabContainer and TabPanel controls which can be quite tricky for a beginner. The article can be found here:
http://www.codeproject.com/KB/sharepoint/RssReaderAjax.aspx
Posted by Madhur | 1 Comments

Implementation of Logging and Instrumentation Application Block in MOSS 2007

Continuing the series of articles on Enterprise Library, I have written the first article
on the implementation of Logging Application Block in MOSS 2007. Due to inclusion of formatted code and images, I have directly uploaded this on Codeproject.

The article can be viewed here
Posted by Madhur | 2 Comments

Get RSS Url for a list or Document Library Programmatically

Recently I developed an RSS Reader webpart which would take the RSS URL of list to render its feeds. Very much same like the out of the box webpart with the exception that it was AJAX enabled. I would post the details of that part on the blog when I am finished.


An idea came to me that wouldn’t it be nice to also enable users to just give the list URL rather than RSS URL since would reduce the few steps on the user’s side. I thought it would be just a matter of accessing the RssUrl property of SPList object, but to my surprise it was not to be. There is no property such property in the API, so I decided to write my own function for it.


Let’s analyze the RSS URL of a list or a library. Whenever the user clicks on View RSS feed on a library, here is how SharePoint constructs the URL:


http://<server>/<site>/_layouts/listfeed.aspx?List=%7B14206B18%2DF68F%2D479B%2DBC84%2D15EE48D19D7D%7D


Listfeed.aspx is the inbuilt RSS viewer of sharepoint which accepts a parameter which is the GUID of the list. %2D tokens refer to ‘-‘characters which exist inside the GUID. Considering all this, it’s easy to write a function which will return the RSS URL. Here is the code for the same:


private string MakeRssUrl(string rawurl)

{

try

{

Uri url = new Uri(rawurl, UriKind.Absolute);

if (url.GetLeftPart(UriPartial.Path).Contains(“_layouts/listfeed.aspx”))

{

return rawurl;

}

else

{

string rssurl;

using (SPWeb web = new SPSite(rawurl).OpenWeb())

{

SPList list = web.GetListFromUrl(rawurl);

rssurl = web.Url + "/_layouts/listfeed.aspx?list=" + list.ID;

return rssurl;

}

}

}

catch (UriFormatException e)

{

return string.Empty;

}

}


The code is pretty self explanatory. The argument to function is list URL or RSS url. We first check if the URL is RSS URL itself, and if it is we just return. Otherwise, if it’s a list URL, we create a SPList instance, grap the GUID and contatenate it with the site URL and listfeed.aspx.


Note that the function does not validate if the given URL was actually a valid list URL or not. The exception handling for that case should be left to the caller of the function.

Posted by Madhur | 3 Comments

Reveal Unknown Error on Sharepoint 2007 pages

How often you have encountered the infamous Unknown Error on Sharepoint 2007 pages. If you are a Sharepoint developer, chances are that innumerable times :)

However, if you are also seasoned ASP.NET developer as well, you also know the trick behind it to reveal them., which I am going to give it here.

The trick is that ASP.NET Framework wants to show the real error message, but its the sharepoint which abstracts the message from the user. This is good for production systems since those cryptic error message may not be user friendly. Or revealing those full stack trace could cause potential security concerns.

But for an experienced developer, this is almost must to turn them off. This is achieved by turning off custom errors in web.config

Find the web.config for the site you normally use as your development site. Locate this tag

<SafeMode ... CallStack="false" ...> and change it to CallStack="true"
Set <customErrors mode="On" /> to mode="Off"
Set <compilation batch="false" debug="false"> to <compilation batch="true" debug="true">

Now you will get the full stack trace as soon as the error is raised.
Posted by Madhur | 0 Comments

Unknown Error on Manage Form Templates Page (Manageformtemplates.aspx)

Getting Unknown Error on this page?.

No helpful clue in Sharepoint log or Event Viewer?


This can happen if database record in the list of published InfoPath forms had a null where it shouldn't be.


Thanks to this blog, there is small code snippet given here which will delete the culprit entry.


http://geekswithblogs.net/HammerTips/archive/2007/12/07/unable-to-manage-form-templates.aspx



But make sure that you run this script under the debugger and only execute the delete operation for the offending form, otherwise it will delete all the published forms in your farm.

Posted by Madhur | 0 Comments

Performing Operations on Infopath 2007 Contact Selector Control using Managed Code

Infopath 2007 provides an out of the box contact selector control to select the user and validate against the Active Directory.


In this blog, We will see, how to get more out of this control. Performing some advanced functions using managed code.


For basic usage of this control, see this blog entry on infopath blog:

http://blogs.msdn.com/infopath/archive/2007/02/28/using-the-contact-selector-control.aspx


To start with basics, this control has predefined schema, since it simultaneously stores the display name, account id and account type.

  user display name
  DOMAIN/user account
  user or group type
 
It is interesting to note that this control behaves like a repeating control, in a sense,
the user can select multiple users from the same control. Internally, the XML schema
shown above is repeated for multiple users.
 
2. Get the Display Names and Login Names for all users in contact Selector Control
 
To get the display names and login names, we just need to parse the generated XML
schema. We will store the names and login names as comma separated values.
Assuming that our control name is gpContactSelector, the code below extracts the
display names and login names.
 

XPathNavigator xNavMain = this.XmlFormView2.XmlForm.MainDataSource.CreateNavigator();

XmlNamespaceManager xNameSpace = new XmlNamespaceManager(new NameTable());

xNameSpace.AddNamespace("my", "http://schemas.microsoft.com/office/infopath/2003/myXSD/2007-11-20T20:01:12");

XPathNodeIterator[] nodes = new XPathNodeIterator[4];

nodes[0] = xNavMain.Select("/my:myFields/my:gpContactSelector/my:Person/my:DisplayName", xNameSpace);

nodes[1] = xNavMain.Select("/my:myFields/my:gpContactSelector/my:Person/my:AccountId", xNameSpace);

nodes[2] = xNavMain.Select("/my:myFields/my:gpContactSelector/my:Person/my:AccountType", xNameSpace);

string names=string.Empty;

string accid=string.Empty;

for (int j = 0; j <>

{

for (int i = 0; i <>

nodes[i].MoveNext();

if (nodes[2].Current.ToString() == "User")

{

names = names + nodes[0].Current.ToString()+";";

accid = accid + nodes[1].Current.ToString()+";";

}

        }
 
The code above is pretty self explanatory. It parses the generated XML Schema
and stores the Names and login ids in two variables, names and accid as semicolon
separated values. Further operations can be then performed on these.
 
2. Sending Emails to All users selected in Contact Selector
 
To send emails, we obviously need email addresses of the contacts selected.
However, contact selector does not automatically grabs out the email addresses
of the contacts. To get the email addresses, we will first extract the login names
from the XML schema and then use the Microsoft.SharePoint.Utilities.SPUtility.GetFullNameandEmailfromLogin
class to get the email addresses.
 
The code below accepts the login names as semicolon separated values and builds
a string containing email addresses as semicolon separated values.
 

private string GetEmails(string final)

{

char[] a = { ';' };

string[] loginIds = final.Split(a, StringSplitOptions.RemoveEmptyEntries);

string[] emailids = new string[loginIds.Length];

for (int i = 0; i <>

{

Microsoft.SharePoint.Administration.SPGlobalAdmin ga = new Microsoft.SharePoint.Administration.SPGlobalAdmin();

string dispname, email;

Microsoft.SharePoint.Utilities.SPUtility.GetFullNameandEmailfromLogin(ga, loginIds[i], out dispname, out email);

emailids[i] = email;

}

string finalstring = string.Empty;

for (int i = 0; i <>

finalstring = finalstring + emailids[i] + ";";

return finalstring;

}

 
 
 
Now, we can use using System.Net.Mail namespace to send mails. This namespace
overrides the System.Web.Mail used in .NET 1.1. For those who are new to this
namespace, below is the sample code given to send mail.

private void SendMail()

{

MailMessage mail = new MailMessage();

mail.From = new MailAddress("Admin@domain.com", "Administrator");

char[] a = { ';' };

string[] emailIds = to.Split(a, StringSplitOptions.RemoveEmptyEntries);

for (int i = 0; i < style="">

mail.To.Add(new MailAddress(emailIds[i]));

mail.Subject = "New Meeting Request";

mail.Priority = MailPriority.Normal;

mail.IsBodyHtml = true;

mail.Body = GetBody();

new SmtpClient("smtpserver").Send(mail);

}

Posted by Madhur | 12 Comments
More Posts Next page »
 
Page view tracker