May 30, 2008

Implementing Remotesoft .NET Protector using MSBuild

Filed under: General — Tags: — Darrin Maidlow @ 4:02 pm

RADE has grown significantly in size and complexity over the past four years.  What started off as a relatively simple classic ASP application has grown to 8+ .NET assemblies, with numerous 3rd party DLL references.  Current R&D is going to further increase the size of the build. In addition to that, we’ve developed several vertical products on top of RADE which need to be updated as new revisions of the base framework are completed.

It’s come to the point where I need to get to a one step build.  The first move in here was to implement in my build process as protecting the assemblies ended up being one of the bigger pains in the butt when building.

So to kick things off, we need to work with MSBuild a little bit.  Originally, I tried using the <exec> call from MSBuild.  This didn’t give me the flexibility to loop through the files being generated.  So I started writing a custom build task.  Please read this on building custom tasks if you are new to this.

In summary, I defined a number of get/set methods for the globals I wanted the build engine to set, and in the Execute function I set it up to loop through the passed .DLL files, and execute Protector on each.  After the dlls were processed, they were moved out of the protected folder and the protected folder was removed.  If you are having problems getting your task running, check out this article on .

RADE uses a Visual Studio 2008 to deploy all of the files on build, so to implement the new task we need to do some editing in the project.  Open the project either with a text editor, or in Visual Studio by a right click on the project and choosing "Open Project File".  This part is quite simple.  First ensure that the assembly generated by building your task is in the same folder as the web deployment .wdproj file.  Next we need to add a line near the top of the web deployment project:

   1: <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
   2:     <UsingTask TaskName="Landor.Deploy.BuildTasks.RemoteSoftProtector" AssemblyFile="Landor.Deploy.Buildtasks.dll"/>
   3:     <PropertyGroup>...

Here we point the UsingTask call to both the namespace and class of our protector code, as well assembly.  One more change to make.  Scroll to the end of the project and you should see a number of empty <Target> tags.  We need to add some code to the Name="Afterbuild" tag.

   1: <Target Name="AfterBuild">
   2:         <ItemGroup>
   3:             <DLLFiles Include="$(MSBuildProjectDirectory)\Release\Bin\*.dll"/>
   4:         </ItemGroup>
   5:         <RemoteSoftProtector
   6:             Files="@(DLLFiles)"
   7:             ProtectorEXEPath="C:\Program Files (x86)\Remotesoft\Protector\bin\protector.exe"
   8:             ProtectorParams="-neutral -string -cctor -clrversion v2.0.50727"
   9:             BinFolder="$(MSBuildProjectDirectory)\Release\Bin\"
  10:             Exclusions="AjaxControlToolkit.dll;Microsoft.Xml.Schema.Linq.dll;ZedGraph.Web.dll"
  11:         />
  12: </Target>

 

Two things occur here.  First, in the <Itemgroup> tag we are initializing a variable called DLLFiles, and it’s getting all the .DLL files in the project’s Release\Bin build folder.   Note that this process creates a semi-colon delimited list of full paths and files.

The next thing that occurs is actually calling the build task using the <RemoteSoftProtector> tag.  The tag name should/must match the name of the build tasks’ class.  Within this tag, we are setting all of the defined public properties, using the same name as those defined with our build task class.

This concludes a day of fun learning how MSBuild and custom tasks work.  Hopefully it helps you out a bit.  Bugs or comments, let me know.

Technorati Tags: ,,

May 25, 2008

.NET Code Protection – Remotesoft Protector to the Rescue!

Filed under: Development — Tags: — Darrin Maidlow @ 1:53 am

When .NET based assemblies go out the door, it’s incredibly simple for others to get access to your code.   Download and take a look at what some of your assemblies have to say.  The code visible is likely not going to be anywhere near as elegant as the original.  The comments will be gone.  The gist of what you are doing will be there.  If you would prefer that your work be a little tougher to get at, read on.

Obfuscation was one of my first answers to this problem.  An obfuscator ships with Visual Studio Pro, free and there are many available on the market.  Obfuscation just didn’t do it for me.  I once helped a customer troubleshoot problems with one of their software solutions from an unnamed vendor using Reflector and walking through the obfuscated code.  This was really a painful experience, it does make it harder to figure out what is going on – but a friend of mine suggested a product that takes code protection one step further.

Hello .  This product is pretty cool.  If you purchase the protector product you will receive three components.  Salamander .NET Decompiler, .NET Obfuscator, and .NET Protector.  Initially I was processing my assemblies with both the obfuscator and the protector.   Now a days, I pretty much only run my assemblies through the protector.

Once you’ve processed an assembly with the protector and you open it up in reflector things are going to look a little different.  Here is a little before and after action for you:

Code as disassembled by Reflector 

Now lets take a look at the same code, but after being protected:

image

That’s it.  Protector has made all your code go bye bye =)  What’s happened here?  As I understand it, Protector compiles all your managed .NET code into native code.  So, yes, is it possible to disassemble native binaries.  The difference here is the height of the bar – with plain .NET assemblies even my grand mother could get my code.  Reverse engineering a native assembly is a different story.  If someone with the skill to do that wants your code – well you must be writing some damn fine code.  It would probably be easier for that kind of person to write it from scratch =)

I’ve been working on increasing my score lately.  One of my biggies is the one step build for RADE.  That sentence really doesn’t do the task justice.  The first step I’m tackling in the one step build is automating the process of protecting my .NET assemblies.  I could not find any resources on doing with with MSBuild.  Once I get it working, I’ll post some code.

All that said, I highly recommend you check out Protector if code protection is your thing.  The price is a little bit steep at 1899$ for 1-5 developers – but how much money have you invested in that one little DLL or EXE file?

May 24, 2008

Visual Studio 2008 Product Comparison

Filed under: General — Tags: — Darrin Maidlow @ 11:51 pm

Came across a really of the various Visual Studio 2008 releases today.  Now if only I could come up with the ten grand Microsoft wants for a Team Suite license =[

Technorati Tags:

May 21, 2008

Dynamic Authoring in Mapguide Enterprise – Before the Viewer has Loaded

Filed under: Mapguide — Tags: , — Darrin Maidlow @ 11:38 pm

As a follow up to my previous post on , I wanted to share some examples of using these new objects, so I created this example of doing some dynamic authoring before the map viewer has loaded.  Currently, all of my work is done using Mapguide Enterprise 2009, however this code should work using Mapguide Open 2.0 as well.  I believe I had this code working using Mapguide 2008 initially, and then migrated it over to the 2009 platform.  Feel free to give that shot if you are still using Mapguide 2008.  Some changes would be needed in the .Net objects project – to remove 2009 specific XSD files and possibly reference some of the older XSDs for things like layers.

One of my first tasks with Mapguide Enterprise was to build a map on the fly.  There were examples for adding layers to the map programmatically after the viewer had loaded, but this didn’t match the desired flow of my application.  Also, having come from years of Mapguide 6.5 and lower -  I was trying to avoid as much client side automation as possible.  The new was one of the things that excited me the most about Mapguide Enterprise / Mapguide Open Source

I’ve pasted the full, commented code here, as well as attached a solution containing the two projects needed.  Note these projects are Visual Studio 2008 projects.  Please feel free to use this code for learning purposes.  If it helps you out, link back to this article =)

If you choose instead to create a new project, be sure to add a project or DLL reference to the OSGeo.Mapguide.Objects.dll file we created previously .  The project I’ve included here contains both the sample Web Application project, as well as a copy of the .NET objects for Mapguide project.  It should be almost ready for the running.  Also ensure there is a reference to the Microsoft.XML.Schema.Linq namespace added to the project.  Also copy the required .DLL files from the Autodesk provided .NET viewer\bin folder to the web projects bin folder.  I did not include these here as I was not sure of the legality of doing so.

First load up the into your Mapguide repository.  For the purposes of this example, open the Map definition in Mapguide Studio (or equivalent) and remove all the layers and layer groups from the map.  If you forget to do this you will get a Duplicate Object error when you load up the map.  Once that is done, you are ready to try out the code.

You should be able top unzip the zip and open the MGEDynamicAuthoringSample.sln file with Visual Studio.  Pretty much the only thing you may need to change are the constants to match your machine specific items:

 

   1: 'set some contants up - webconfig.ini, map viewer url, Map, Layout and Layer locatinos
   2: Private Const gszWebConfig As String = "C:\inetpub\MapGuideEnterprise2009\WebServerExtensions\www\webconfig.ini"
   3: Private Const gszViewerUrl As String = "http://localhost/mapguide2009/mapviewerdwf"
   4: Private Const gszMapResID As String = "Library://Samples/Sheboygan/Maps/Sheboygan.MapDefinition"
   5: Private Const gszLayerFolderResID As String = "Library://Samples/Sheboygan/Layers.Folder"
   6: Private Const gszLayoutResID As String = "Library://Samples/Sheboygan/Layouts/SheboyganAsp.WebLayout"
   7: 'lets run with Administrator for noew to eliminate permission problems.  Update your password as needed, or specify your
   8: 'own credentials if you are comfortable with setting up repository permissions.
   9: Private Const gszMGUser As String = "Administrator"
  10: Private Const gszMGPass As String = "admin"


Once you’ve changed all the needed settings, you should be good to go.  Run the code in Visual Studio and you should now see a map similar to the default Sheboygan map.  The key difference is that all of the layers will exist within the layer group we created programmatically.

Commented Codebehind:

   1: Imports OSGeo.MapGuide
   2: 'we still need to import and use system.xml, but don't really need to use many parts of the XML
   3: 'functionality =)
   4: Imports System.Xml
   5: Partial Public Class _Default
   6:     '' 'So for this example, we're going to use the sheboygan sample package available from 
   7:     '' ' http://mapguide.osgeo.org/download/releases/2.0.x-samples.  Be sure to grab both the 
   8:     '' ' dotnetviewstample.zip as well as the sheboygan.mgp.  Import the package using
   9:     '' ' the mapguide site administrator.  I've tried to use the default resource paths
  10:     '' ' but some tweaking of map will be needed
  11:     '' ' 
  12:     '' ' Open the map definition in Studio, and remove all the layers from Samples/Sheboygan/Maps/Sheboygan.MapDefinition 
  13:     '' ' we're going to add them dynamically    
  14:  
  15:     Inherits System.Web.UI.Page
  16:  
  17:     'set some contants up - webconfig.ini, map viewer url, Map, Layout and Layer locatinos
  18:     Private Const gszWebConfig As String = "C:\inetpub\MapGuideEnterprise2009\WebServerExtensions\www\webconfig.ini"
  19:     Private Const gszViewerUrl As String = "http://localhost/mapguide2009/mapviewerdwf"
  20:     Private Const gszMapResID As String = "Library://Samples/Sheboygan/Maps/Sheboygan.MapDefinition"
  21:     Private Const gszLayerFolderResID As String = "Library://Samples/Sheboygan/Layers.Folder"
  22:     Private Const gszLayoutResID As String = "Library://Samples/Sheboygan/Layouts/SheboyganAsp.WebLayout"
  23:     Private Const gszMGUser As String = "Administrator"
  24:     Private Const gszMGPass As String = "admin"
  25:  
  26:     ''' <summary>
  27:     ''' Page_Load does everything =)  Be sure to copy the dll files from your mapviewernet\bin folder to this projects bin folder.
  28:     ''' I did not include them as that might have violated some autodesk license.    
  29:     ''' </summary>
  30:     ''' <param name="sender"></param>
  31:     ''' <param name="e"></param>
  32:     ''' <remarks></remarks>
  33:     Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
  34:  
  35:         'setup our siteconnection
  36:         Dim siteConnection As New MgSiteConnection
  37:         siteConnection = CreateMGSession(gszMGUser, gszMGPass, gszWebConfig)
  38:         Dim szSessionId As String = siteConnection.GetSite.CreateSession()
  39:  
  40:         'create our connection to the resource service
  41:         Dim resSvc As MgResourceService
  42:         resSvc = siteConnection.CreateService(MgServiceType.ResourceService)
  43:  
  44:         'get or set the resourceid of the map to load - in this case i'm just going to hard code it
  45:         Dim mapResourceID As New MgResourceIdentifier(gszMapResID)
  46:  
  47:         'load the existing map from the libary into an XML document
  48:         Dim mapXML As XmlDocument = GetResourceXML(resSvc, mapResourceID)
  49:  
  50:         'so at this point we have an XML document.  Let's try using our new OSGeo.Mapguide.Object classes
  51:         Dim newMapDefinition As New OSGeo.MapGuide.Objects.MapDefinition
  52:  
  53:         'to load the xml - call the shared/static method of MapDefinition.Parse on the OuterXML of the XMLDocument
  54:         newMapDefinition = OSGeo.MapGuide.Objects.MapDefinition.Parse(mapXML.OuterXml)
  55:  
  56:         'at this point we have a loaded .NET Object containing the map.  Try using Intellisense to see the different methods
  57:         'Lets add a layer group now
  58:  
  59:         'add any needed layer groups to the map.  In this example we're only going to add one.
  60:         Dim szLayerGroupName As String = "NewLayerGroup"
  61:         Dim szLayerGroupAlias As String = "My Layer Group Alias"
  62:         newMapDefinition = AddMapLayerGroup(newMapDefinition, szLayerGroupName, szLayerGroupAlias)
  63:  
  64:         'get or set the resourceid of the folder that contains our layers to add
  65:         Dim layerPath As New MgResourceIdentifier(gszLayerFolderResID)
  66:  
  67:         'so we now have a layer group in our map.  Lets add the layers
  68:         AddFolderOfLayers(resSvc, newMapDefinition, layerPath, szLayerGroupName)
  69:  
  70:         'at this point our map definition is loaded.  Lets convert it back to XML and save it to the session
  71:         Dim newMapDoc As New XmlDocument
  72:         newMapDoc.PreserveWhitespace = True
  73:  
  74:         Dim szXML As String = newMapDefinition.Untyped.ToString
  75:         newMapDoc.LoadXml(szXML)
  76:  
  77:         'create our 'new' resourceID in the session repository
  78:         Dim newMapResourceID As New MgResourceIdentifier("Session:" & szSessionId & "//" & mapResourceID.GetName() & "." & MgResourceType.MapDefinition)
  79:  
  80:         'save the newly created map in the session repository
  81:         CreateSessionResource(resSvc, newMapDoc, szSessionId, newMapResourceID.GetName, MgResourceType.MapDefinition)
  82:  
  83:         'now lets deal with the layout - we need to load up the library layout, update the map definition, and save it to the repository
  84:         Dim layoutResourceID As New MgResourceIdentifier(gszLayoutResID)
  85:         Dim newLayoutResourceID As New MgResourceIdentifier("Session:" & szSessionId & "//" & layoutResourceID.GetName() & "." & MgResourceType.WebLayout)
  86:  
  87:         'same process here, load the XML, parse the XML into the new .net Object
  88:         Dim layoutXML As XmlDocument = GetResourceXML(resSvc, layoutResourceID)
  89:  
  90:         Dim newLayout As New OSGeo.MapGuide.Objects.WebLayout
  91:         newLayout = OSGeo.MapGuide.Objects.WebLayout.Parse(layoutXML.OuterXml)
  92:  
  93:         'update the map resource ID in the layout to point to our newly created map in the session
  94:         newLayout.Map.ResourceId = newMapResourceID.ToString
  95:  
  96:         'finally go through the motinos to save the layout to the session
  97:         Dim newLayoutXML As New XmlDocument
  98:         newLayoutXML.PreserveWhitespace = True
  99:         newLayoutXML.LoadXml(newLayout.Untyped.ToString())
 100:  
 101:         CreateSessionResource(resSvc, newLayoutXML, szSessionId, newLayoutResourceID.GetName, MgResourceType.WebLayout)
 102:  
 103:         'our last step is to update the src attribute of the iframe with the information needed to load the newly created layout
 104:         SetupIFrame(newLayoutResourceID, szSessionId)
 105:     End Sub
 106:     Private Sub SetupIFrame(ByVal layoutResId As MgResourceIdentifier, ByVal szSessionID As String)
 107:         'build the URL based on our constants, and the layout + session info
 108:         Dim szUrl As String = gszViewerUrl & "?WEBLAYOUT=" & layoutResId.ToString & "&SESSION=" & szSessionID
 109:  
 110:         'update the iframe
 111:         ifrmViewer.Attributes.Add("src", szUrl)
 112:     End Sub
 113:     ''' <summary>
 114:     ''' Adds a layer group to the passed MapDefinition, and returns the updated MapDefinition Object.
 115:     ''' If desired, additional parameters could be added to this to customize the various other properties
 116:     ''' like ShowInLegend etc        
 117:     ''' </summary>
 118:     ''' <param name="mapDef">The loaded MapDefinition obect</param>
 119:     ''' <param name="szLayerGroupName">The Layer Name of the new layer group</param>
 120:     ''' <param name="szLayerGroupAlias">The Layer Alias of the new layer group</param>
 121:     ''' <returns>the passed MapDefinition, with the new layer group added</returns>
 122:     ''' <remarks></remarks>
 123:     Public Shared Function AddMapLayerGroup(ByVal mapDef As OSGeo.MapGuide.Objects.MapDefinition, ByVal szLayerGroupName As String, ByVal szLayerGroupAlias As String) As OSGeo.MapGuide.Objects.MapDefinition
 124:         Dim newLayGRoup As New OSGeo.MapGuide.Objects.MapLayerGroupType
 125:         newLayGRoup.Name = szLayerGroupName
 126:         newLayGRoup.LegendLabel = szLayerGroupAlias
 127:         newLayGRoup.ShowInLegend = "True"
 128:         newLayGRoup.ExpandInLegend = "True"
 129:         newLayGRoup.Visible = "True"
 130:         newLayGRoup.Group = ""
 131:         mapDef.MapLayerGroup.Add(newLayGRoup)
 132:         Return mapDef
 133:     End Function
 134:  
 135:     ''' <summary>
 136:     ''' Adds all layer definitions within the specified repository folder to the specified map
 137:     ''' </summary>
 138:     ''' <param name="resSvc">An open resource service connection</param>
 139:     ''' <param name="newMap">the MapDefinition object to modify</param>
 140:     ''' <param name="resLocation">The folder in the repository to search for layer objects</param>
 141:     ''' <returns>Update MapDefinition object</returns>
 142:     ''' <remarks></remarks>
 143:     Public Shared Function AddFolderOfLayers(ByRef resSvc As MgResourceService, ByVal newMap As OSGeo.MapGuide.Objects.MapDefinition, ByVal resLocation As MgResourceIdentifier, ByVal szLayerGroupName As String) As OSGeo.MapGuide.Objects.MapDefinition
 144:         'get the list of layers in the folder
 145:         Dim resList As OSGeo.MapGuide.Objects.ResourceList
 146:         'use GetAllResources to get a new resList
 147:         resList = GetAllResources(resSvc, resLocation, 1, MgResourceType.LayerDefinition)
 148:  
 149:         'resList items are pretty much a collection of resrouceDocumentLocalTypes so define one of these to use in the for loop
 150:         Dim resItem As OSGeo.MapGuide.Objects.ResourceList.ResourceDocumentLocalType
 151:  
 152:         'define a new layerResID to use in the loop
 153:         Dim layerResID As MgResourceIdentifier
 154:  
 155:         'for each layer - process it and add it to the map
 156:         Dim newLayerType As New OSGeo.MapGuide.Objects.MapLayerType
 157:         For Each resItem In resList.ResourceDocument
 158:             layerResID = New MgResourceIdentifier(resItem.ResourceId)
 159:             'for adding a layer to the map - we must use the MapLayerType object 
 160:             newLayerType = New OSGeo.MapGuide.Objects.MapLayerType
 161:             newLayerType.ResourceId = layerResID.ToString
 162:             'Be sure to specify the name of the 
 163:             newLayerType.Group = szLayerGroupName
 164:             newLayerType.Name = layerResID.GetName
 165:             newLayerType.Selectable = "false"
 166:             newLayerType.ShowInLegend = "true"
 167:             newLayerType.LegendLabel = layerResID.GetName
 168:             newLayerType.ExpandInLegend = "true"
 169:             newLayerType.Visible = "true"
 170:  
 171:             newMap.MapLayer.Insert(0, newLayerType)
 172:         Next
 173:         'return the updated MapDefinition
 174:         Return newMap
 175:     End Function
 176:  
 177:     'Some handy functions
 178:  
 179:     ''' <summary>
 180:     ''' Saves the specified XMLDocument to the session respository
 181:     ''' </summary>
 182:     ''' <param name="resSvc">The open resource service connection</param>
 183:     ''' <param name="xmlDoc">the XMLDocument to save to the session repository</param>
 184:     ''' <param name="szSessionId">String containing the session ID</param>
 185:     ''' <param name="szName">The name of the newly created object in the repository</param>
 186:     ''' <param name="szResourceType">the object type</param>
 187:     ''' <returns></returns>
 188:     ''' <remarks></remarks>
 189:     Public Shared Function CreateSessionResource(ByRef resSvc As MgResourceService, ByVal xmlDoc As XmlDocument, ByVal szSessionId As String, ByVal szName As String, ByVal szResourceType As String) As MgResourceIdentifier
 190:         Dim byteSource As MgByteSource = getByteSource(xmlDoc)
 191:         Dim resID As New MgResourceIdentifier("Session:" & szSessionId & "//" & szName & "." & szResourceType)
 192:         resSvc.SetResource(resID, byteSource.GetReader, Nothing)
 193:         Return resID
 194:     End Function
 195:  
 196:     ''' <summary>
 197:     ''' Returns an MgByteSource for the passed XMLDocument
 198:     ''' </summary>
 199:     ''' <param name="xmlDoc">The XMLDocument to convert</param>
 200:     ''' <returns>MgByteSource of the XMLDocumnet</returns>
 201:     ''' <remarks></remarks>
 202:     Public Shared Function getByteSource(ByVal xmlDoc As XmlDocument) As MgByteSource
 203:         Dim xmlStream As New IO.MemoryStream
 204:         xmlDoc.Save(xmlStream)
 205:         Dim arrBytes() As Byte = xmlStream.ToArray()
 206:         Dim Enc As Encoding = Encoding.UTF8
 207:         Dim docString As String = New String(Enc.GetChars(arrBytes))
 208:         arrBytes = Nothing
 209:  
 210:         Dim arrOutput(docString.Length - 1) As Byte
 211:         Dim nByteCount As Integer = Enc.GetBytes(docString, 0, docString.Length, arrOutput, 0)
 212:  
 213:         Dim byteSource As New MgByteSource(arrOutput, arrOutput.Length)
 214:         byteSource.SetMimeType(MgMimeType.Xml)
 215:  
 216:         Return byteSource
 217:     End Function
 218:  
 219:     ''' <summary>
 220:     ''' returns a loaded XML document of the specified resourceID
 221:     ''' </summary>
 222:     ''' <param name="resSvc">An open connection to the Mapguide Resource Service</param>
 223:     ''' <param name="resId">The MgResourceID of the entity in question</param>
 224:     ''' <returns>A loaded XMLDocument of the specified resource</returns>
 225:     ''' <remarks></remarks>
 226:     Public Shared Function GetResourceXML(ByRef resSvc As MgResourceService, ByVal resId As MgResourceIdentifier) As XmlDocument
 227:         Dim retDoc As New XmlDocument
 228:         retDoc.PreserveWhitespace = True
 229:  
 230:         Dim szXML As String = resSvc.GetResourceContent(resId).ToString()
 231:         Dim nByteCount As Integer
 232:         Dim arrByte(szXML.Length) As Byte
 233:         nByteCount = Encoding.UTF8.GetBytes(szXML, 0, szXML.Length, arrByte, 0)
 234:  
 235:         Dim memStream As New IO.MemoryStream(arrByte)
 236:         retDoc.Load(memStream)
 237:         Return retDoc
 238:     End Function
 239:  
 240:  
 241:     ''' <summary>
 242:     ''' Create a new mapguide session and return the resulting session ID
 243:     ''' </summary>
 244:     ''' <param name="szMGUser">Valid mapguide user name</param>
 245:     ''' <param name="szMGPass">Valid password for the specified mapguide user name</param>
 246:     ''' <param name="szMGWebTier">The path to the Mapguide web tier (valid on the mapguide web server</param>
 247:     ''' <returns>MgSiteConnection - the newly created mapguide site connection</returns>
 248:     ''' <remarks></remarks>
 249:     Public Shared Function CreateMGSession(ByVal szMGUser As String, ByVal szMGPass As String, ByVal szMGWebTier As String) As MgSiteConnection
 250:         MapGuideApi.MgInitializeWebTier(szMGWebTier)
 251:         Dim siteConnection As New MgSiteConnection()
 252:         siteConnection.Open(New MgUserInformation(szMGUser, szMGPass))
 253:         Return siteConnection
 254:     End Function
 255:  
 256:     ''' <summary>
 257:     ''' Converts MgReader to XMLDocument
 258:     ''' </summary>
 259:     ''' <param name="byteReader">Loaded MgByteReader</param>
 260:     ''' <returns>Loaded XMLDocument</returns>
 261:     ''' <remarks></remarks>
 262:     Public Shared Function ConvertMgReaderXml(ByVal byteReader As MgByteReader) As XmlDocument
 263:         Dim szXML As String
 264:         Try
 265:             szXML = byteReader.ToString
 266:         Catch ex As Exception
 267:             Throw ex
 268:         End Try
 269:         Dim nByteCount As Integer
 270:         Dim arrByte(szXML.Length) As Byte
 271:         Try
 272:             nByteCount = Encoding.UTF8.GetBytes(szXML, 0, szXML.Length, arrByte, 0)
 273:         Catch ex As Exception
 274:             Throw ex
 275:         End Try
 276:  
 277:         Dim memStream As New IO.MemoryStream(arrByte)
 278:         Dim retDoc As New XmlDocument
 279:         retDoc.PreserveWhitespace = True
 280:         Try
 281:             retDoc.Load(memStream)
 282:         Catch ex As Exception
 283:             memStream.Close()
 284:             memStream.Dispose()
 285:             Throw ex
 286:         End Try
 287:         memStream.Close()
 288:         memStream.Dispose()
 289:         Return retDoc
 290:     End Function
 291:  
 292:     ''' <summary>
 293:     ''' Gets a ResourceList for each item in the specified repository folder.
 294:     ''' </summary>
 295:     ''' <param name="resSvc">The opened resource server connection</param>
 296:     ''' <param name="resId">The MgResourceId of the folder we want to enumerate</param>
 297:     ''' <param name="nDepth">The folder depth to enumerate.  From the Mapguide Web API Reference:
 298:     ''' (int) Recursion depth, relative to the specified resource.
 299:     '''* If the resource is a document, depth must be set to 0.
 300:     '''* If the resource is a folder:
 301:     '''      o If the depth is equal to 0, only information about the specified folder is returned.
 302:     '''      o If the depth is greater than 0, information about the folder and its descendants up to the specified depth are returned.
 303:     '''* If the depth is -1, information about the folder and all its descendants is returned.
 304:     ''' </param>
 305:     ''' <param name="szType">Again from the Mapguide Web API Reference:
 306:     '''     (String/string) Type of the resource to be enumerated. (Case sensitive.) See MgResourceType  for valid types. If the type is a folder, 
 307:     '''     you must include the trailing slash.
 308:     '''     Or, this can be set to null, in which case information about all resource types is returned
 309:     ''' </param>
 310:     ''' <returns>OSGeo.Mapguide.Objects.ResourceList of resource entities</returns>
 311:     ''' <remarks></remarks>
 312:     Public Shared Function GetAllResources(ByRef resSvc As MgResourceService, ByVal resId As MgResourceIdentifier, ByVal nDepth As Integer, ByVal szType As String) As OSGeo.MapGuide.Objects.ResourceList
 313:         Dim retDoc As XmlDocument
 314:         retDoc = ConvertMgReaderXml(resSvc.EnumerateResources(resId, nDepth, szType))
 315:  
 316:         Dim resourceList As New OSGeo.MapGuide.Objects.ResourceList
 317:         resourceList = OSGeo.MapGuide.Objects.ResourceList.Parse(retDoc.OuterXml)
 318:         Return resourceList
 319:     End Function
 320: End Class

If you have any questions or comments – feel free to let me know.

May 16, 2008

Creating .NET objects for Mapguide XML schema Definitions (XSD) using LINQ

Filed under: Mapguide — Tags: , — Darrin Maidlow @ 4:18 am

With the release of Mapguide 2007 I was really excited about the new API and the significant power that it would give us developer types.  Though I now appreciate the work involved with creating a full object API for all the supported development platforms, I was a little sad to see that only a handful of objects had been created for working with the various entity types available in Mapguide.

One of my first tasks with Mapguide was to add some layers to the map, before the viewer had loaded up, save that map in the session repository and then build a layout for the map – again in the session repository.  I tried various ways of working with the XML provided by the resource  service, and read through many of the e-mails on the topic from the Osgeo list, but I just couldn’t get it to work.  So I set out on my mission to get an object based method of getting things done.  First, as per the developer documentation I tried out xsd.exe.  XSD allowed me to get a class skeleton created, but there was a lot of work needed to a rounded API running.   I had been recently investigating the new functionality Microsoft was exposing with LINQ – and I came across LINQ to XSD.  For more information on Linq to XSD the is a good place to start. 

So download and install it.  You will need to have installed first.  In addition to that I’m using Visual Studio 2008 Pro.  I have tried these steps with both Mapguide Enterprise 2008 and  2009.  I have to assume the resulting code will work with Mapguide Open Source as well.

Setting up your project

At this point you should be able to fire up Visual Studio and create a new project.  If you are a VB developer, I’m afraid you’re going to have to dive into a bit of C#, as its the only support language right now.  It’s not that bad.

The new project window should look a little like this:

image

Choose LINQ to XSD Library and give the project a name.  Once the new project is loaded, create a new folder called XSD.  Browse to your Mapguide 2009/OS 2.0 server Schema folder (on a default installation its c:\Program Files\Autodesk\MapGuideEnterprise2009\Server\Schema).  Take a these files and drag them into the new XSD folder.

Back in Visual Studio, select all of the XSD files.  Look in the property window and change the "Build Action" to be "LinqToXsdSchema".

Building the project

At this point, right click on the project in Visual Studio, and choose build.  You should have 173 errors.  This is caused by having multiple, similar versions of some of the XSDs.  Let’s exclude the following files:

  • FdoProviderCapabilities-1.0.0.xsd
  • LayerDefinition-1.0.0.xsd
  • LayerDefinition-1.1.0.xsd
  • LoadProcedure-1.0.0.xsd
  • SymbolDefinition-1.0.0.xsd

Rebuild the project and we should be down to five errors.  I have found that by excluding the following files I was able to get the code to generate:

  • ApplicationDefinition-1.0.0.xsd
  • ApplicationDefinitionInfo-1.0.0.xsd
  • SymbolLibrary-1.0.0.xsd

 

For my purposes, I was not too concerned about being able to work with these entity types programmatically (at least at this time).  If anyone is able to work around and would like to share it, please let me know.

Build the project again, and we will see three new errors.  This time we will need to dig into the generated code to correct them.  For each of the three errors we will want to change the public string name.  For example:

   1: public string DataType {

 

We would change to:

   1: public string Datatype {

 

Change the case of the property name in each of the three errors and we are just about there.  At this point you should have around 42904 lines of code in this file.  Good thing I didn’t try to do this manually.   Take a copy of all these wonderful lines o’ code, and we’re going to paste them into a new class. For the sake of this example, I renamed class1.cs to be OSGeo.Mapguide.Objects.cs and pasted the code in there.  I also wrapped all the code in a namespace to match the file name.

If you need to find the generated code, take a look in the obj folder within your project.  You should find a file named LinqToXsdSource.cs in the appropriate build folder.

Next we need to prevent the XSDs from rebuilding each time.  Select all of your XSD files again, and change the "Build Action" to "None".    Building the project at this point will generate fifty some odd errors.  We can eliminate these by doing a search and replace on global:: and replacing it with an empty string.  Build the project again, and you will find one error.  Simply remove the "global." from the line in question and the project will now build.

At this point, you should have a nice .dll file which you can reference from your projects and access many of the Mapguide entity types using objects.  Be sure to grab a copy of Microsoft.XmlSchema.Linq.dll and distribute that with your new DLL.

For your convenience, I’ve posted the Project, source and binaries

This project maybe have some issues.  I have worked with LayerDefinitions, MapDefinitions, and WebLayouts – but not tested many of the classes here yet.  If you have any feedback or enhancements you want to share – please let me know!

In the next week I will post some examples on how to build maps and layouts – before the viewer has loaded.

May 8, 2008

Cannot Select Entities in Mapguide Enterprise 2009 Viewer

Filed under: Mapguide — Tags: , — Darrin Maidlow @ 3:27 am

I recently encountered a frustrating problem in my travels.  When the map in my application came up, I could not select any entities.  Both window and single selections just did nothing.  I had recently setup my so I was not sure if I had missed something in the configuration there.  In addition to this, my application is doing some dynamic authoring and adding a bunch of layers to the map before the viewer loads it up.

I saved a copy of the dynamically generated maps and layers to the library repository.  Everything checked out.  The layers were marked as selectable.

Next step was to check out the server logs on my Mapguide server.    We have a nice error occurring a lot: Error: An exception occurred in FDO component. String does not represent a valid filter.

So into the code I go.  I ripped out all my dynamic authoring calls, and the map loads and selects no problem.  As I was starting to suspect, my code was to blame – and there is something specific to my filters on the layers:

   1: newLayer.VectorLayerDefinition.Filter = "BLDG_ID = """ & szBldgID & """ AND BLDG_FLOOR "" IN(0," & nFloorNumber.ToString & ")"""

 

I had assumed that FDO had an IN clause, did a quick check in Mapguide Studio’s condition builder and didn’t see one.  Fair enough, in this case I can get by with two equals conditions in my select as the list of conditions will only ever consist of two values.

   1: newLayer.VectorLayerDefinition.Filter = "BLDG_ID = " & szBldgID & " AND (BLDG_FLOOR = 0 OR BLDG_FLOOR = " & nFloorNumber.ToString & ")"
   2: 'newLayer.VectorLayerDefinition.Filter = "BLDG_ID = " & szBldgID & " AND BLDG_FLOOR  IN(0," & nFloorNumber.ToString & ")"""


Long story short, if you are selecting items in the map viewer that are supposed to be selectable, check your server logs.

Technorati Tags: ,,

May 1, 2008

Manually configure Mapguide 2009 Web Tier on Vista x64 IIS7

Filed under: Uncategorized — Tags: , — Darrin Maidlow @ 3:37 pm

My configuration is this. I have a 32 bit window server running IIS6 and Mapguide server. however for development purposes and local debugging I needed the web tier to be run from my workstation – but working with my network Mapguide enterprise server.

First off, you need to ensure that IIS 7 is installed on the Vista machine, you also need to specify ASP.NET and the ISAPI components be installed. To start off, run the web tier installation and choose a manual installation. I did a custom install, and did not install the php, site admin, or java components. I installed my web tier to C:\inetpub\MapGuideEnterprise2009. It is critical that you do not install it to the default location in program files (x86), or the Vista permissions will break everything. You will know this has occurred because your map will show all question marks in the layer legend, and no map data.  In the server log you will see session timeout errors.  If you do install it to the default location, just move the entire folder structure out of program files (x86).

Finish the installer, this should deploy the needed Mapguide files. I noticed a bit of a delay in the copying new files portion of the installer. Seemed to be hung for several minutes, but it did complete.

Now start up IIS manager (start / run / inetmgr)

This is the ‘hard’ part. It looks nothing like what I was expecting.  IIS Internet Manager has been pretty static for the past, oh ten years…

A default web site should be configured – double click on the computer in the left pane to expand the tree.

 clip_image002

 

First off – we’re going to create a new application pool specifically for Mapguide. This will save us some problems down the road. If you are not familiar with them, application pools are a newer concept introduced in IIS 6. Each application in IIS requires an application pool. Only one version of the .NET framework can be used by a single application pool. We’re going to set this pool to use .NET 2.0 (which also includes 3.0 and 3.5)

 clip_image004

 

The most important reason for a new application pool is to set this pool/worker process to run in 32 bit mode. Select the new application pool and choose Advanced Settings. Change Enable 32-Bit Applications to true.

 clip_image006

 

If you do not set the pool to run as 32 bit, you will get an error message that says:

"The page you are requesting cannot be served because of the Multipurpose Internet Mail Extensions (MIME) map policy that is configured on the Web server. The page you requested has a file name extension that is not recognized, and is not allowed."

A new behavior in IIS7 is a change between virtual directories and applications. In IIS6 and below, we would create a virtual directory, and then add an application to the properties of that virtual directory. With IIS7, we right click and instead of choosing Add Virtual Directory we choose Add Application. So let’s add a new application to the Default Web Site.

For simplicity, I’m re-creating the structure exactly as defined on my Windows 2003 server. Once the application is created, right click on it and choose Add Application. So right click on the default web site and choose add application. Call it Mapguide2009 and choose the newly created mapguide2009 application pool.

Next we need to right click on the Mapguide2009 application and create two new applications. One called mapviewerdwf, and mapviewerajax. Each of these apps must point to the same location – the mapviewernet folder. (by default its location is C:\Program Files (x86)\Autodesk\MapGuideEnterprise2009\WebServerExtensions\www\mapviewernet – but we need to use c:\inetpub\wwwroot\ MapGuideEnterprise2009\WebServerExtensions\www\mapviewernet) The other difference is that for each application, we must set the default document to be dwfviewer.aspx and ajaxviewer.aspx respectively. To set the default document select the new application in the tree and double click Default Document in the central pane. Enter the appropriate default document.

clip_image008

 

Next we need to setup a script mapping for the FCGI. Select the Mapguide2009 application and we are going to create yet another application. Call this application mapagent. This should act like adding an application to an existing folder in IIS6. Next, double click on Handler Mappings. In the top right, select Add Script Map. On the script mapping form, enter MapAgent.fcgi (or *.fcgi) for the request path. Browse to and select the isapi_MapAgent.dll from the www\mapagent folder. Under request restrictions, you can also set the verbs to GET,POST.

clip_image010

 

Click Ok and you will be prompted to enable the ISAPI extension. Click on yes.

At this point – the Mapguide 2009 web tier should be installed and running. Try it out by starting up a browser and hitting http://localhost/mapguide2009/mapagent/index.html. Next you can access maps using http://localhost/mapguide2009/mapviewerdwf or mapviewerajax.

Technorati Tags: ,,

Powered by WordPress

Switch to our mobile site