IBackgroundCopyJobHttpOptions

Apr 6, 2009 at 10:26 PM
Has anyone implemented this interface yet? I'm looking for the easy way out... :)
Dec 20, 2012 at 1:31 PM
Edited Dec 20, 2012 at 1:32 PM

I've implemented this interface and added the following to the BITSinterop.cs

 

    #region IBackgroundCopyJobHttpOptions
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    [GuidAttribute("F1BD1079-9F01-4BDC-8036-F09B70095066")]
    [ComImportAttribute()]
    public interface IBackgroundCopyJobHttpOptions
    {
        void RemoveClientCertificate();
        void SetCustomHeaders([MarshalAs(UnmanagedType.LPWStr)] ref string RequestHeaders);
        void GetCustomHeaders([MarshalAs(UnmanagedType.LPWStr)] out string pRequestHeaders);
        void SetSecurityFlags([In] ulong Flags);
        void GetSecurityFlags(out ulong Flags);
    }
    #endregion


Then I extended the BitsJob.cs in ctor with

            this.httpOptions = this.job as IBackgroundCopyJobHttpOptions;
            this.httpOptions.SetCustomHeaders("Accept-Encoding: gzip, deflate\r\n");

Everytime I call httpOptions.SetCustomHeaders()..., I get the System.AccessViolationException
Does anybody has a clue?
Jan 18, 2013 at 7:40 PM

http://msdn.microsoft.com/en-us/library/windows/desktop/aa964250(v=vs.85).aspx says to call the IBackgroundCopyJob::QueryInterface method with the HttpOptions guid...but I do not see that method defined anywhere...

I would suspect that casting a job to the IBackgroundCopyJobHttpOptions is not the correct way to get a handle to the options...but I don't have a solution yet either...

Jan 21, 2013 at 6:32 AM

As far as I know the cast is the same as

  IBackgroundCopyJob* pJob = NULL;
  IBackgroundCopyJobHttpOptions* pHttpOptions = NULL;

  hr = pJob->QueryInterface(__uuidof(IBackgroundCopyJobHttpOptions), (void**)&pHttpOptions);
I use this code snippet in an extra .dll which I call from my managed code and then it's possible to set the custom header in pHttpOptions without any problems.

 

Jan 22, 2013 at 2:44 PM
Edited Jan 22, 2013 at 4:07 PM

So I put this code inside my BackgroundCopyJob implementation as the managed version of your snippet above:

 

public IBackgroundCopyJobHttpOptions HttpOptions
{
	get
	{
		Guid interfaceGuid = typeof(IBackgroundCopyJobHttpOptions).GUID;
		IntPtr jobPtr = Marshal.GetIUnknownForObject(this.interopJob);
		IntPtr httpOptionsPtr;
		Marshal.QueryInterface(jobPtr, ref interfaceGuid, out httpOptionsPtr);
		return Marshal.GetObjectForIUnknown(httpOptionsPtr) as IBackgroundCopyJobHttpOptions;
	}
}

 

It comes back with a non-null object, and if I query, say, 'httpOptions.GetSecurityFlags(out secFlags)' I get a uint or ulong of zero. But I still get the AccessViolation if I try to set them.

Can you post an example of how you call your unmanaged code snippet from managed?

Also, I am not an expert in COM Interop, but based on the other signature translations and what I have read about how an umanaged ulong is really only four bytes, I was thinking the interface definition you proposed ought to be 

 

void SetSecurityFlags([In] uint Flags);
void GetSecurityFlags(out uint Flags);
Jan 23, 2013 at 7:32 AM
Edited Jan 23, 2013 at 7:33 AM

For setting the custom headers i've implemented a separate c++ dl with unmanaged code:

Header file

 

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
#pragma once

#undef __REQUIRED_RPCNDR_H_VERSION__
#ifndef _DLL_TEST
#define _DLL_TEST
#include <bits3_0.h>
#include <stdio.h>
#include <tchar.h>
#include <lm.h>
#include <iostream>

#if defined DLL_EXPORT
#define DECLDIR __declspec(dllexport)
#else
#define DECLDIR __declspec(dllimport)
#endif

using namespace std;

extern "C"
{
	 DECLDIR bool setCustomHeaders(GUID guidJob, LPCWSTR pwszHeader);
}

#endif

 

Implementation file:

 

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
#define DLL_EXPORT
#include "BITSHELPER.h"

extern "C"
{
	DECLDIR bool setCustomHeaders(GUID guidJob, LPCWSTR pwszHeader)
	{	
		HRESULT hr;
		IBackgroundCopyManager *pQueueMgr;
		IBackgroundCopyJob* pJob = NULL;
		IBackgroundCopyJobHttpOptions* pHttp = NULL;
		// LPCWSTR pwszHeader = L"Authorization: Basic YWxleDphbGV4\r\n";

		// Connect to BITS
		hr = CoCreateInstance(__uuidof(BackgroundCopyManager), NULL, CLSCTX_LOCAL_SERVER, __uuidof(IBackgroundCopyManager), (void **)&pQueueMgr);

		if (FAILED(hr)) // Failed to connect
		{	
			wprintf(L"Failed to connect to BITS.Error: 0x%x\n",hr);
			goto done;
		}

		// Get Job
		wprintf(L"Get Job...\n");
		hr = pQueueMgr->GetJobW(guidJob, &pJob);

		// Free resources
		pQueueMgr->Release();

		if(FAILED(hr))
		{   
			wprintf(L"Get Job failed with error: %x\n",hr);
    		goto done;
		}
    
		// Get the IBackgroundCopyJobHttpOptions interface     
		hr = pJob->QueryInterface(__uuidof(IBackgroundCopyJobHttpOptions), (void **)&pHttp);
	
		if (FAILED(hr))
		{
			wprintf(L"Unable to get IBackgroundCopyJobHttpOptions Interface\n");
		}

		// Set the custom header to given value
		hr = pHttp->SetCustomHeaders(pwszHeader);

		// Release resources
		pHttp->Release();

		if (FAILED(hr))
		{
			//SetCustomHeaders Failed
			wprintf(L"SetCustomHeaders failed with error %x\n",hr);
		}
        return true;
	done:
	failed:
		return false;
	cancel:
		pJob->Cancel();
		pJob->Release();
		goto done;
	}
}

 

These sources are compiled to a native dll. This dll is then referenced from my managed code and I call the dll from my managed code like in this snippet:

 

[DllImport("bitshttpheader.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void setCustomHeaders(Guid guid, [MarshalAs(UnmanagedType.LPWStr)] string pwszHeader);

// snipp some code here.......

            BitsManager manager = new BitsManager();
            manager.EnumJobs(JobOwner.CurrentUser);
            manager.OnJobTransferred += new EventHandler<NotificationEventArgs>(manager_OnJobTransferred);
            manager.OnJobError += new EventHandler<ErrorNotificationEventArgs>(manager_OnJobError);

            BitsJob newJob = manager.CreateJob("TestJob", JobType.UploadReply);
            newJob.AddFile(UploadDesitination + "x.txt", "C:\\x.txt");
            Guid guid = newJob.JobId;

            setCustomHeaders(guid, "MyHeader_2: Header Two Value\r\n");
            newJob.Resume();

 

This code is just a working prove of concept for me and I'm still searching for a 'cleaner' solution.

Jan 23, 2013 at 3:07 PM

Excellent alternative. Thank you much for posting that code.

Jan 24, 2013 at 5:47 AM

In my opinion it's a good workaround, but I'm still searching for a solution without additional DLL. If anybody finds a solution using only managed code, I would be very happy, if it posted here.

Nov 8, 2013 at 10:22 PM
For future reference (since this is 10 months old):

In your C# example you have not implemented the entire COM interface. This means that you are basically calling a different function (according to the vTable index, it would be the GetClientCertificate function called with the signature of SetSecurityFlags...).

Implementing the complete interface would make this work.