SharePoint IRM Protectors implemented in managed code

Can SharePoint IRM (Information Rights Management) Protectors be implemented in managed code (C#)?  - was the question I asked when I had a customer who implemented an IRM protector in C# and was running into a problem in registering it with SharePoint.

Technically, SharePoint IRM Protector is a COM interface and their internal implementation should not really matter to the process that is using the COM object (SharePoint).  So yes – an IRM protector implemented in managed code can be used with SharePoint.  Below are the steps recommended to achieve this.

1. The sequence of method declaration in interface is very important.  It must be exactly the same as its IDL definition.  The interface referred to here is I_IrmProtector.  The interface function declaration can be referred from the IDL file that is made available through the SampleDocProtector sample in the SharePoint Server 2007 SDK.  Here’s how the interface is defined.

 [
     uuid(fcfbc0ac-672b-452d-80e5-40652503d96e)
 ]
 interface I_IrmProtector: IUnknown
 {
     HRESULT HrInit(
         /*[OUT]*/ BSTR  *pbstrProduct,
         /*[OUT]*/ DWORD *pdwVersion,
         /*[OUT]*/ BSTR  *pbstrExtentions,
         /*[OUT]*/ BOOL  *pfUseRMS) ;
  
     HRESULT HrIsProtected(
         /*[IN]*/  ILockBytes *pilbInput,
         /*[OUT]*/ DWORD *pdwResult) ;
  
     HRESULT HrSetLangId(
         /*[IN]*/  LANGID langid) ;
  
     HRESULT HrProtectRMS(
         /*[IN]*/  ILockBytes         *pilbInput,
         /*[IN]*/  ILockBytes         *pilbOutput,
         /*[IN]*/  I_IrmPolicyInfoRMS *piid,
         /*[OUT]*/ DWORD              *pdwStatus) ;
  
     HRESULT HrUnprotectRMS(
         /*[IN]*/  ILockBytes         *pilbInput,
         /*[IN]*/  ILockBytes         *pilbOutput,
         /*[IN]*/  I_IrmPolicyInfoRMS *piid,
         /*[OUT]*/ DWORD              *pdwStatus) ;
  
     HRESULT HrProtect(
         /*[IN]*/  ILockBytes      *pilbInput,
         /*[IN]*/  ILockBytes      *pilbOutput,
         /*[IN]*/  I_IrmPolicyInfo *piid,
         /*[OUT]*/ DWORD           *pdwStatus) ;
  
     HRESULT HrUnprotect (/*[IN]*/  ILockBytes      *pilbInput,
         /*[IN]*/  ILockBytes      *pilbOutput,
         /*[IN]*/  I_IrmPolicyInfo *piid,
         /*[OUT]*/ DWORD           *pdwStatus) ; 
 };

And here’s the same interface definition in the managed implementation.

 [ComVisible(true)]
 [Guid("FCFBC0AC-672B-452D-80E5-40652503D96E")]
 [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 public interface I_IrmProtector
 {
     void HrInit(
         [MarshalAs(UnmanagedType.BStr)]
         ref string pbstrProduct,
         ref uint pdwVersion,
         [MarshalAs(UnmanagedType.BStr)]
         ref string pbstrExtentions,
         ref int pfUseRMS);    
  
     void HrIsProtected(
         ILockBytes pilbInput,
         ref uint pdwResult);
  
     void HrSetLangId(
         ushort langid);
  
     void HrProtectRMS(
         ILockBytes pilbInput,
         ILockBytes pilbOutput,
         I_IrmPolicyInfoRMS piid,
         ref uint pdwStatus);
  
     void HrUnprotectRMS(
         ILockBytes pilbInput,
         ILockBytes pilbOutput,
         I_IrmPolicyInfoRMS piid,
         ref uint pdwStatus);
  
     void HrProtect(
         ILockBytes pilbInput,
         ILockBytes pilbOutput,
         I_IrmPolicyInfo piid,
         ref uint pdwStatus);
  
     void HrUnprotect(
         ILockBytes pilbInput,
         ILockBytes pilbOutput,
         I_IrmPolicyInfo piid,
         ref uint pdwStatus);
 }

2. Once this is done and the protector is coded, the Visual Studio project should be built either targeting x64 or Any CPU.

image

3. The resulting DLL should then be registered using x64 version of Regasm.exe.  NOTE: If x86 version of Regasm.exe is used that will result in CoCreateInstance error “Class not registered”.

4. The DLL should also be installed in GAC.  NOTE: If this is not done it will result in CoCreateInstance error “System cannot find the file specified”.

5. Setup IRM protector registry keys per How to: Register an IRM Protector.

6. And finally register the IRM protector with SharePoint as mentioned in the last section of Sample: Creating Custom IRM Protectors titled Setting and Enabling IRM Add-in Property.

Once these steps are performed an IRM protector implemented in managed code will successfully play with SharePoint after an IISRESET.  You should see the following captured in ULS denoting the protector was loaded successfully.

 03/28/2014 14:00:00.29  w3wp.exe (0x1A6C)       0x1BB8 SharePoint Foundation Information Rights Management (                5034       Information        Information Rights Management (IRM): The initialization of protector {XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} was completed. Protector: {XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}

For troubleshooting COM registration problems you can use the below script in a VBS file and provide the fully qualified name of the protector COM object.  In the below script I am trying to create an object of SampleIRMProtector.SampleIRMProtector which is the name of the sample protector implemented.

 /********* Script Start ******************/
 On Error Resume Next
 Set sampleIRMProtector=NOTHING
  
 set sampleIRMProtector=CreateObject("SampleIRMProtector.SampleIRMProtector")
  
 IF sampleIRMProtector IS NOTHING THEN
    Wscript.Echo "Unable to load SampleIRMProtector."
 ELSE
 Wscript.Echo "SampleIRMProtector loaded successfully."
 END IF
 /********* Script End ******************/

If you don’t see SampleIRMProtector loaded successfully message then COM registration could be the problem and troubleshooting should being from there.

I haven’t seen large number of developers implementing IRM protectors for SharePoint mostly because it has to be implemented in COM (C++ sample is what is provided in SDK).  But this approach of developing IRM protectors in managed code would be fairly easy for developers working on C# in terms of implementing the actual protector.

This was something new I learnt and thought of sharing this out!