Wednesday, August 18, 2004

BITS C# Wrapper

I was recently using BITS (Background Intelligent Transfer Service) to create a component for uploading and downloading files asynchronously from a windows sharepoint services document library. One of the requirement was transfer can only be done by authenticated users. BITS version 1.5 and above supports this via the IBackgroundCopyJob2::SetCredentials method. However, this method uses an union in its parameter and C# does not have union. After playing with interop attributes for a while I've worked out the necessary attributes to make this work with a C# only wrapper without going to managed C++.

After looking at the definition for BG_AUTH_CREDENTIALS in the .h file from the SDK, it turns out that the union (BG_AUTH_CREDENTIALS_UNION) only contains one type which is BG_BASIC_CREDENTIALS. I was able to make the call to SetCredentials work by using the FieldOffset attribute in the struct definitions.

Here is an extract of the important struct definitions.

internal enum BG_AUTH_SCHEME
{
// Fields
BG_AUTH_SCHEME_BASIC = 1,
BG_AUTH_SCHEME_DIGEST = 2,
BG_AUTH_SCHEME_NTLM = 3,
BG_AUTH_SCHEME_NEGOTIATE = 4,
BG_AUTH_SCHEME_PASSPORT = 5
}

internal enum BG_AUTH_TARGET
{
// Fields
BG_AUTH_TARGET_SERVER = 1,
BG_AUTH_TARGET_PROXY = 2,
}

[StructLayout(LayoutKind.Explicit, Size=16, Pack=4)]
internal struct BG_AUTH_CREDENTIALS
{
[FieldOffset(0)]
public BG_AUTH_TARGET Target;

[FieldOffset(4)]
public BG_AUTH_SCHEME Scheme;

[FieldOffset(8)]
public BG_AUTH_CREDENTIALS_UNION Credentials;
}

[StructLayout(LayoutKind.Explicit, Size=8, Pack=4)]
internal struct BG_AUTH_CREDENTIALS_UNION
{
[FieldOffset(0)]
public BG_BASIC_CREDENTIALS Basic;
}

[StructLayout(LayoutKind.Explicit, Size=8, Pack=4)]
internal struct BG_BASIC_CREDENTIALS
{
[FieldOffset(0)]
[MarshalAs(UnmanagedType.LPWStr)]
public string UserName;

[FieldOffset(4)]
[MarshalAs(UnmanagedType.LPWStr)]
public string Password;
}

I've posted the interop library at http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=99f2f473-26de-41cd-bf39-8f7e75fda5aa

7 comments:

Eddie Tse said...

Most of the definitions are marked "internal" because I need the final assembly to be CLR compliant and some of types defined in the interface definition were not.

If you don't need your assembly to be CLR compliant, you can change the interfaces and types from internal to public. Or you can mark your JobCallback class to be internal as well.

Anonymous said...

This is a great piece of work. Suits my needs perfectly

Anonymous said...

So many thanks for your work ! I can't understand why MS don't provide it ... Anyway, you did it.
Regards

Anonymous said...

Hi Thanks for the workaround. I had problems running my application in 64 bit and found that I needed to change the LayoutKind for the _BG_BASIC_CREDENTIALS struct to Sequential. Then it all worked fine.

Anonymous said...

thanks for explanation using code... nice stuff...

Anonymous said...

Can anyone help me with setting up credentials? If I set them before I Activate() or Resume() it throws an error.

If I do it after, it doesn't seem to use the credentials since when I call resume the file upload errors out saying that authentification is required.

Does anyone understand what I'm doing wrong?

Thanks

digitis @ yahoo dot com

Anonymous said...

Now that gotdotnet.com is dead, here is the download link on MSDN Code Gallery.