Geeks With Blogs
Blog Moved to http://podwysocki.codebetter.com/ Blog Moved to http://podwysocki.codebetter.com/
Update:  Put a comment in if you want the C# version of this code and I will get it to you
 
In previous days, I discussed the methods and properties to be implemented with the BITS wrapper BackgroundCopyJob.  Let's go over some of them today.
 
Let's start off with the IDisposable pattern which must be implemented when using these unmanaged objects.  Here is the simple implementation required:
 
// BackgroundCopyJob finalizer
Podwysocki::Services::BackgroundTransferServices::BackgroundCopyJob::~BackgroundCopyJob()
{
     // Call dispose(false)
     Dispose(false);
} // finalizer- BackgroundCopyJob
 
// Dispose method
void Podwysocki::Services::BackgroundTransferServices::BackgroundCopyJob::Dispose()
{
     // Call dispose(true)
     Dispose(true);
} // method - BackgroundCopyJob::Dispose
 
// Overloaded protected Dispose method
void Podwysocki::Services::BackgroundTransferServices::BackgroundCopyJob::Dispose(bool disposing)
{
     // Check if disposed
     if(!isDisposed)
     {
          // Check if disposing, suppress finalize
          if(disposing)
               GC::SuppressFinalize(this);
  
          // Dispose of copy job
          if(pCopyJob)
               pCopyJob->Release();
  
          isDisposed = true;
     } // if - isDisposed
} // method - BackgroundCopyJob::Dispose(bool)
 
Now that we've looked at implementing the IDisposable pattern properly, let's look at some of the basic methods that we need to implement.  Since this class implements the IDisposable pattern, we need to check in each method and property to check whether the Dispose has been called.  This class could have disasterous results should any method be called after the BITS object has been released.
 
Now let's take a look at adding a file to the job via the IBackgroundCopyJob.AddFile method.  We must also make sure to validate all data coming into this method as well as validate that the instance has not been disposed as well.  Below is the code to add a file:
 
// Adds a file to the copy job
void Podwysocki::Services::BackgroundTransferServices::BackgroundCopyJob::AddFile(String* remoteUrl, String* localName)
{
     // Validate disposed
     ArgumentValidation::CheckForDisposedObject(isDisposed, this->GetType()->Name);
 
     // Validate arguments
     ArgumentValidation::CheckForEmptyOrNullString(remoteUrl, S"remoteUrl");
     ArgumentValidation::CheckForEmptyOrNullString(localName, S"localName");
 
     // Check if type not download and if already a file on record
     if(BackgroundCopyJobType::Download != Type && Files->Count > 0)
          throw new InvalidOperationException(ResourceMessageManager::MultipleFilesUploadExceptionMessage);
 
     // Initialize locals
     HRESULT hr;
 
     // Convert strings
     IntPtr pRemoteUrl = Marshal::StringToCoTaskMemUni(remoteUrl);
     IntPtr pLocalName = Marshal::StringToCoTaskMemUni(localName);
     LPCWSTR pszRemoteUrl= (LPCWSTR)pRemoteUrl.ToPointer();
     LPCWSTR pszLocalName = (LPCWSTR)pLocalName.ToPointer();
 
    try
    {
         // Add file and check for error
         hr = pCopyJob->AddFile(pszRemoteUrl, pszLocalName);
         if(E_INVALIDARG == hr)
              throw new ArgumentException(ResourceMessageManager::UnsupportedProtocolExceptionMessage);
         else if(E_ACCESSDENIED == hr)
              throw new SecurityException(ResourceMessageManager::WriteAccessDeniedExceptionMessage);
         else if(FAILED(hr))
              throw new COMException(ResourceMessageManager::UnknownExceptionMessage(S"IBackgroundCopyJob", S"AddFile", hr), hr);
    } // try
    __finally
    {
         // Free memory
         Marshal::FreeCoTaskMem(pRemoteUrl);
         Marshal::FreeCoTaskMem(pLocalName);
         CoTaskMemFree((void*)pszRemoteUrl);
         CoTaskMemFree((void*)pszLocalName);
    } // finally
} // method - BackgroundCopyJob::AddFile
 
As you can see, we handled all error conditions coming back from the IBackgroundCopyJob interface and made sure that the unmanaged pointers were cleaned before the method finished.
 
Now let's implement the IBackgroundCopyJob3.AddFileWithRanges method.  We must take into account the same things as above.  We must also query the IBackgroundCopyJob interface through IUnknown to get a reference to IBackgroundCopyJob3.  Below is the code to implement the method:
 
// Add a file to a download job and specify the ranges of the file to download
void Podwysocki::Services::BackgroundTransferServices::BackgroundCopyJob::AddFileWithRanges(String* remoteUrl, String* localName, Podwysocki::Services::BackgroundTransferServices::BackgroundCopyFileRange ranges[])
{
     // Validate disposed
     ArgumentValidation::CheckForDisposedObject(isDisposed, this->GetType()->Name);
 
     // Validate arguments
     ArgumentValidation::CheckForEmptyOrNullString(remoteUrl, S"remoteUrl");
     ArgumentValidation::CheckForEmptyOrNullString(localName, S"localName");
     if(ranges->Length == 0)
          throw new ArgumentException(ResourceMessageManager::EmptyRangesExceptionMessage);
 
     // Validate job type
     if(BackgroundCopyJobType::Download != Type)
          throw new NotImplementedException(ResourceMessageManager::DownloadJobOnlyExceptionMessage);
 
     // Initialize locals
     HRESULT hr;
     IBackgroundCopyJob3* pCopyJob3 = NULL;
     int rangeCount = ranges->Length;
     DWORD dwRangeCount = rangeCount;
     IntPtr pRemoteUrl = IntPtr::Zero;
     IntPtr pLocalName = IntPtr::Zero;
     LPCWSTR pszRemoteUrl = NULL;
     LPCWSTR pszLocalName = NULL;
     BG_FILE_RANGE* fileRanges = NULL;
 
     try
     {
          // Convert strings
          pRemoteUrl = Marshal::StringToCoTaskMemUni(remoteUrl);
          pLocalName = Marshal::StringToCoTaskMemUni(localName);
          pszRemoteUrl = (LPCWSTR)pRemoteUrl.ToPointer();
          pszLocalName = (LPCWSTR)pLocalName.ToPointer();
  
          // Create file ranges
          fileRanges = (BG_FILE_RANGE*) malloc(sizeof(BG_FILE_RANGE) * dwRangeCount);
          if(NULL == fileRanges)
               throw new ExternalException(ResourceMessageManager::MallocExceptionMessage(S"BG_FILE_RANGE"));
  
          // Iterate through ranges
          for(int rangeIdx=0; rangeIdx<rangeCount; rangeIdx++)
          { 
               fileRanges[rangeIdx].InitialOffset = ranges[rangeIdx].InitialOffset;
               fileRanges[rangeIdx].Length = ranges[rangeIdx].Length;
          } // for - rangeIdx<rangeCount
  
          // Query interface to get IBackgroundCopyJob3
          hr = pCopyJob->QueryInterface(__uuidof( IBackgroundCopyJob3 ), (void**)&pCopyJob3);
          if(E_NOINTERFACE == hr)
               throw new NotSupportedException(ResourceMessageManager::BitsVersionNotSupportedExceptionMessage(S"2.0"));
          else if(FAILED(hr))
               throw new COMException(ResourceMessageManager::QueryInterfaceExceptionMessage(S"IBackgroundCopyJob3"), hr);
  
          // Release pCopyJob to decrement count
          pCopyJob->Release();
  
          // Call AddFileWithRanges
          hr = pCopyJob3->AddFileWithRanges(pszRemoteUrl, pszLocalName, dwRangeCount, fileRanges);
          if(E_ACCESSDENIED == hr)
               throw new SecurityException(ResourceMessageManager::WriteAccessDeniedExceptionMessage);
          else if(BG_E_INVALID_RANGE == hr)
               throw new ArgumentException(ResourceMessageManager::InvalidRangeExceptionMessage);
          else if(BG_E_OVERLAPPING_RANGES == hr)
               throw new ArgumentException(ResourceMessageManager::OverlappingRangeExceptionMessage);
          else if(E_INVALIDARG == hr)
               throw new ArgumentException(ResourceMessageManager::UnsupportedProtocolExceptionMessage);
          else if(FAILED(hr))
               throw new COMException(ResourceMessageManager::UnknownExceptionMessage(S"IBackgroundCopyJob3", S"AddFileWithRanges", hr), hr);
     } // try
     __finally
     {
          // Release pCopyJob3
          if(pCopyJob3)
               pCopyJob3->Release();
  
          // Free memory
          Marshal::FreeCoTaskMem(pRemoteUrl);
          Marshal::FreeCoTaskMem(pLocalName);
          CoTaskMemFree((void*)pszRemoteUrl);
          CoTaskMemFree((void*)pszLocalName);
  
          // Free ranges
          free(fileRanges);
     } // finally
} // method - BackgroundCopyJob::AddFileWithRanges
 
As you see, when doing mixed mode coding in Managed C++, you can see how careful we need to be with our unmanaged types.  We had to do malloc and free of an array as well.  Be careful to pay attention to the API for BITS, as it tells you which objects you must free using a particular method.  It's always interesting in the COM world to know what to free and how to free it.
 
Lastly today, we will cover setting the priority of the BackgroundCopyJob through the IBackgroundCopyJob.SetPriority method.  This is a rather straight forward method that validates the arguments as always and sets the priority.  Below is the implementation:
 
// Sets the BackgroundCopyJob priority
void Podwysocki::Services::BackgroundTransferServices::BackgroundCopyJob::set_Priority(Podwysocki::Services::BackgroundTransferServices::BackgroundCopyJobPriority value)
{
     // Validate disposed
     ArgumentValidation::CheckForDisposedObject(isDisposed, this->GetType()->Name);
 
     // Validate arguments
     ArgumentValidation::CheckForInvalidEnum(__box(value)->GetType(), __box(value), S"value");
 
     // Check for state
     if(BackgroundCopyJobState::Acknowledged == State || BackgroundCopyJobState::Cancelled == State)
          throw new InvalidOperationException(ResourceMessageManager::AcknowledgedCancelledStateExceptionMessage);
 
     // Initialize locals
     HRESULT hr;
     BG_JOB_PRIORITY bgJobPriority = (BG_JOB_PRIORITY)value;
 
     // Set priority
     hr = pCopyJob->SetPriority(bgJobPriority);
     if(FAILED(hr))
          throw new COMException(ResourceMessageManager::UnknownExceptionMessage(S"IBackgroundCopyJob", S"SetPriority", hr), hr);
} // method - BackgroundCopyJob::set_Priority
 
So, as you can see, it takes a bit of work to do the translation and to be type safe while doing so.  The great thing about Managed C++ is that you can mix and match your existing C++ code with Managed CLR code seamlessly.  Many times this can be used to prevent having to reinvent the wheel with some legacy code.  I'm providing these examples of how you can move any type of existing code to Managed C++ and beyond with some work.
Posted on Friday, May 26, 2006 12:01 PM Microsoft , Background Intelligent Transfer Service , .NET | Back to top


Comments on this post: Day 7 of the Background Intelligent Transfer Service (BITS) Managed Wrapper

# re: Day 7 of the Background Intelligent Transfer Service (BITS) Managed Wrapper
Requesting Gravatar...
Hi,
I have checked your tutorial. Can u provide be how to specify the remote url for the addfile method. I was hanged up there in my developement
Left by Naresh Thondepu on Aug 26, 2010 12:06 AM

# re: Day 7 of the Background Intelligent Transfer Service (BITS) Managed Wrapper
Requesting Gravatar...

I trying to upload file to remote machine. It works for Windows 2008 and windows 2003 but it failed with E_ACCESSDENIED for Windows XP and Windows 7. Is there any pre-requisties should do in client before transferring files to client?. Will SetCredential() help in this?

Thanks in advance

-ilavaa
Left by ilavaa on Dec 14, 2012 4:08 AM

Your comment:
 (will show your gravatar)


Copyright © Matthew Podwysocki | Powered by: GeeksWithBlogs.net