Tech in the 603, The Granite State Hacker

Getting at Office 365 SharePoint Lists In Windows Phone 7 Silverlight Apps

[Edit 7/8/2013:  I’ve tweaked this to note that this was for WP7.  WP8 has a whole new set of SDKs for SP integration, and it’s a different, and much easier story to use them.]

As promised from my presentation at SharePoint Saturday New Hampshire, I owe a code listing on the meaty part of the chat…  the Office 365 authentication component, especially.  It allows a Windows Phone Silverlight app to access things the lists.asmx service behind the Windows Live ID authentication.  (Frankly, the technique is the same, no matter what kind of client you’re using, but the demo I was doing was using Silverlight 4 for Windows Phone 7.

I also owe slides:
[office src=”https://r.office.microsoft.com/r/rlidPowerPointEmbed?p1=1&p2=1&p3=SD90A564D76FC99F8F!274&p4=&ak=!ABO7SzieOkx6gtY&kip=1″ width=”402″ height=”327″]

Here’s the activity rundown:

1)  The client app (“Windows Phone App”) makes a SAML SOAP request to https://login.microsoftonline.com/extSTS.srf
2)  The SAML response comes back, allowing the app to parse the SAML token.
3)  Make another call, this time to  {your Office365 team site}/_forms/default.aspx?wa=wsignin1, posting the token.
4) The response that comes back need only be checked for errors, the magic is in the cookie container.  It contains an HTTPOnly token (which the development tools do a terribly good job of hiding.)
5)  Assign your cookie container from your previous result to the ListSoapClient that you’re using to make your service calls from.
6)  Profit!

I broke up the “Activation” line on the client side to point out that the calls are Async.

In any case, I have a very rough SPAuthenticationHelper class that I also promised to post.

Here’s an example of how you can use it:

    class SPTasksList

    {
 
        SPAuthenticationHelper _authenticationHelper;
        ListsSoapClient _listsClient;
        bool isBusy = false;

        TaskItem currentUpdate = null;

        string _taskListUri = “http://spsnh.sharepoint.com/TeamSite/Lists/Tasks/AllItems.aspx”;

        public SPTasksList()

        {
            _authenticationHelper = new SPAuthenticationHelper(_taskListUri);
            _listsClient = new ListsSoapClient();
            _listsClient.GetListItemsCompleted += newEventHandler<GetListItemsCompletedEventArgs>(_listsClient_GetTasksListCompleted);
            _listsClient.UpdateListItemsCompleted += newEventHandler<UpdateListItemsCompletedEventArgs>(_listsClient_UpdateListItemsCompleted);
        }
 

        public voidBeginGetTasksList()

        {
            if (!_authenticationHelper.IsAuthenticated)
            {
                _authenticationHelper.OnAuthenticated += newEventHandler<EventArgs>(_authenticationHelper_OnAuthenticated_GetTasks);
                _authenticationHelper.SigninAsync(Configuration.UserName, Configuration.Password);
            }
            else if (!isBusy)
            {
                isBusy = true;
                XElement query = XElement.Parse(“Completed”);
                string ListName = “Tasks”;
                string ViewId = “{f717e507-7c6e-4ece-abf2-8e38e0204e45}”;
                _listsClient.GetListItemsAsync(ListName, ViewId, query, null, null, null, null);
            }
        }

        void_authenticationHelper_OnAuthenticated_UpdateTask(objectsender, EventArgs e)

        {
            _listsClient.CookieContainer = _authenticationHelper.Cookies;
            BeginUpdateTask(currentUpdate);
        }

……
} 

I ported this from a few other examples I found online to Silverlight for Windows Phone.  I apologize,  I haven’t had time to polish it, and I’m having a hard time with the embedded SOAP litteral, but here’s the SPAuthenticationHelper class:





using System;

using System.Net;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
 
namespace SPSNH_SPConnector.Implementation
{
    public class SPAuthenticationHelper
    {
        public CookieContainerCookies { get; set; }
        public boolIsAuthenticated { get; privateset; }
        public event EventHandler<EventArgs> OnAuthenticated;
 
        private bool_isAuthenticationInProgress = false;
 
        const string_authUrl=“https://login.microsoftonline.com/extSTS.srf”;
        const string _login=“/_forms/default.aspx?wa=wsignin1.0”;
       
        //namespaces in the SAML response
        const string _nsS = “http://www.w3.org/2003/05/soap-envelope”;
        const string _nswst = “http://schemas.xmlsoap.org/ws/2005/02/trust”;
        const string _nswsse = “http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd”;
        const string _nswsu = “http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd”;
        const string _nswsa = “http://www.w3.org/2005/08/addressing”;
        const string _nssaml = “urn:oasis:names:tc:SAML:1.0:assertion”;
        const string _nswsp = “http://schemas.xmlsoap.org/ws/2004/09/policy”;
        const string _nspsf = “http://schemas.microsoft.com/Passport/SoapServices/SOAPFault”;
        const string _samlXml =@” http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue http://www.w3.org/2005/08/addressing/anonymous https://login.microsoftonline.com/extSTS.srf {0} {1} {2} http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey http://schemas.xmlsoap.org/ws/2005/02/trust/Issue urn:oasis:names:tc:SAML:1.0:assertion “;

        Uri _uri;  
        HttpWebRequest _getTokenRequest = HttpWebRequest.CreateHttp(_authUrl);
        HttpWebRequest _submitTokenRequest = null;
        string _token;
 
        public SPAuthenticationHelper(string uri)
        {
            _uri = new Uri(uri);
            Cookies = new CookieContainer();
        }
 
        public voidSigninAsync(string userName, string password)
        {
            if (!_isAuthenticationInProgress)
            {
                _isAuthenticationInProgress = true;
                getTokenAsync(userName, password);
            }
        }
 
       
        private  void getTokenAsync(stringuserName, string password)
        {
            string tokenRequestXml = string.Format(_samlXml, userName, password, _uri.Host);
 
            _getTokenRequest.Method = “POST”;
            _getTokenRequest.BeginGetRequestStream(newAsyncCallback(Get_GetToken_RequestStreamCallback), tokenRequestXml);
        }
 
        private voidGet_GetToken_RequestStreamCallback(IAsyncResultresult)
        {
            string tokenRequestXml = (string)result.AsyncState;
            var reqstream = _getTokenRequest.EndGetRequestStream(result);
            using (StreamWriterw = new StreamWriter(reqstream))
            {
                w.Write(tokenRequestXml);
                w.Flush();
            }
            _getTokenRequest.BeginGetResponse(new AsyncCallback(Get_GetToken_ResponseStreamCallback), null);
        }
 
 
 
        private voidGet_GetToken_ResponseStreamCallback(IAsyncResultresult)
        {
            _token = null;
 
            varresponse = _getTokenRequest.EndGetResponse(result);
 
            var xDoc = XDocument.Load(response.GetResponseStream());
 
            var body=xDoc.Descendants(XName.Get(“Body”, _nsS)).FirstOrDefault();
            if (body != null)
            {
                var fault = body.Descendants(XName.Get(“Fault”, _nsS)).FirstOrDefault();
                if (fault != null)
                {
                    var error=fault.Descendants(XName.Get(“text”, _nspsf)).FirstOrDefault();
                    if (error != null)
                        throw new Exception(error.Value);
                }
                else
                {
                    var token = body.Descendants(XName.Get(“BinarySecurityToken”, _nswsse)).FirstOrDefault();
                    if (token != null)
                    {
                        _token = token.Value;
                        SubmitTokenAsync();
                    }
                }
            }           
        }
 

        private  void SubmitTokenAsync()
        {
 
            UriBuilder bldr = newUriBuilder(_uri.Scheme, _uri.Host, _uri.Port);
            _submitTokenRequest = HttpWebRequest.CreateHttp(bldr.Uri + _login);
            _submitTokenRequest.CookieContainer = Cookies;
            _submitTokenRequest.Method = “POST”;
            _submitTokenRequest.BeginGetRequestStream(newAsyncCallback(Get_SubmitToken_RequestStreamCallback), null);
        }
 
        private voidGet_SubmitToken_RequestStreamCallback(IAsyncResultresult)
        {
            var requestStream = _submitTokenRequest.EndGetRequestStream(result);
            using (StreamWriterw = new StreamWriter(requestStream))
            {
                w.Write(_token);
                w.Flush();
            }
            _submitTokenRequest.BeginGetResponse(newAsyncCallback(Get_SubmitToken_ResponseCallback), null);
        }
 
        private voidGet_SubmitToken_ResponseCallback(IAsyncResultresult)
        {
            UriBuilder bldr = newUriBuilder(_uri.Scheme, _uri.Host, _uri.Port);
 
            varresponse = _submitTokenRequest.EndGetResponse(result);
            string responseString = (newStreamReader(response.GetResponseStream())).ReadToEnd();
           
            bldr.Path = null;
            Cookies = _submitTokenRequest.CookieContainer;//.GetCookies(bldr.Uri);
            _isAuthenticationInProgress = false;
            IsAuthenticated = true;
            if (OnAuthenticated != null)
            {
                EventArgs args = new EventArgs();
                OnAuthenticated(this, args);
            }
        }
    }
}
 
 

Tech in the 603, The Granite State Hacker

Reliving “Revolutionary” with Windows 8

“What do you think of Windows 8?”   I hear this question all the time… everywhere I go.   I hear people talking about it on the bus, in line at coffee shops, and even in odd places like hospital rooms.  It’s the biggest change we’ve had in the PC in well more than a decade.  Everyone knows this is as big as broadband in everyone’s home.

But… more than a decade?   Really? 

Definitely.  How old would a child be if it was born the last time there was a *true*, major version iteration of Windows?   3?  8…? 

How about…  18?   Yeah…  18… old enough to drive.  Old enough to be looking at colleges. The Daytona (Windows NT) / Chicago (Windows 95) user experience, were it a child, would now be looking at an opportunity to suffer the choice between Romney or Obama.  The experience unleashed on IT and the public introduced us to the Start menu, the Desktop, managed application installs, and several other major features that the enterprise and private user alike have now literally grown up on.

Some might argue that Windows XP was a hefty revision that almost qualifies, but I would say not so much.  Improvements abounded, but the core user experience hasn’t changed by more than revision increments in Windows 98, ME, 2000, XP, 2003, 2008, 7… really…  since Windows 95. 

But, with Windows 8, this changes.  Windows 8 brings us a whole new user experience in the “Modern UI” formerly known as “Metro UI”. 

If you recall, Windows 95 still essentially lived on top of DOS, and had enough of the Windows 3.x framework to run all the apps we’d already come to depend on (like Word 6, Excel 5, and Windows AOL 2.5).  While those programs ran in Chicago, there were compatibility issues, and the user interface really started to look crusty on legacy applications.  I was actually a relatively late adopter, waiting until Windows 98 before I finally succumbed to the dark side. (I had discovered IBM OS/2 Warp and become a fan… it actually took a 1-2 punch to Warp to get me to switch.  1:  When Warp was stable, it was unbeatable, but when it crashed it was unrecoverable, (and crash, it inevitably did).  2:   Command & Conquer / Red Alert, which had an improved video mode that was only available when installed in Windows… and it was even more awesome in that improved resolution mode. )

Just like Windows 95, Windows 8 is a transitional OS.

One of the big things I keep hearing about Windows 8 is… what a P.I.T.A. is is to figure out. “Microsoft is taking a huge risk with this… why are they breaking my Windows?”, I hear.  Or…  “I’m open-minded.  I subjected myself to it until the pain became unbearable.  (I can’t wait until Mac OS X takes over.)”

Transition, though?  Yes.  Transition.  Again, this is the first real full version increment of the Windows user experience that we’ve seen in years, and it all comes down to this Modern UI thing.  It does exactly what Windows 95 did to Windows 3.x on DOS.  It wipes the slate clean and re-imagines how we operate our computers from the ground up using modern human interface devices… (HIDs). 

Touch screen, movement, gestures, enhanced 3D graphics… these are things that started to accelerate development not long after the release of 95, but the world was still on the Windows 95 learning curve.  Hardware was too immature & expensive to develop an OS around them then… So, while you were getting comfortable with your desktop, (if you haven’t noticed) your cell phone’s user experience surpassed your desktop.

So on the surface (no pun intended) this is what Windows 8 is…  it’s a full OS-deep refresh that catches home computing back up to what people have gotten used to in their cellphones.

“Common sense” says this all implies a true P.I.T.A. for people and companies that dig in on it. 

Let’s look a little deeper, though, at what else this represents.  Again, this is a transitional OS.  It does everything the old user experience did… if you dig a bit.  It does this to support the old applications with their freshly encrusted-feeling user experience.  People can continue leveraging your old technology investments.  Indeed, you can continue making investments in the old user experience…  just know that the writing’s on the wall. 

It’s only a matter of time before people do what they inevitably did with Daytona/Chicago… adopt, extend, and embrace, or be extinguished.  

Why?  Because… when it comes down to it, the part that people really hate is not the “user experience” part.   It’s the “NEW” part that hurts.  Once the “NEW” wears off, what you’ve got left is a really genuinely cleaner, better, more efficient UI that leverages new hardware in important ways, and puts it years ahead of desktop OS competition, both in terms of capability, and even in terms of price point…  and pushes that same advantage out seamlessly to a myriad of other devices.  So getting past the sharp learning curve on one device means you’ll be rocking the new UI everywhere in no time.

Like the glory days of the Dot-Com boom, the days of Daytona & Chicago, these will be days of learning and technical renovation, even re-invention.  This is what I see coming with Windows 8 in the desktop, with an added benefit of being even more ubiquitous than it was back in the 90’s.  With the coming of Surface, Windows Phone 8, your apps will have more opportunity to run in more places, on more machines, than ever before…. using more “Star Trek” functionality than we’re yet used to. 

Those looking to remodel that kitchen… here’s your wake up call.  Windows 8’s user experience is representative of what made the Dot Com days so great… (and there were some plus sides.)  It was when leveraging any of the revolutionary new technology became a competitive advantage all by itself.  Early adopters will feel the pinch of the initial investment, but… with some planning, will reap the rewards by having that pain behind them by the time Windows 9 rolls around. 

I, for one, look forward to my new OS overlord.

Tech in the 603, The Granite State Hacker

Time to Remodel the Kitchen?

A few good reasons to consider keeping your IT infrastructure up to snuff…

http://edgewatertech.wordpress.com/2012/08/21/time-to-remodel-the-kitchen/

(I’m honored to have the post accepted & published on Edgewater’s blog.)  🙂 

Tech in the 603, The Granite State Hacker

Using Client Certs to Pull Data from WCF in SSIS Data Flow Transform Script

I’ve recently had the opportunity to brush off my SSIS skills and revisit this toolset.   In my most recent usage, I had a requirement to use SSIS to pull data from a WCF web service that was a) using the net.tcp protocol, and b) used transport security with a client X.509 certificate for authentication.

This was fun enough by itself.  Configuring WCF tend typcially to be non-trival even when you don’t have to tweak app.config files for SQL SSIS services.  One of my goals, in fact, was to avoid having to update that, meaning I had to put code in my SSIS Script block in the data flow to configure my channel & security & such.

Luckily, I was able to find examples of doing this with wsHttpBinding’s, so it wasn’t a stretch to tweak it for netTcpBinding with the required changes to support certificate authenticated transport security.

Here’s the code…

using System;
usingSystem.Data;
usingMicrosoft.SqlServer.Dts.Pipeline.Wrapper;
usingMicrosoft.SqlServer.Dts.Runtime.Wrapper;
usingSystem.ServiceModel;
usingSC_13defb16ae45414dbac17137434aeca0.csproj.PaymentSrv;


[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
    ChannelFactory<IProfile> channelFactory;
    IProfileclient;
    public override voidPreExecute()
    {
        base.PreExecute();

        boolfireAgain = false;
        this.ComponentMetaData.FireInformation(0, “Pull From Profile Service.PreExecute”, “Service URI: ‘” + this.Variables.varProfileServiceUrl + “‘”, null, 0, ref fireAgain);
        this.ComponentMetaData.FireInformation(0, “Pull From Profile Service.PreExecute”, “Cert Fingerprint: ‘” + this.Variables.varClientCertFingerprint + “‘”, null, 0, ref fireAgain);

        //create the binding
        NetTcpBindingbinding = new NetTcpBinding();
        binding.Security.Mode = SecurityMode.Transport;
        binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;
        binding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;

       
        EndpointAddressendpointAddress = new EndpointAddress(this.Variables.varPaymentServiceUrl);
        channelFactory = new ChannelFactory<IProfile>(binding, endpointAddress);

        channelFactory.Credentials.ClientCertificate.SetCertificate(
            System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine,
            System.Security.Cryptography.X509Certificates.StoreName.My,
            System.Security.Cryptography.X509Certificates.X509FindType.FindByThumbprint,
            this.Variables.varClientCertFingerprint);
            //” x8 60 66 09 t6 10 60 2d 99 d6 51 f7 5c 3b 25 bt 2e 62 32 79″);

        channelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode =
            System.ServiceModel.Security.X509CertificateValidationMode.PeerTrust;
       
        //create the channel
        client = channelFactory.CreateChannel();

       
        IClientChannel channel = (IClientChannel)client;

       
        channel.Open();
        this.ComponentMetaData.FireInformation(0, “Pull From Profile Service.PreExecute”, “Open Succeeded.”, null, 0, reffireAgain);


    }

    public override voidPostExecute()
    {
        base.PostExecute();

        //close the channel
        IClientChannelchannel = (IClientChannel)client;
        channel.Close();

        //close the ChannelFactory
        channelFactory.Close();

    }

    public override voidInput0_ProcessInputRow(Input0Buffer Row)
    {
        GuidtxGuid = Guid.NewGuid();
        Profileprofile = null;
        try
        {
            profile = client.getProfile(txGuid, Row.ProfileId);
            Row.PSProfileType = GetProfileType(profile);
           
        }
        catch (Exception ex)
        {
            stringmessage = ex.Message();
            Log(message, 0, null);
        }
       
       
    }
    private string GetProfileType(Profileprofile)
    {
        return “x”;
    }
}

So one of the challenges I encountered while using this method had to do with the client certificate.  This error drove me nuts:

The credentials supplied to the package were not recognized.
Server stack trace:
   at System.Net.SSPIWrapper.AcquireCredentialsHandle(SSPIInterface SecModule, String package, CredentialUse intent, SecureCredential scc)
   at System.Net.Security.SecureChannel.AcquireCredentialsHandle(CredentialUse credUsage, SecureCredential& secureCredential)
   at System.Net.Security.SecureChannel.AcquireClientCredentials(Byte[]& thumbPrint)
   at System.Net.Security.SecureChannel.GenerateToken(Byte[] input, Int32 offset, Int32 count, Byte[]& output)
   at System.Net.Security.SecureChannel.NextMessage(Byte[] incoming, Int32 offset, Int32 count)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
   at System.Net.Security.SslStream.AuthenticateAsClient(String targetHost, X509CertificateCollection clientCertificates, SslProtocols enabledSslProtocols, Boolean checkCertificateRevocation)
   at System.ServiceModel.Channels.SslStreamSecurityUpgradeInitiator.OnInitiateUpgrade(Stream stream, SecurityMessageProperty& remoteSecurity)
   at System.ServiceModel.Channels.StreamSecurityUpgradeInitiatorBase.InitiateUpgrade(Stream stream)
   at System.ServiceModel.Channels.ConnectionUpgradeHelper.InitiateUpgrade(StreamUpgradeInitiator upgradeInitiator, IConnection& connection, ClientFramingDecoder decoder, IDefaultCommunicationTimeouts defaultTimeouts, TimeoutHelper& timeoutHelper)
   at System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.SendPreamble(IConnection connection, ArraySegment`1 preamble, TimeoutHelper& timeoutHelper)
   at System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.DuplexConnectionPoolHelper.AcceptPooledConnection(IConnection connection, TimeoutHelper& timeoutHelper)
   at System.ServiceModel.Channels.ConnectionPoolHelper.EstablishConnection(TimeSpan timeout)
   at System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open()
Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at System.ServiceModel.ICommunicationObject.Open()
   at ScriptMain.PreExecute()
   at Microsoft.SqlServer.Dts.Pipeline.ScriptComponentHost.PreExecute()

If you look at it, this is an authentication error.  Tracing the code, it happens AFTER the code successfully retrieves the client certificate from the certificate store.  The call to SetServerCertificate succeeds without incident.

The error hits  when the code opens the channel, and tries to use the private key attached to the client certificate to prove to the server that “I’m a valid client.”

I went nuts because I was an administrator on the machine, and had installed the client certificate to the certificate store myself.  It initially worked, and there was no indication that there was a problem getting the certificate from the cert store.

It turns out that when you use the machine store under these circumstances, I needed to give myself explicit permission to the client certificate in order for the SetServerCertificate to get the private key along with the client certificate.  This was counter-intuitive for two *additional* reasons:  1)  I was an administrator on the box, and already should have had this permission by the fact that my login account belonged to the administrators group (which you can see from the pic below, also had access.)  2)   It worked the day before.  When I imported the private key originally to the key store, it appears somewhere in the depths of Windows 7 (and this applied on Server 2008 R2 as well) I still had permission in my active session context.  When I logged out, that login context died, and, coming back the next day, I logged in again, not realizing I wouldn’t be able to access the key.  Giving myself explicit permission as shown below allowed me to run my SSIS package within Visual Studio and from SSMS.

(Sorry, Blogger’s not letting me include this bigger… click it for full size view.)
Tech in the 603, The Granite State Hacker

Microsoft Announces Windows Phone Dev Center

I’ve learned a few things in the past months in working with the SharePoint community.  Namely, if you don’t have something nice to say, don’t say anything at all.  In today’s age of social media meeting business networking, this is more important than ever.  

I hope, however, that Microsoft’s Windows Phone Dev Team forgives me for the tough love I dished out on them back in May.  (I won’t even link to that post.)  

I love developing apps in Silverlight & C# for my phone, and I’m so happy to see an update that directly impacts us App Developers…  

Here’s the Windows Phone Developers Blog:
http://windowsteamblog.com/windows_phone/b/wpdev/archive/2012/08/07/meet-the-windows-phone-dev-center.aspx

Here’s the great looking new app publisher’s experience for Windows Phone Developers:
https://dev.windowsphone.com/

I haven’t fully explored it yet, but at first glance, it looks much more like the excellent developer’s & publishers’ experience I’ve come to take for granted from Microsoft… I can’t wait to explore it more and see how it all came together.

Tech in the 603, The Granite State Hacker

Keeping in the Code

At the end of the day, the business solution is always the most important part of the equation, but it’s not the only part.  While I’m working on a solution, I’m also looking at tools, scaffolding, and framework.  This is especially true if others are going to be working on the project, and that accounts for nearly every non-trivial project.

How easy is it to set up?  How easy is it to work with?  Do the expressions make sense?  Can I hand it off to my least experienced teammate, get them to pick this up, and expect reasonable results?  (For that matter, can I hand it off to my most experienced teammate and expect them to respect the design decisions I made? )

Keeping my head in the code is critical.  Loosing touch with tools means shooting in the dark on the above questions.  It doesn’t matter what their experience is, if you ask someone to push a tack into a corkboard, hand them the wrong tools for the job, they won’t be able to push the thumbtack into the corkboard… or you’ll nuke your budget paying for tools that are overpowered for the job.  (But that thumbtack will be SO IN THERE!)

In any case, in most projects, after the architecture & technical designs have been sorted out, frameworks, built, automations put in place, I’ll take on the coding, too.

Of course, I’ve said this before…  if you can really simplify the work, what’s to stop you from taking the extra step and automating it?   I’m always eyeing code, especially “formulaic”, repetititive stuff, looking for opportunities to simplify, abstract, and/or automate.

Tech in the 603, The Granite State Hacker

Custom Guid’s

Caught a question from Stacy Draper, @StacyDraper this morning about custom guids, to make them more recognizable.

It reminded me of a post I saw recently about Facebook using hex characters to make IPv6 addresses more recognizable.

Here’s what I was thinking… create a guid that has an embedded word.

For example, the following code creates a Guid that always starts with FACEB00C:

using System;
usingSystem.Collections.Generic;
using System.Linq;
usingSystem.Text;
 
namespaceCustomGuidTest
{
    class Program
    {
        static void Main(string[] args)
        {
            GuidcustomGuid = GenerateCustomGuid();
            Console.WriteLine(customGuid.ToString(“B”));
            Console.ReadKey();
        }
 
        static Guid GenerateCustomGuid()
        {
            Guidresult;
 
            //0xFACEB00C
            //backwards, but required this to achieve desired result
            //Likely due to some standard with respect to bit order.
            byte[] custom = new byte[] { 0x0C, 0xB0, 0xCE, 0xFA  };

            byte[] random = Guid.NewGuid().ToByteArray();
            byte[] final = new byte[16];
            for(int idx = 0; idx <16; idx++)
            {
                if(idx >= custom.Length)
                {
                    final[idx] = random[idx];
                }
                else
                {
                    final[idx] = custom[idx];
                }
            }
            result = newGuid(final);
            returnresult;
        }
    }
}

Example output:
{faceb00c-7828-4e99-8cf5-280e33202670}

Tech in the 603, The Granite State Hacker

Hobby Project Supporting #NoKidHungry Now Has Free Edition

I’m please to say my hobby project, a Windows Phone app I call “Jimmy Sudoku” is now available both for free or for purchase. 

The two SKUs in the Windows Phone App Marketplace are identical.  

The free version is available in almost all markets around the world (including the US). 

The paid version is only available in the US and 100% of the proceeds continue to support #NoKidHungry.

Link to Free SKU

Link to #NoKidHungry SKU

Please…  Enjoy!  🙂

Tech in the 603, The Granite State Hacker

GSSPUG Hub (Free App) for Windows Phone Now Available

My “artisan portfolio” of Windows Phone apps just DOUBLED in size!  Yes, I’ve now successfully published my second Windows Phone app.  🙂

The Granite State SharePoint Users Group Hub is a somewhat minimal app, but if you’re a member of the group, it’s got some useful features.   My favorites are being able to get info about the next meeting, (both in the app, and as a live tile) and being able to RSVP through EventBright.

The direct link to find it in the Marketplace on your Windows Phone is this.

Regarding the name…  GSSPUG?  Ya, I know… it’s not quite as intuitive as NHSPUG…    

If you’re from New Hampshire, you know you search for “Granite State” any time you’re looking for something local…  and if you don’t know that, it probably is just as well you don’t find it.  😉

One other nice thing is that the content is largely driven from the group’s web site, which, of course, is a SharePoint site.   The app does require a network connection, but it can be updated without having to go through the week-long process of publishing an update. 

Like Jimmy Sudoku, the app uses your phone’s system wide theme colors.

Essentially this is what ends up in the Hub app.

And it appears like so: