Wednesday, September 26, 2007

Creating custom views for MS CRM 3

Any modification made to the main or META-database is considered unsupported customization. Not very strange, since you never know what new fields or tables Microsoft might add to the database.

Sometimes, however, especially when working with reports, it can be useful to create your own views that aggregate data to your needs. Adding a view to the CRM database might be the easiest and you will probably be ok as long as you are a bit careful when naming the view. However, you don't have to do it this way. You can also create a new database on the same server and create your view in this database. To be able to access the views (and if you still want to play unsupported, tables), just add the database name in front of the viewname. For instance:

SELECT * FROM MyCompany_MSCRM.dbo.SystemUser;

The "dbo" part doesn't have to be there but is added by the view creator GUI, so I left it there to avoid any misunderstandings.

Unfortunately, the "Add Table" function in the GUI doesn't support selecting views from other tables even though it is supported in the database.

As far as I know, it is also possible to connect to a view in a database on another server. I don't know how this is done and don't really see the relevance from a MS CRM 3 perspective, but if I find out how, I will let you know.

Gustaf

Friday, September 21, 2007

Silverlight and Dynamics CRM

Silverlight is a new product from Microsoft that can be viewed as the same sort of plug-in as for instance Flash, Java Applets or ActiveX. There are two feautres that make it stand out from the rest, namely:
- Vector based graphics built on the new Windows Presentation Foundation (a part of .NET 3.0). The definition of the GUI is definied in a langauge called XAML.
- From Silverlight version 1.1, you can write managed code. In other word you can finally start working with C# or VB.NET on the client side instead of the irritating language javascript.

These two features make the foundation for a more user friendly and more rich experience of using the web, something at least I have been longing for. Being able to use managed code/CLR-code, will make web development a lot easier and speed up the process of making more robust and reusable programs.

On the CRM Team Blog you can download an example of how silverlight can be used in conjunction with Dynamics CRM 3. I saw that some people had had some problems with it, and I havn't had time to test it myself yet, so be advised.

Here is the link: http://blogs.msdn.com/crm/archive/2007/06/19/crm-and-silverlight-integration.aspx


Gustaf Westerlund
CRM and SharePoint Consultant

Humandata AB
www.humandata.se

Sunday, September 16, 2007

Office 2007 + CRM = true

Michael Höhne is one of the most generous and knowledgable people in the Microsoft CRM 3 community. His webpage http://www.stunnware.com/crm2/ hosts lots of very interesting articles, both for advanced Microsoft CRM developers and more novice Microsoft CRM application professionals. For instance, he has a very interesting posting showing an example of how to use the new file format OpenXML that comes with Office 2007 in conjuction with Microsoft CRM 3 to make very powerful software with almost no effort (ok, a little effort :)

Have a look: http://www.stunnware.com/crm2/topic.aspx?id=OpenXml

Gustaf Westerlund
CRM and SharePoint Consultant

Humandata AB
www.humandata.se

Request hotfixes online

The most common way of requesting a specific hotfix for Microsoft Dynamics CRM or SharePoint is to call Microsoft Support (they cannot be downloaded directly). However, there is also a webpage where you can request a hotfix online. Have a look an add it to you favorites, you will probably need it.

https://support.microsoft.com/contactus2/emailcontact.aspx?scid=sw;en;1410&WS=hotfix

Gustaf Westerlund
Microsof CRM and SharePoint Consultant

Humandata AB
www.humandata.se

Fake lookups

As I mentioned before, when I was working with the company Cybernetics, there is a way of creating false lookups when it might not be possible to do so normally (for instance system-system relationsships).

The blog http://andrewn23.blogspot.com/ describes in detail how this is done, so, if you are interested, please have a look. This technique can of course be enhanced in many ways.

Also, I am not certain if this technique is supported since Microsoft might decide to change the way some of these more internal pages (lookupsingle.aspx) work, hence breaking the customization.

The blog also mentions some hazards of using allcolumns that are very worthwhile reading (I like to use them to increase development time and change to specific attributes when I get performance issues).

So, take some time and have a look at it, I am however sad to see that the last entry was in october 2006.

Gustaf Westerlund
CRM and SharePoint Consultant

Humandata AB
www.humandata.se

Thursday, September 13, 2007

Selecting multiple records from a grid in MS CRM 3

Using isv.config customizations you can add buttons to the menybar above a grid (next to "More Actions"). A special technique has to be used to fetch the selected records and Ronald Lemmen wrote some about it this summer, here: http://ronaldlemmen.blogspot.com/2007/06/fetching-selected-records-in-grid.html . I had reason to try it and ran into problems when the url of the file containing the javascript was on a different server (different tcp-port has the same characteristics as a different server from an IE viewpoint). The window.dialogArguments was null. I tried to modify the settings in IE but found no way of getting around the problem except for setting up a virtual directory bellow the CRM-site containing the html-file.

So, if you run into similar problems, try moving the html-file to a directory on the same server (from an IE standpoint).

Gustaf

User based Timezones in SharePoint 3

I have been on the look for this feature for some time. I've heard it's there but never found it. Now my colleague Sebastian Tegel found it. Have a look if you're interested!

http://sebastiant.blog.com/2079971/

Gustaf

Friday, September 07, 2007

Environment independent solutions

Developing CRM solutions that are environment independent is not trivial. By just adding a webservice you are making your code dependant on the CRM customization of that system. If you move the solution/dll:s to another system you will be running a risk of your code not working. This is especially problematic for ISVs since they never know what system their product will be installed on. However, there is a good way to handle it. The best way is to first use a plain out-of-the-box CRM system and use the program

C:\Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin\wsdl /out:c:\temp\myCRMProxyClass.cs http://localhost:5555/mscrmservices/2006/crmservice.asmx?wsdl

This will create a file called myCRMProxyClass.cs is c:\temp.

Add this to your project. This is in essence the same this as adding a web reference and adding "using CRMSDK" or whatever you named it. Hence you can access all the CRM webservice classes.

Then create the service-instance and set the url by using the registry-key added during the installation of Microsoft CRM. Have a look here if you want to know how. http://ronaldlemmen.blogspot.com/2007/08/creating-environment-independent.html

If you have other environment specific data, it should be placed in the web.config / appsettings (if using a normal web-site/virtual directory) or you can use the AssemblySettings.dll (has to be downloaded, not from Microsoft) to create a assembly specific config-file, alternatively, add you own keys to the registry.

I hope I have given you a few hints on how this is done. Happy coding!

Gustaf

Activate a Workflow using Javascript

Workflows are a great tool. They are quite simple to create, even non-programmers can actually create them and they are flexible. However, there are some problems with workflows in CRM 3 (how workflows are handled in Titan/CRM 4 will be interesting to see) for instance, a workflow cannot be activated when an entity is updated, only when it is created, when it changes status, when it changes owner and of course, manually.

Mitch Milam has written an interesting post on his blog concerning how to activate a workflow using javascript. You can read about it here: http://blogs.infinite-x.net/2007/06/15/launching-a-workflow-rule-from-javascript/.

A bit of caution is however advised. I would actually prefer to have the workflow being activated by a PostUpdate-callout instead, to make sure that it is only run once. Activating a workflow on the on-change event can be a bit hazardous since it is very possible that the event is triggered several times on the same form before it has been saved.

Never the less, it is a good posting and can probably give you an idea or two about how to programmatically activate CRM workflows. If I wasn't block from using my own virtual directory or website with my own aspx-files, I would also prefer to trigger the workflow from an aspx-page and call the aspx-page using AJAX. Have a look at the class ExecuteWFProcessRequest in the CRMSDK as it enables you to execute a workflow using the web service.

Gustaf

Wednesday, September 05, 2007

SQL COALESCE

SQL Reporting Services is a great tool. It allows you to create flexible and exportable (to pdf etc) reports. However, things sometimes get a bit complicated and a normal problem that you face is the problem of columns containing null-values that you want to use for calculations. For this there is a nice function called COALESCE which replaces null with the value directed.

For example:

SELECT firstname, lastname, age FROM contacts;

Might return the list:

firstname        lastname        age

John            Smith            5

Peter            Jacks            null

If you want to use the age-column to do some calculations or you just don't want it to be null, just change your SELECT-statement to:

SELECT firstname, lastname, COALESCE(age, 0) As NewAge, COALESCE(age, 0) * 2 As DoubleAge FROM contacts;

This will give you the result:

firstname        lastname        NewAge    DoubleAge

John            Smith            5        10    

Peter            Jacks            0        0


 

A useful and simple functions, to get you out of some nasty trouble when doing for instance LEFT OUTER JOIN, RIGHT OUTER JOIN or FULL OUTER JOIN when the risk of returning null is great.

Gustaf

Humandata AB

Saturday, September 01, 2007

Javascript – define functions and programmatically attach events

Since we're on the subject of javascript I might as well add some more stuff. First of all, how to define you own functions, then how to programmatically bind a function to an event, for instance, onchange of a field.

This mainly concerns Microsoft CRM but the technique can also be used in SharePoint, although javascripts aren't as common.

First off, how to define a function.

There are actually several different ways, for instance the normal:

function myfunc1(param1, param2)

{

alert("myfunc1");

}


 

And the one with a weird sort of syntax:

var myfunc2 = function(param1, param2)

{

alert("myfunc2");

}


 

In CRM 3, these can actually be defined within the onLoad, hence creating some ways of code reuse. These functions will be available for all event in the page since they have been declared in the onload.

This leads me to another point, that a function has to be defined before it can be used. Hence, in the example above, myfunc2 can call myfunc1, but not the other way around. If you try, you will get strange error messages saying stuff like "null is not 'null'".

Secondly, you can bind events to functions using code. There are several ways of doing this as well (what did you think!?) and they mainly differ based on which browser you are using. If you want to read some more on the subject, if you want some more info on event binding in javascript, please have a look at this very instructive page: http://developer.apple.com/internet/webcontent/eventmodels.html

First the simple method by just setting the onchange attribute.

crmForm.all.ext_myfield.onchange = myfunc2;


 

or

document.getElementById("objectid").onchange = myfunc2;


 

This has the advantage of a quite simple syntax and, if you want it, the override of previous events.

The second way of doing this is using the attachEvent method. As far as I've read, there are some differences with syntax between browsers, but since neither MS CRM nor SharePoint work in any acceptable way in anything but IE, this is not a big issue. Here is some example syntax:

crmForm.all.ext_myfield.attachEvent("onchange", myfunc2);

or

document.getElementById("objectid").attachEvent("onchange", myfunc2);


 

The main advantage of this method is that you can attach several functions to one event, hence, not overriding other handling.

Bellow is an example html-file which demonstrates this:

<html
xmlns="http://www.w3.org/1999/xhtml">

<head>

<script
type="text/javascript">

var myfunc1 = function()

{

alert("myfunc1");

}

var myfunc2 = function()

{

alert("myfunc2");

}

var loading = function()

{

alert("running");

document.getElementById("btTest").attachEvent("onclick", myfunc1);

document.getElementById("btTest").attachEvent("onclick", myfunc2);

}

</script>

</head>

<body
onload="loading();">


<button
id="btTest">Click me</button>

</body>

</html>

If you copy this to a file called test.htm and test it, you will see that when clicking the button, myfunc2 will be executed first and then myfunc1.

So, all put together, this can be used to a great extent in MS CRM 3, where you can now put all code in one place, the onLoad script, define some functions and then bind the onchange event of all the attributes you like in the same code. Remember to define the functions before you attach the events.

I havn't seen CRM 4 / Titan yet, and I don't know what features it will have in this aspect, but if it includes the feature to be able to add a global javascript file that will be included on all pages, this would be very very useful when using techniques like this.

And remember, javascript is an ugly beast, but it is very useful! J

Gustaf Westerlund

Humandata AB

Window.close() in javascript acts a bit strange…

Javascript is a strange language, it is often confused to be Java, which is very far from right, and the syntax can often be thought to be as consistent as C# or Java. Not the case. Javascript is a strange and weird beast, that when tamed can be a very strong ally. Taming it can however really get on your nerves!

There is one method working on the object window, called close(). Hence it is called using window.close(). It does the simple thing of closing the current browser window. No more, no less.

Well, actually, it does it in a strange way. Have a look at the following code that is executed in body onload. What is going to happen?

Alert("Start");

Windows.close();

Alert("End");


 

I will give you two options:

Option 1:

  1. A dialog window showing "Start" will show.
  2. The current browser window will close.

Option 2:

  1. A dialog window showing "Start" will show.
  2. A dialog window showing "End" will show.
  3. The current browser window will close.

Well?

You would think that option 1 is correct, well, it isn't, the script is executed according to option 2. For some reason, window.close() doesn't actually close the window, it signals it to close and when the onload has reached its end, it is closed. I havn't checked to see if this behavior only happens when executed in onload or if it is the same all over. Be aware though, it might not work as you'd expect!

Gustaf

How to add a virtual directory below your SharePoint or CRM website

Sometimes you would like to create a virtual directory bellow for instance the SharePoint main site. Lets say you MOSS is installed at http://intranet and you would like to create your own virtual directory with your custom made aspx-pages at for instance http://intranet/custom. Well, if you use the IIS Management Console, add a virtual directory to the website, and add an aspx-page to that virtual directory, you will get errors saying that the application cannot find certain assemblies. This is due to the fact that these assemblies have been referenced in web.config using <add assembly="name-of-assembly" / > tags. Due to the way IIS works, these references are automatically inherited to all sub-directories, in this case, you virtual directory.
So, what to do? The simplest way to handle this is to add <remove assembly="name-of-assembly" / > tags to the web.config that resides in your virtual directory. This will explicitly remove the reference created by the parent website. Remove them one by one and soon you will be able to run your code just as you would like.
One good advantage of using adding your code to a virtual directory bellow the main site instead of using a new website at for instance another host header or TCP port, is that you can address your code using server relative urls. In essence, if you created an aspx-page at http://intranet/custom/mycustompage.aspx. If you address this page from the SharePoint, you can use the url: /custom/mycustompage.aspx without http:// or the servername. This also gives you the benefit that the page is on the same server, hence no security problems with IE.
In SharePoint, make sure that the address of your virtual directory is NOT bellow any wildcard included och explicitly included sites (in the setup of the webapplication in central admin).
Please note that adding virtual directories bellow the CRM root is an unsupported customization. This is due to the fact that Microsoft might release an update that adds a directory with the exact name of you virtual directory. This would give rise to either the fact that your code stops working or more likely that the content of the directory added by Microsoft cannot be accessed.
Gustaf Westerlund
Humandata AB

Friday, August 31, 2007

Showing SQL Reporting Services reports in Microsoft Office SharePoint Server 2007-08-31

Today I was working with a customer who wanted to show their SSRS 2005 reports in SharePoint. As many of you might be aware, from SP 2 of SQL 2005 there is an option of storing you reports on the SharePoint Server. Very nice, but I havn't really tried getting that to work with MS CRM 3, so the SSRS I was using ran in normal mode, i.e. not SharePoint integration. However, I did want to show the reports and the reports explorer in SharePoint. For SharePoint v2 there has long existed RSWebparts.cab, which is a web part package consisting of two web parts; report viewer and report explorer.

After a bit of looking I couldn't find it for SharePoint v3, and found some information saying that the old webparts actually still worked well. So, after the always so tedious work of trying to locate the file, RSWebparts.cab (it cannot be downloaded from the Internet as far as I know) I installed it using stsadm –o installwp -o addwppack -force –filename "C:\temp\RSWebParts.cab" –globalinstall (use stsadm –help addwppack to see possible syntax), and adding the RSWebparts.dll as a safecontroll (with the correct strong name). I got it to work, just as nicely as it did in SharePoint v2. So, my suggestion to you, if you have the same need, use the old one.

And where can you find the file? It is installed in some impossible path bellow c:\program files\SQL Server of a SQL 2000 SP2 (or greater) server. So, get your hands on it, and don't let go. I don't think I can publish it here though, due to copyright reasons.

Gustaf

Thursday, August 30, 2007

Entity and attribute information

I was reading a bit on the CRM Teams blog and read a posting on the fact that there is an aspx-page that lists all entities that can also show all attributes of the entity. Read the post here or just brows to: http://<yourservername>/sdk/list.aspx.

You learn something new, every day J.

Gustaf Westerlund

Humandata AB

How to stop infinite recursions/loops in PostUpdate Callouts

As many of you who work with development in Microsoft CRM know, callouts is a great tool. Lots of different tasks can be left to be handled by callouts and they are even run when offline data is resynced to the main system. However, a common problem when working with PostUpdate callouts is that the code wants to update the object that created the callout, hence executing the calloutcode again, which might result in an endless recursion.

I recently ran into this problem working at a customer of mine, and found a nice generic way of solving it using the thought behind the design pattern object factory or single instance.

I guess you want some code! Well, the basis of this solution is to have a static instance of a collection that keeps track of if a certain key just has been run. I use the collection StringCollection. It is simple, and just what I needed in this case:

private
static System.Collections.Specialized.StringCollection justtreated;


 

Then I created a method that is supposed to return false the first, third, fifth… time it is called and true every second, fourth, sixth… time it is called with a certain key.


 

private
bool JustDone(string key)

{

    if (justtreated == null)

    {

        justtreated = new System.Collections.Specialized.StringCollection();

    }

    if (justtreated.Contains(key))

    {

        justtreated.Remove(key);

        return
true;

    }

    justtreated.Add(key);

    return
false;

    }

}

The method is not very complicated. First off, it makes sure that the static variable has been created. Then it checks if it can find the key string in the string collection. If it can it will remove it and then return true. If it cannot find the key, it will add it to the collection and return false.

Since, this method is working with a static variable that will remain in memory between instantiations of the class, hence the data will remain between PostUpdate calls.

Then in the beginning of the PostUpdate method just call JustDone() with the object key (or some other unique key) and if it is true, just "return".

Gustaf Westerlund

Humandata AB

Thursday, August 23, 2007

August 2007 MS CRM VPC is here!

I've been bad at reading all blogs recently. Full of work and my private time is now mostly filled with my daughter Nora, hence less time to read up on blogs.

We have all (?!) been waiting for the new MS CRM VPC and it has now been released. It can be downloaded from here:

http://www.microsoft.com/downloads/details.aspx?FamilyID=57394bc8-5ecc-422e-a066-34189f48f8c8&displaylang=en

I havn't downloaded it myself yet, but I will soon.

Gustaf Westerlund
CRM and SharePoint Consultant

Humandata AB
www.humandata.se

Java, javascript, jscript and so forth

When developing software customizations for SharePoint and MS CRM 3, javascript is often used. It can easily be confused with the totaly unrelated language java. Mennotk's blog has a posting linking to another blog that describes in a bit more detail what javascript, jscript and so forth really is. Being a developer in MS CRM and SharePoint, where this is one of my main tools, I found this quite enlightening and it sheds some light on some question marks I personally had on the subject of javascript and client side scripting.

Please have a look if you are interested: http://blogs.msdn.com/gauravseth/archive/2007/08/15/the-world-of-jscript-javascript-ecmascript.aspx

Gustaf Westerlund
CRM and SharePoint Consultant

Humandata AB
www.humandata.se

Wednesday, August 22, 2007

Javascript event handling and dynamic lists in SharePoint

As many of you already know and probably have used, it is possible using javascript to create dynamic drop-down-lists. For instance one drop-down list that selects country and another that lets you select a city in that country. Depending on which country you select, you want the options in the city drop-down to change.

This is a quite common task in MS CRM where javascript is a common tool. In SharePoint however, javascript isn't as common. I am currently working with a colleague of mine, Sebastian Tegel, at a large swedish customer of ours. They had this requirement on some metadata in a SharePoint library so Sebastian and I used the same principles as I usually use in MS CRM to create dynamic drop-down lists in SharePoint. Sebastian has a very nice and instructive posting on his blog on how this is done. Please have a look at it: http://sebastiant.blog.com/2023682/

In the posting before that, he also describes how to create your own custom edit form for a document properties page. This is very useful in these cases, so please have a look at that as well. (http://sebastiant.blog.com//custom+forms/).

Gustaf Westerlund
CRM and SharePoint Consultant

Humandata AB
www.humandata.se

Tuesday, August 21, 2007

Not so simple SharePoint and CRM integration

A customer of mine wanted to integrate their MOSS Ent with CRM 3. Specifically they wanted to be able to handle documents in CRM with SharePoint, not surprising since the document handling in MS CRM 3 is bad, if you are a bit positive. They were running 2 servers, one with CRM and one with MOSS Ent. Both with their own SQL 2005 installations (to avoid problems with SPN:s and such).

The solution? I based the solution on my previous posting from October 2006 where I described how to do this; Simple SharePoint and CRM integration (http://gustafwesterlund.blogspot.com/2006/10/simple-crm-and-sharepoint-integration.html). That was WSS 2 and both applications were running on the same server. So, not really the same setup, but, I felt, it shouldn't be a problem.

My plan was to create a SharePoint site using a specific custom template on the Account PostCreate callout. The callout would set the websiteurl attribute and an onLoad javascript would show a custom aspx-page that shows a documents webpart using the minimal.master (in order to hide all the SharePoint specific framing).

I started off by creating the PostCreate callout. Not a problem, just create a normal class file with the following content:

using System;

using Microsoft.Crm.Callout;

using Callouts.CRMSDK;


 

namespace Callouts

{

           ///
<summary>

           /// Summary description for Class1.

           ///
</summary>

           public
class Account: CrmCalloutBase       

           {

                      public Account()

                      {

                      }

                      public
override
void PostCreate(CalloutUserContext userContext, CalloutEntityContext entityContext, string postImageEntityXml)

                      {

                     
 

                                 CrmService service = new CrmService();

                                 service.Credentials = System.Net.CredentialCache.DefaultCredentials;


 

                                 service.CallerIdValue = new CallerId();

                                 service.CallerIdValue.CallerGuid = userContext.UserId;


 

                                 systemuser user = (systemuser)service.Retrieve(EntityName.systemuser.ToString(), userContext.UserId, new AllColumns());

                                 account acc = (account)service.Retrieve(EntityName.account.ToString(), entityContext.InstanceId, new AllColumns());       


 

// Code to create sharepoint site/subweb and set acc.websiteurl to the url.

                                 service.Update(acc);


 

                      }


 

           }

}


 

And add a web reference to the CRM webservice (look in the SDK to find the exact URL). As you probably can see, I called it CRMSDK (strange name actually, but it´s tradition).

And the callout.config.xml looked like this:

<callout.config
version="3.0"
xmlns="http://schemas.microsoft.com/crm/2006/callout/">

           <callout
entity="account"
event="PostCreate">

                      <subscription
assembly="Callouts.dll"
class="Callouts.Account" />

           </callout>

</callout.config>


 

Well, I modified the code and set some attribute of the account to something just to make sure it worked. It did.

How to create a site in SharePoint from one server? Well, first I tried adding Microsoft.SharePoint.dll which contains lots of nice stuff to access the SharePoint data. This was the technique I had used previously and thought it might be good this time too. But no, it can actually only be used on the same server as SharePoint and not on some other server.

There are still a lot of nice web services in SharePoint, I could probably find some web method that would let me create my site. I started by looking in the WssSDK…

But I could find anything useful. The natural webservice would of course be webs.asmx and it contains lot of nice functions to handle SharePoint webs, like GetWeb() and so on. But I found no way of creating a new site. In the admin.asmx I found a method called createSite() but that creates sitecollections which is not really what I wanted. (Each sitecollection has unique security handling, template and sitedefinition setup and a lot of other specific stuff) My customer has around 5000 customer and I would want to create 5000 sitecollections.

I started searching the Internet. But, no. All I could find was a blog entry by Nick Swan describing how to use admin.createSite() (http://weblog.vb-tech.com/nick/archive/2006/03/07/1472.aspx).

I contacted Pontus Haglund and Kalle Becker at Microsoft and my colleague Sebastian Tegel and asked them if they knew how to do this or could assist in any way. They didn't unfortunately.  

I was left with only one major option as I saw it; creating my own web service that used Microsoft.SharePoint.dll functions to create the web site (subweb). I had found a blog that described how to do this (http://blogs.ittoolbox.com/km/sharepoint/archives/creating-a-custom-web-service-for-sharepoint-13553). So, I thought that it shouldn't be that big of a hazel. To make sure my SharePointcode worked, I first created a normal windows forms application that created a site according to certain variables. I found that the following code worked very well:


 


string url = "";

SPSecurity.RunWithElevatedPrivileges(delegate()

{

SPSite siteCollection = new SPSite("http://intranet");

SPWebCollection subSites = siteCollection.AllWebs["customersites"].Webs;


 

SPWebTemplateCollection templates = siteCollection.GetCustomWebTemplates(1033);

SPWebTemplate template = templates[0];


 

SPWeb mySite = subSites.Add(name, Title, Description, 1033, template, false, false);

mySite.Update();

url = mySite.Url;

});


return url;


 

After a suggestion from my colleague Sebastian Tegel, I ran all the code with Elevated Privileges. Not sure it is actually needed but I added it to make sure that there isn't any security issues with the current user and permissions to create sites.

Now, I just had to follow the blog I had found. Again my misfortune struck me, as I was unable to make any sense of it. I got the web service to work as long as I didn't use any SharePoint functions. When I did, I got the following error:

System.IO.FileNotFoundException: The Web application at http://intranet could not be found. Verify that you have typed the URL correctly. If the URL should be serving existing content, the system administrator may need to add a new request URL mapping to the intended application.

   at Microsoft.SharePoint.SPSite..ctor(SPFarm farm, Uri requestUri, Boolean contextSite, SPUserToken userToken)

.

.

.


 

I was using the exact same code as I had tested in the forms test application so there wasn't any problems there.

The blog post mentions a lot about strong naming assemblies but it doesn't reference it with the strong name and my experience told me that this error probably was due to Code Access Security (CAS) problems. The assembly should be strong-named, placed in the GAC and referenced using the strong name.

I think some important pictures has fallen out of the blog why it doesn't make much sense. It did however mention that there was a walk-through on msdn that described the same thing. This was my next lead on the way to my goal of SharePoint document handling In MS CRM 3.

I found the walk-through (http://msdn2.microsoft.com/en-us/library/ms464040.aspx) and followed it minutely. It gave me a lot better feeling than the first blog posting I had found. In short, it described how to create a web service asmx-file that references a strong named assembly with a class of web methods in it. When created, the program disco.exe was used to create wsdl and disco files for the web service. When these had been created, the were renamed to become aspx files so that they can dynamically link to the webservice depending on where in SharePoint it has been referenced. (all sharepoint webservices can be found in the sub directory of /_vti_bin/ of any site, for instance the web service webs.asmx can be found in both the url http://intranet/_vti_bin/webs.asmx and http://intranet/customersites/webs.asmx). When the wsdl and disco files have been properly modified to fit the SharePoint standard of handling this feature, the new web service has to be added to the file spdisco.aspx which is a directory of all web services that are available.

After a lot of hazel, I got this to work, and with a strong named assembly, deployed in the GAC and run in the SharePoint context. I finally got it to work the way I wanted it to.

So, if you also want your own web service in SharePoint. I would suggest following the walk-through very thoroughly and I have a few pointers. My web service was called WebsExtra.asmx, my class was called Service, and the assembly "WSTools.dll".

  1. First, make sure the asmx-file works. Develop it using a normal web application. You should be able to address it. I had created a new web site using TCP port 8000 and hence tried: http://intranet:8000/WebsExtra.asmx. If that doesn't work, make sure you have strong named your assembly correctly, deployed it the the GAC (i.e. copied it to c:\windows\assembly) and addressed the class using the full strong name ("Service, WSTools, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=037c5a828198ba1e" in my case).
  2. Ok. That works. Good. Make sure the file also works when you placed it in the _layouts directory according to the walk-through. If not, you might still not be addressing the class with the full strong name.
  3. After I created the files WebsExtra.wsdl and WebsExtra.disco, copied them to the ISAPI directory, renamed them to WebsExtrawsdl.aspx and WebsExtradisco.aspx, and modified them to be able to handle the dynamic linking that SharePoint uses, I made sure that I could really see the aspx-files using IE. I found them at http://intranet/_vti_bin/WebsExtrawsdl.aspx and http://intranet/_vti_bin/WebsExtradisco.aspx .
    Also check the contents of them and make sure they follow the same structure as similar files in the ISAPI directory (for instance Webswsdl.aspx and Websdisco.aspx, which you also can find using IE). Make sure that the referencing to the web service really uses the name you have set and not the name used in the walk-through.At the bottom of this entry is a copy of the files I used and a copy of the result I got in IE.
  4. When both these files work, make the additions to the bottom of spdisco.aspx as well (within the discovery-tag though). This file can also be found using IE. You can probably guess it's url. Have a look at my version at the bottom of this entry, including the result in IE.
  5. When I got that working, I now tried to access the addresses:
    http://intranet/_vti_bin/WebsExtra.asmx?wsdl and http://intranet/_vti_bin/WebsExtra.disco. These aren't actually files that you can find in the ISAPI directory but are mapped to the aspx-files mentioned in 3 above. You can check this by checking one of the out-of-the-box web services like Webs.asmx.
  6. When you've got all these working properly, you should be able to add the web service as a web reference in some test project. (I usually use Windows Forms programs since they are the easiest to debug). If all has been set up properly, you should now be able to add the web service as a web refence. If the dialog gives you problems, it is usually due to errors in the wsdl or disco files. I had lots of these and was quite close to suicide but finally found them all. Most of the problems I had were due to the fact that I had used Visual Studio to edit the wsdl and disco aspx files. VS 2005 had been very "helpful" and had added ="" here and there, making the syntax erroneous. So, my suggestion, use notepad to copy paste the code from the walk-through. When that is done, you can use VS 2005 to modify the code.


     

WebsExtrawsdl.aspx

<%@
Page
Language="C#"
Inherits="System.Web.UI.Page"
%>

<%@
Assembly
Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
%>

<%@
Import
Namespace="Microsoft.SharePoint.Utilities"
%>

<%@
Import
Namespace="Microsoft.SharePoint"
%>

<% Response.ContentType = "text/xml"; %>

<wsdl:definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:tns="http://tempuri.org/"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
targetNamespace="http://tempuri.org/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">


<wsdl:types>


<s:schema
elementFormDefault="qualified"
targetNamespace="http://tempuri.org/">


<s:element
name="CreateSharePointSubWeb">


<s:complexType>


<s:sequence>


<s:element
minOccurs="0"
maxOccurs="1"
name="portalUrl"
type="s:string"
/>


<s:element
minOccurs="0"
maxOccurs="1"
name="subsite"
type="s:string"
/>


<s:element
minOccurs="0"
maxOccurs="1"
name="name"
type="s:string"
/>


<s:element
minOccurs="0"
maxOccurs="1"
name="Title"
type="s:string"
/>


<s:element
minOccurs="0"
maxOccurs="1"
name="Description"
type="s:string"
/>


<s:element
minOccurs="1"
maxOccurs="1"
name="templateId"
type="s:int"
/>


</s:sequence>


</s:complexType>


</s:element>


<s:element
name="CreateSharePointSubWebResponse">


<s:complexType>


<s:sequence>


<s:element
minOccurs="0"
maxOccurs="1"
name="CreateSharePointSubWebResult"
type="s:string"
/>


</s:sequence>


</s:complexType>


</s:element>


<s:element
name="HelloWorld">


<s:complexType
/>


</s:element>


<s:element
name="HelloWorldResponse">


<s:complexType>


<s:sequence>


<s:element
minOccurs="0"
maxOccurs="1"
name="HelloWorldResult"
type="s:string"
/>


</s:sequence>


</s:complexType>


</s:element>


</s:schema>


</wsdl:types>


<wsdl:message
name="CreateSharePointSubWebSoapIn">


<wsdl:part
name="parameters"
element="tns:CreateSharePointSubWeb"
/>


</wsdl:message>


<wsdl:message
name="CreateSharePointSubWebSoapOut">


<wsdl:part
name="parameters"
element="tns:CreateSharePointSubWebResponse"
/>


</wsdl:message>


<wsdl:message
name="HelloWorldSoapIn">


<wsdl:part
name="parameters"
element="tns:HelloWorld"
/>


</wsdl:message>


<wsdl:message
name="HelloWorldSoapOut">


<wsdl:part
name="parameters"
element="tns:HelloWorldResponse"
/>


</wsdl:message>


<wsdl:portType
name="ServiceSoap">


<wsdl:operation
name="CreateSharePointSubWeb">


<wsdl:input
message="tns:CreateSharePointSubWebSoapIn"
/>


<wsdl:output
message="tns:CreateSharePointSubWebSoapOut"
/>


</wsdl:operation>


<wsdl:operation
name="HelloWorld">


<wsdl:input
message="tns:HelloWorldSoapIn"
/>


<wsdl:output
message="tns:HelloWorldSoapOut"
/>


</wsdl:operation>


</wsdl:portType>


<wsdl:binding
name="ServiceSoap"
type="tns:ServiceSoap">


<soap:binding
transport="http://schemas.xmlsoap.org/soap/http"
/>


<wsdl:operation
name="CreateSharePointSubWeb">


<soap:operation
soapAction="http://tempuri.org/CreateSharePointSubWeb"
style="document"
/>


<wsdl:input>


<soap:body
use="literal"
/>


</wsdl:input>


<wsdl:output>


<soap:body
use="literal"
/>


</wsdl:output>


</wsdl:operation>


<wsdl:operation
name="HelloWorld">


<soap:operation
soapAction="http://tempuri.org/HelloWorld"
style="document"
/>


<wsdl:input>


<soap:body
use="literal"
/>


</wsdl:input>


<wsdl:output>


<soap:body
use="literal"
/>


</wsdl:output>


</wsdl:operation>


</wsdl:binding>


<wsdl:binding
name="ServiceSoap12"
type="tns:ServiceSoap">


<soap12:binding
transport="http://schemas.xmlsoap.org/soap/http"
/>


<wsdl:operation
name="CreateSharePointSubWeb">


<soap12:operation
soapAction="http://tempuri.org/CreateSharePointSubWeb"
style="document"
/>


<wsdl:input>


<soap12:body
use="literal"
/>


</wsdl:input>


<wsdl:output>


<soap12:body
use="literal"
/>


</wsdl:output>


</wsdl:operation>


<wsdl:operation
name="HelloWorld">


<soap12:operation
soapAction="http://tempuri.org/HelloWorld"
style="document"
/>


<wsdl:input>


<soap12:body
use="literal"
/>


</wsdl:input>


<wsdl:output>


<soap12:body
use="literal"
/>


</wsdl:output>


</wsdl:operation>


</wsdl:binding>


<wsdl:service
name="Service">


<wsdl:port
name="ServiceSoap"
binding="tns:ServiceSoap">


<soap:address
location=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %>
/>


</wsdl:port>


<wsdl:port
name="ServiceSoap12"
binding="tns:ServiceSoap12">


<soap12:address
location=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %>
/>


</wsdl:port>


</wsdl:service>

</wsdl:definitions>


 

When accessed at http://intranet/_vti_bin/WebsExtrawsdl.aspx or http://intranet/_vti_bin/WebsExtra.asmx?wsdl the following was the result:


 

<wsdl:definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:tns="http://tempuri.org/"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
targetNamespace="http://tempuri.org/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">

    <wsdl:types>

        <s:schema
elementFormDefault="qualified"
targetNamespace="http://tempuri.org/">

            <s:element
name="CreateSharePointSubWeb">

                <s:complexType>

                    <s:sequence>

                        <s:element
minOccurs="0"
maxOccurs="1"
name="portalUrl"
type="s:string" />

                        <s:element
minOccurs="0"
maxOccurs="1"
name="subsite"
type="s:string" />

                        <s:element
minOccurs="0"
maxOccurs="1"
name="name"
type="s:string" />

                        <s:element
minOccurs="0"
maxOccurs="1"
name="Title"
type="s:string" />

                        <s:element
minOccurs="0"
maxOccurs="1"
name="Description"
type="s:string" />

                        <s:element
minOccurs="1"
maxOccurs="1"
name="templateId"
type="s:int" />

                    </s:sequence>

                </s:complexType>

            </s:element>

            <s:element
name="CreateSharePointSubWebResponse">

                <s:complexType>

                    <s:sequence>

                        <s:element
minOccurs="0"
maxOccurs="1"
name="CreateSharePointSubWebResult"
type="s:string" />

                    </s:sequence>

                </s:complexType>

            </s:element>

            <s:element
name="HelloWorld">

                <s:complexType />

            </s:element>

            <s:element
name="HelloWorldResponse">

                <s:complexType>

                    <s:sequence>

                        <s:element
minOccurs="0"
maxOccurs="1"
name="HelloWorldResult"
type="s:string" />

                    </s:sequence>

                </s:complexType>

            </s:element>

        </s:schema>

    </wsdl:types>

    <wsdl:message
name="CreateSharePointSubWebSoapIn">

        <wsdl:part
name="parameters"
element="tns:CreateSharePointSubWeb" />

    </wsdl:message>

    <wsdl:message
name="CreateSharePointSubWebSoapOut">

        <wsdl:part
name="parameters"
element="tns:CreateSharePointSubWebResponse" />

    </wsdl:message>

    <wsdl:message
name="HelloWorldSoapIn">

        <wsdl:part
name="parameters"
element="tns:HelloWorld" />

    </wsdl:message>

    <wsdl:message
name="HelloWorldSoapOut">

        <wsdl:part
name="parameters"
element="tns:HelloWorldResponse" />

    </wsdl:message>

    <wsdl:portType
name="ServiceSoap">

        <wsdl:operation
name="CreateSharePointSubWeb">

            <wsdl:input
message="tns:CreateSharePointSubWebSoapIn" />

            <wsdl:output
message="tns:CreateSharePointSubWebSoapOut" />

        </wsdl:operation>

        <wsdl:operation
name="HelloWorld">

            <wsdl:input
message="tns:HelloWorldSoapIn" />

            <wsdl:output
message="tns:HelloWorldSoapOut" />

        </wsdl:operation>

    </wsdl:portType>

    <wsdl:binding
name="ServiceSoap"
type="tns:ServiceSoap">

        <soap:binding
transport="http://schemas.xmlsoap.org/soap/http" />

        <wsdl:operation
name="CreateSharePointSubWeb">

            <soap:operation
soapAction="http://tempuri.org/CreateSharePointSubWeb"
style="document" />

            <wsdl:input>

                <soap:body
use="literal" />

            </wsdl:input>

            <wsdl:output>

                <soap:body
use="literal" />

            </wsdl:output>

        </wsdl:operation>

        <wsdl:operation
name="HelloWorld">

            <soap:operation
soapAction="http://tempuri.org/HelloWorld"
style="document" />

            <wsdl:input>

                <soap:body
use="literal" />

            </wsdl:input>

            <wsdl:output>

                <soap:body
use="literal" />

            </wsdl:output>

        </wsdl:operation>

    </wsdl:binding>

    <wsdl:binding
name="ServiceSoap12"
type="tns:ServiceSoap">

        <soap12:binding
transport="http://schemas.xmlsoap.org/soap/http" />

        <wsdl:operation
name="CreateSharePointSubWeb">

            <soap12:operation
soapAction="http://tempuri.org/CreateSharePointSubWeb"
style="document" />

            <wsdl:input>

                <soap12:body
use="literal" />

            </wsdl:input>

            <wsdl:output>

                <soap12:body
use="literal" />

            </wsdl:output>

        </wsdl:operation>

        <wsdl:operation
name="HelloWorld">

            <soap12:operation
soapAction="http://tempuri.org/HelloWorld"
style="document" />

            <wsdl:input>

                <soap12:body
use="literal" />

            </wsdl:input>

            <wsdl:output>

                <soap12:body
use="literal" />

            </wsdl:output>

        </wsdl:operation>

    </wsdl:binding>

    <wsdl:service
name="Service">

        <wsdl:port
name="ServiceSoap"
binding="tns:ServiceSoap">

            <soap:address
location="http://intranet/_vti_bin/WebsExtrawsdl.aspx" />

        </wsdl:port>

        <wsdl:port
name="ServiceSoap12"
binding="tns:ServiceSoap12">

            <soap12:address
location="http://intranet/_vti_bin/WebsExtrawsdl.aspx" />

        </wsdl:port>

    </wsdl:service>

</wsdl:definitions>


 

And the file WebsExtradisco.aspx:

<%@
Page
Language="C#"
Inherits="System.Web.UI.Page"
%>
<%@
Assembly
Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
%>
<%@
Import
Namespace="Microsoft.SharePoint.Utilities"
%>
<%@
Import
Namespace="Microsoft.SharePoint"
%>

<% Response.ContentType = "text/xml"; %>

<discovery
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.xmlsoap.org/disco/">


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request) + "?wsdl"),Response.Output); %>
docref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<soap
address=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %>
xmlns:q1="http://tempuri.org/"
binding="q1:ServiceSoap"
xmlns="http://schemas.xmlsoap.org/disco/soap/"
/>


<soap
address=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %>
xmlns:q2="http://tempuri.org/"
binding="q2:ServiceSoap12"
xmlns="http://schemas.xmlsoap.org/disco/soap/"
/>

</discovery>


 

And when I browsed the pages http://intranet/_vti_bin/WebsExtradisco.aspx or http://intranet/_vti_bin/WebsExtra.disco was:


 

<discovery
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.xmlsoap.org/disco/">

    <contractRef
ref="http://intranet/_vti_bin/WebsExtradisco.aspx?wsdl"
docref="http://intranet/_vti_bin/WebsExtradisco.aspx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <soap
address="http://intranet/_vti_bin/WebsExtradisco.aspx"
xmlns:q1="http://tempuri.org/"
binding="q1:ServiceSoap"
xmlns="http://schemas.xmlsoap.org/disco/soap/" />

    <soap
address="http://intranet/_vti_bin/WebsExtradisco.aspx"
xmlns:q2="http://tempuri.org/"
binding="q2:ServiceSoap12"
xmlns="http://schemas.xmlsoap.org/disco/soap/" />

</discovery>


 

Of course, to make the address translation from http://intranet/_vti_bin/WebsExtra.disco actually point to the correct file http://intranet/_vti_bin/WebsExtradisco.aspx and similary http://intranet/_vti_bin/WebsExtra.asmx?wsdl point to http://intranet/_vti_bin/WebsExtrawsdl.aspx the correct setting have to be added to the file spdisco.aspx. This is the version I had (Notice the two last tags before the if-statement):


 

<%@
Page
Language="C#"
Inherits="System.Web.UI.Page"
%>
<%@
Assembly
Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
%>
<%@
Import
Namespace="Microsoft.SharePoint.Utilities"
%>
<%@
Import
Namespace="Microsoft.SharePoint"
%>

<%@
Import
Namespace="Microsoft.SharePoint.WebControls"
%>

<%@
Import
Namespace="Microsoft.SharePoint.Administration"
%>

<% SPSite spServer = SPControl.GetContextSite(Context); SPWeb spWeb = SPControl.GetContextWeb(Context); %>

<% Response.ContentType = "text/xml"; %>

<discovery
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.xmlsoap.org/disco/">


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/alerts.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/alerts.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/alerts.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/Authentication.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/Authentication.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/Authentication.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/copy.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/copy.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/copy.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/dspsts.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/dspsts.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/dspsts.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/dws.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/dws.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/dws.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/forms.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/forms.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/forms.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/imaging.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/imaging.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/imaging.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/lists.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/lists.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/lists.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/meetings.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/meetings.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/meetings.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/People.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/People.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/People.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/permissions.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/permissions.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/permissions.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/SharepointEmailWS.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/SharepointEmailWS.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/SharepointEmailWS.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/SiteData.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/SiteData.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/SiteData.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/sites.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/sites.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/sites.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/spsearch.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/spsearch.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/spsearch.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/UserGroup.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/UserGroup.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/UserGroup.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/versions.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/versions.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/versions.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/views.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/views.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/views.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/WebPartPages.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/WebPartPages.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/WebPartPages.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/webs.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/webs.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_bin/webs.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>


<contractRef
ref=<% SPEncode.WriteHtmlEncodeWithQuote(Response, spWeb.Url + "/_vti_bin/WebsExtra.asmx?wsdl", '"'); %>
docRef=<% SPEncode.WriteHtmlEncodeWithQuote(Response, spWeb.Url + "/_vti_bin/WebsExtra.asmx", '"');%>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<soap
address=<% SPEncode.WriteHtmlEncodeWithQuote(Response, spWeb.Url + "/_vti_bin/WebsExtra.asmx", '"'); %>
xmlns:q1="http://schemas.microsoft.com/sharepoint/soap/directory/"
binding="q1:ServiceSoap"
xmlns="http://schemas.xmlsoap.org/disco/soap/"
/>

<%

    if (spServer.WebApplication is SPAdministrationWebApplication)

    {

%>


<contractRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_adm/Admin.asmx?wsdl"),Response.Output); %>
docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_adm/Admin.asmx"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/scl/"
/>


<discoveryRef
ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(spWeb.Url + "/_vti_adm/Admin.asmx?disco"),Response.Output); %>
xmlns="http://schemas.xmlsoap.org/disco/"
/>

<% } %>

</discovery>


 

And when browsing to http://intranet/_vti_bin/spdisco.aspx I got the following result:

<discovery
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.xmlsoap.org/disco/">

    <contractRef
ref="http://intranet/_vti_bin/WebsExtradisco.aspx?wsdl"
docref="http://intranet/_vti_bin/WebsExtradisco.aspx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <soap
address="http://intranet/_vti_bin/WebsExtradisco.aspx"
xmlns:q1="http://tempuri.org/"
binding="q1:ServiceSoap"
xmlns="http://schemas.xmlsoap.org/disco/soap/" />

    <soap
address="http://intranet/_vti_bin/WebsExtradisco.aspx"
xmlns:q2="http://tempuri.org/"
binding="q2:ServiceSoap12"
xmlns="http://schemas.xmlsoap.org/disco/soap/" />

</discovery>

<discovery
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://schemas.xmlsoap.org/disco/">

    <contractRef
ref="http://intranet/_vti_bin/alerts.asmx?wsdl"
docRef="http://intranet/_vti_bin/alerts.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/alerts.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/Authentication.asmx?wsdl"
docRef="http://intranet/_vti_bin/Authentication.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/Authentication.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/copy.asmx?wsdl"
docRef="http://intranet/_vti_bin/copy.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/copy.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/dspsts.asmx?wsdl"
docRef="http://intranet/_vti_bin/dspsts.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/dspsts.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/dws.asmx?wsdl"
docRef="http://intranet/_vti_bin/dws.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/dws.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/forms.asmx?wsdl"
docRef="http://intranet/_vti_bin/forms.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/forms.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/imaging.asmx?wsdl"
docRef="http://intranet/_vti_bin/imaging.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/imaging.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/lists.asmx?wsdl"
docRef="http://intranet/_vti_bin/lists.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/lists.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/meetings.asmx?wsdl"
docRef="http://intranet/_vti_bin/meetings.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/meetings.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/People.asmx?wsdl"
docRef="http://intranet/_vti_bin/People.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/People.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/permissions.asmx?wsdl"
docRef="http://intranet/_vti_bin/permissions.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/permissions.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/SharepointEmailWS.asmx?wsdl"
docRef="http://intranet/_vti_bin/SharepointEmailWS.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/SharepointEmailWS.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/SiteData.asmx?wsdl"
docRef="http://intranet/_vti_bin/SiteData.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/SiteData.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/sites.asmx?wsdl"
docRef="http://intranet/_vti_bin/sites.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/sites.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/spsearch.asmx?wsdl"
docRef="http://intranet/_vti_bin/spsearch.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/spsearch.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/UserGroup.asmx?wsdl"
docRef="http://intranet/_vti_bin/UserGroup.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/UserGroup.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/versions.asmx?wsdl"
docRef="http://intranet/_vti_bin/versions.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/versions.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/views.asmx?wsdl"
docRef="http://intranet/_vti_bin/views.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/views.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/WebPartPages.asmx?wsdl"
docRef="http://intranet/_vti_bin/WebPartPages.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/WebPartPages.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/webs.asmx?wsdl"
docRef="http://intranet/_vti_bin/webs.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <discoveryRef
ref="http://intranet/_vti_bin/webs.asmx?disco"
xmlns="http://schemas.xmlsoap.org/disco/" />

    <contractRef
ref="http://intranet/_vti_bin/WebsExtra.asmx?wsdl"
docRef="http://intranet/_vti_bin/WebsExtra.asmx"
xmlns="http://schemas.xmlsoap.org/disco/scl/" />

    <soap
address="http://intranet/_vti_bin/WebsExtra.asmx"
xmlns:q1="http://schemas.microsoft.com/sharepoint/soap/directory/"
binding="q1:ServiceSoap"
xmlns="http://schemas.xmlsoap.org/disco/soap/" />

</discovery>


 

So, with these example files, and these tips, I hope I can ease your troubles in making you own custom sharepoint web service.

Gustaf Westerlund
CRM and SharePoint Consultant

Humandata AB
www.humandata.se