Writing a SmsServiceProvider.dll extension

I've written a SmsServiceProvider.dll class to work with the SMS OTP, and now I've stumbled across this error when resetting my password:

 

Microsoft.IdentityManagement.SmsServiceProviderManager: System.TypeLoadException: An error occurred while loading the custom SMS provider DLL.  Please review the inner exception details. ---> System.TypeLoadException: The custom SMS provider DLL does not implement the required interface.

   at Microsoft.IdentityManagement.SmsServiceProvider.SmsServiceProviderManager.InitializeSmsServiceProvider()

   --- End of inner exception stack trace ---

   at Microsoft.IdentityManagement.SmsServiceProvider.SmsServiceProviderManager.InitializeSmsServiceProvider()

   at Microsoft.IdentityManagement.SmsServiceProvider.SmsServiceProviderManager.get_ExternalSmsServiceProviderExists()

   at Microsoft.ResourceManagement.Workflow.Hosting.SmsNotificationServiceImpl.SendSmsMessage(String mobileNumber, String message, Guid requestId, Dictionary`2 deliveryAttributes)

   at Microsoft.ResourceManagement.Workflow.Activities.OneTimePasswordSmsAuthenticationGate.SendOneTimePassword(String plainTextOneTimePassword)

Now the weird thing is that I've seen this in the beginning when fooling around, but then it started working. Now all of a sudden no matter what I do it just doesnt want to work.

Here's my code, a bit stripped though

using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.IdentityManagement.SmsServiceProvider;
using System.Diagnostics;
using System.Configuration;
using Shared.DataContracts;
using System.Reflection;

namespace FIM.SmsServiceProvider
{
    public class SmsServiceProvider : ISmsServiceProvider
    {
        readonly static TraceSource trace = new TraceSource("SmsServiceProvider", SourceLevels.All);

        public void SendSms(string mobileNumber,
                            string message,
                            Guid requestId,
                            Dictionary<string, object> deliveryAttributes)
        {
            
//some logging stripped                
mySMSProvider.SendSms(ui, message);            
            }            
        }       
    }

    class mySMSProvider
    {
        readonly static TraceSource trace = new TraceSource("SmsServiceProvider", SourceLevels.All);

        mySMSProvider()
        {
        }

        public static int SendSms(UserInfo ui, string message)
        {
           //send sms logic stripped
            return 1;
        }

        //public static string GetRequestData(string mobile, string message)
        //{
        //    return "";
        //}        

        //public void GetCredentials()
        //{
        //}       
    };
}

This is really really weird, as the only method to implement for that Interface is sendSMS which to me seems pretty present in the code...
December 12th, 2012 5:47pm

I remember there was a bug in the product (i think it's fixed) -- basically the workaround is to make sure the DLL only contain 1 clas... so if u want >1 class, use inner class instead
Free Windows Admin Tool Kit Click here and download it now
December 12th, 2012 6:08pm

Bah,

No matter how "simple" I make the code, even by only specifying the public sendSms method with some dummy lines in it the thing doesnt work. Restarting the FIM Service should be enough to be "up to date" about the newly compiled DLL eh?

Even taking the sample code (from http://technet.microsoft.com/en-us/library/jj134288(v=ws.10)) and compiling as is doesn't seem to work.

This is turning into a nightmare.

Kind regards,

December 12th, 2012 7:49pm

can u make sure your DLL only have 1 class defined?
Free Windows Admin Tool Kit Click here and download it now
December 12th, 2012 8:23pm

Additionally, the *one* class defined in SmsProvider must have a no-argument constructor.

The SMS wrapper interface would be easier to troubleshoot if it simply did this:

return assembly.GetTypes().Where(t => typeof(ISmsServiceProvider).IsAssignableFrom(t)).Single().GetConstructor(new Type[0]).Invoke();

Unfortunately, it presently conceals informative exceptions with uninformative ones, and throws a fit if the "first" type in the assembly isn't what it's looking for.

December 12th, 2012 9:34pm

Additionally, the *one* class defined in SmsProvider must have a no-argument constructor.

The SMS wrapper interface would be easier to troubleshoot if it simply did this:

return assembly.GetTypes().Where(t => typeof(ISmsServiceProvider).IsAssignableFrom(t)).Single().GetConstructor(new Type[0]).Invoke();

Unfortunately, it presently conceals informative exceptions with uninformative ones, and throws a fit if the "first" type in the assembly isn't what it's looking for.

Free Windows Admin Tool Kit Click here and download it now
December 12th, 2012 9:34pm

This is also Thomas. So in my first example I've got two classes and the  smsserviceprovider  has the sendsms method and no "no argument" constructor like Steve states. so I'm wrong twice?
so IT would be better to nest the myserviceprovider class in the first one and add a no argument constructor for the first class?
December 13th, 2012 1:28am

This is also Thomas. So in my first example I've got two classes and the  smsserviceprovider  has the sendsms method and no "no argument" constructor like Steve states. so I'm wrong twice?
so IT would be better to nest the myserviceprovider class in the first one and add a no argument constructor for the first class?
  • Edited by Invisibal Wednesday, December 12, 2012 10:30 PM
Free Windows Admin Tool Kit Click here and download it now
December 13th, 2012 1:28am

It's probably just the problem of having two Types in the assembly; the no-arg constructor is implicitly available unless hidden or alternate constructors are present.  I just noticed it as another weird quirk in the SmsProvider loader.
December 13th, 2012 1:32am

After asking a (dev) colleague for some insights I finally got it working again. As you guys stated: use one class AND make sure there's a no-arg constructor. I really had to do both.

Oh and perhaps eassy to be forgotten: adding a "service reference" in visual studio also results in additional class files! And the same goes for "settings" which results in a settings.cs file.

I'm cooking up a blogpost for future reference, but it's kinda busy here...

Thanks all for the quick replies!

Free Windows Admin Tool Kit Click here and download it now
December 18th, 2012 11:15am

Did you get a chanse to write that blogpost? I'm stuck aswell on the SMS OTP. Everything goes as planned but I'm not recieving any sms's..
January 31st, 2013 1:47pm

Hello, I had similar problems. At this moment I have created a DLL that doesn't sent any SMS, but writes the SMS code into a log file on the FIM Server. This allows me to test the SMS OTP environment without an actual SMS provider. Just compile this code and it will create the DLL. This will allow the Windows 7 client to continue when you have configured the SMS OTP. Instead of sending an SMS it will write the "secret code" to the LOG file.

In the source code there is an hardcoded path to c:\temp\smslogging.txt make sure the FIMServer account has enough rights on the folder.

As you can see I have stripped everything else so you don't need the SmsEncryptedCredentials.txt either.

Hope this helps anyone.

//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// Changed by Stefan Peters (with help :-)
//------------------------------------------------------------
namespace Microsoft.IdentityManagement.Samples
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Net;
    using System.Text;
    using Microsoft.IdentityManagement.SmsServiceProvider;
    using System.Web;
    using System.Security.Cryptography;
    using System.IO;
   
    public class SmsServiceProvider : ISmsServiceProvider
    {
        
        public void SendSms(string mobileNumber,
                            string message,
                            Guid requestId,
                            Dictionary<string, object> deliveryAttributes)
        {
		    string myDateTimeNow = DateTime.Now.ToString();
            string myAll = myDateTimeNow + " - " + mobileNumber + " - " + message;
            using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\temp\smslogging.txt", true))
            {
                file.WriteLine(myAll);
            }
          //  mySMSProvider.SendSms(mobileNumber, message);
        }
    }

    // class mySMSProvider
    // {
        // static string RequestURL = "http://www.mySMSProvider.contoso.com/sms.dll?Action=SendSMS";
        // static string adminAccount;
        // static string adminEmail;
        // static string adminPassword;

        // mySMSProvider()
        // {
        // }

        // public static int SendSms(string userMobileNumber, string message)
        // {
            // WebClient wc = new WebClient();
            // string requestData;

            // requestData = Microsoft.IdentityManagement.Samples.mySMSProvider.GetRequestData(userMobileNumber, message);

            // byte[] postData = Encoding.ASCII.GetBytes(requestData);

            // byte[] response = wc.UploadData(mySMSProvider.RequestURL, postData);

            // string result = Encoding.ASCII.GetString(response);  // result contains the error text

            // int returnValue = System.Convert.ToInt32(result.Substring(0, 4), NumberFormatInfo.InvariantInfo);
            // return returnValue;
        // }

        // public static string GetRequestData(string mobile, string message)
        // {

            // string myrequestData;

            // myrequestData = "AccountId=" + adminAccount
                 // + "&Email=" + System.Web.HttpUtility.UrlEncode(adminEmail)
                 // + "&Password=" + System.Web.HttpUtility.UrlEncode(adminPassword)
                 // + "&Recipient=" + System.Web.HttpUtility.UrlEncode(mobile)
                 // + "&Message=" + System.Web.HttpUtility.UrlEncode(message);

            // return myrequestData;


        // }

        // public void GetCredentials()
        // {
 
            // string mypwordFile = (@"C:\Program Files\Microsoft Forefront Identity Manager\2010\Service\SmsEncryptedCredentials.txt");


            // FileInfo info;
            // int len;
            // byte[] buffin;
            // byte[] buffout;

            // byte[] Entropy = { 9, 8, 7, 6, 5 };

            // info = new FileInfo(mypwordFile);
            // len = (int)info.Length;

            // buffin = File.ReadAllBytes(mypwordFile);
            // buffout = ProtectedData.Unprotect(buffin, Entropy, DataProtectionScope.CurrentUser);

            // File.WriteAllBytes(mypwordFile, buffout);

            // StreamReader sr = new StreamReader(mypwordFile);
            // adminAccount = sr.ReadLine();
            // adminEmail = sr.ReadLine();
            // adminPassword = sr.ReadLine();


            // sr.Close();

            // buffin = File.ReadAllBytes(mypwordFile);
            // buffout = ProtectedData.Protect(buffin, Entropy, DataProtectionScope.CurrentUser);

            // File.WriteAllBytes(mypwordFile, buffout);
            

        // }


    // };
}


Free Windows Admin Tool Kit Click here and download it now
July 31st, 2013 4:30am

Hello, I had similar problems. At this moment I have created a DLL that doesn't sent any SMS, but writes the SMS code into a log file on the FIM Server. This allows me to test the SMS OTP environment without an actual SMS provider. Just compile this code and it will create the DLL. This will allow the Windows 7 client to continue when you have configured the SMS OTP. Instead of sending an SMS it will write the "secret code" to the LOG file.

In the source code there is an hardcoded path to c:\temp\smslogging.txt make sure the FIMServer account has enough rights on the folder.

As you can see I have stripped everything else so you don't need the SmsEncryptedCredentials.txt either.

Hope this helps anyone.

//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// Changed by Stefan Peters (with help :-)
//------------------------------------------------------------
namespace Microsoft.IdentityManagement.Samples
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Net;
    using System.Text;
    using Microsoft.IdentityManagement.SmsServiceProvider;
    using System.Web;
    using System.Security.Cryptography;
    using System.IO;
   
    public class SmsServiceProvider : ISmsServiceProvider
    {
        
        public void SendSms(string mobileNumber,
                            string message,
                            Guid requestId,
                            Dictionary<string, object> deliveryAttributes)
        {
		    string myDateTimeNow = DateTime.Now.ToString();
            string myAll = myDateTimeNow + " - " + mobileNumber + " - " + message;
            using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"C:\temp\smslogging.txt", true))
            {
                file.WriteLine(myAll);
            }
          //  mySMSProvider.SendSms(mobileNumber, message);
        }
    }

    // class mySMSProvider
    // {
        // static string RequestURL = "http://www.mySMSProvider.contoso.com/sms.dll?Action=SendSMS";
        // static string adminAccount;
        // static string adminEmail;
        // static string adminPassword;

        // mySMSProvider()
        // {
        // }

        // public static int SendSms(string userMobileNumber, string message)
        // {
            // WebClient wc = new WebClient();
            // string requestData;

            // requestData = Microsoft.IdentityManagement.Samples.mySMSProvider.GetRequestData(userMobileNumber, message);

            // byte[] postData = Encoding.ASCII.GetBytes(requestData);

            // byte[] response = wc.UploadData(mySMSProvider.RequestURL, postData);

            // string result = Encoding.ASCII.GetString(response);  // result contains the error text

            // int returnValue = System.Convert.ToInt32(result.Substring(0, 4), NumberFormatInfo.InvariantInfo);
            // return returnValue;
        // }

        // public static string GetRequestData(string mobile, string message)
        // {

            // string myrequestData;

            // myrequestData = "AccountId=" + adminAccount
                 // + "&Email=" + System.Web.HttpUtility.UrlEncode(adminEmail)
                 // + "&Password=" + System.Web.HttpUtility.UrlEncode(adminPassword)
                 // + "&Recipient=" + System.Web.HttpUtility.UrlEncode(mobile)
                 // + "&Message=" + System.Web.HttpUtility.UrlEncode(message);

            // return myrequestData;


        // }

        // public void GetCredentials()
        // {
 
            // string mypwordFile = (@"C:\Program Files\Microsoft Forefront Identity Manager\2010\Service\SmsEncryptedCredentials.txt");


            // FileInfo info;
            // int len;
            // byte[] buffin;
            // byte[] buffout;

            // byte[] Entropy = { 9, 8, 7, 6, 5 };

            // info = new FileInfo(mypwordFile);
            // len = (int)info.Length;

            // buffin = File.ReadAllBytes(mypwordFile);
            // buffout = ProtectedData.Unprotect(buffin, Entropy, DataProtectionScope.CurrentUser);

            // File.WriteAllBytes(mypwordFile, buffout);

            // StreamReader sr = new StreamReader(mypwordFile);
            // adminAccount = sr.ReadLine();
            // adminEmail = sr.ReadLine();
            // adminPassword = sr.ReadLine();


            // sr.Close();

            // buffin = File.ReadAllBytes(mypwordFile);
            // buffout = ProtectedData.Protect(buffin, Entropy, DataProtectionScope.CurrentUser);

            // File.WriteAllBytes(mypwordFile, buffout);
            

        // }


    // };
}


  • Edited by SPeters Wednesday, July 31, 2013 8:29 AM
July 31st, 2013 11:27am

I just published a small blog post describing the error and the solution we are using in our FIM deployments today.

http://konab.com/building-fim-2010-r2-smsserviceprovider-dll/

Free Windows Admin Tool Kit Click here and download it now
April 1st, 2014 12:20pm

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics