update progress bar while copying

Hi all,

I am using the copyFile function to copy some files. the location is decided upon by the user via a form. However becuase the file to copy can be large then it looks like the form has frozen up when it is still copying. I have worked out how to use the progress bar and timer and want to update the progress bar during the copy. However the copy seems to have priority and the progress bar doesnt update. Can anyone help? Do I have to set up a separate thread for the progress bar? Can i do what I want? I ahve looked at FileCopyEx but cant get this working either so I thought this way would be a good workaroudn

Thanks in advance
Richard
richjo100Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

AlexFMCommented:
Use SHFileOperation Function. It shows progress dialog like Windows Explorer. It is used internally by Windows Explorer to copy files.
0
AlexFMCommented:
CopyFileEx should work also, but it requires more code for progress indication. If you have problems using CopyFileEx or SHFileOperation, post your code here.
0
richjo100Author Commented:
Hi AlexFM,
Thanks for the quick reply. I would rather use copyfileEx if possible so here is my code. At the moment it copies but doesnt fall out of the loop. The code is

function.............

 BOOL b = false;
 return  CopyFileEx(source, fileNameToWrite, (LPPROGRESS_ROUTINE) CopyProgressRoutine, 0,  &b, COPY_FILE_FAIL_IF_EXISTS);      
}
      
public: static DWORD CALLBACK CopyProgressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred,  LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber,        DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData){
      int str = 999;
      switch (dwCallbackReason){
             case (CALLBACK_STREAM_SWITCH):
                  str  = PROGRESS_CONTINUE;
                  break;    
            case (CALLBACK_CHUNK_FINISHED):
                  updateProgressBar(updateVal += 2)
                  str=  PROGRESS_CONTINUE;
                  break;
            default:
                  str = PROGRESS_CONTINUE;
            break;
      }    
            return str;
    }


=====================================


Ideally I want the updateProgressBar function to be called. However because CopyProgressRoutine has to be static then so does this function. However when I construct the form I do this in form1.cpp and the code above is in form1.h therefore I cannot work out how to call the updateProgressBar function as the class is constructed in form1.cpp and then all the processing happens in form.h

However even if I dont include the updateProgressBar function then the file copies and then never jumps out the loop. I have tried looking for examples but cant find anything.

Any help would be appreciated
Richard

0
Cloud Class® Course: CompTIA Healthcare IT Tech

This course will help prep you to earn the CompTIA Healthcare IT Technician certification showing that you have the knowledge and skills needed to succeed in installing, managing, and troubleshooting IT systems in medical and clinical settings.

AlexFMCommented:
LPVOID lpData member may be used to pass class pointer. Using this pointer you have access to the class instance.

return  CopyFileEx(source, fileNameToWrite, (LPPROGRESS_ROUTINE) CopyProgressRoutine, this,  &b, COPY_FILE_FAIL_IF_EXISTS);  

static DWORD CALLBACK CopyProgressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred,  LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber,       DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData)
{  
    CMyClass* pClass = (CMyClass*)lpData;

   pClass->...;
}

To stop copying return PROGRESS_CANCEL.
0
richjo100Author Commented:
Thanks for your reply. I have added the following code to the CopyProgress...

Form1* pClass = (Form1*)lpData;

and I get the following error: (where XSend is the name of the project)
error C2440: 'type cast' : cannot convert from 'LPVOID' to 'XSend::Form1 __gc *'

I think the problem may stem from the fact that I created a form in visual c++ and then just added buttons and double cliked on them to write the code. Therefore I presume the only class I have produced is the class to do with the form whch is called form1. Is my understanding correct?

Thanks again
Richard
0
AlexFMCommented:
I didn't know that you are using managed extensions. Managed pointer cannot be casted to unmanaged pointer. However, you can use some trick: create unmanaged structure which contains Form1* reference and pass this pointer to this structure to CopyFileEx.
Or use SHFileOperation, it is simple for use.
0
richjo100Author Commented:
Hi AlexFm
I think I will use SHFileOperation then if it is easier. Thanks for your help so far. i'll let you know how I get on with SHFileOperation
Richard
0
AlexFMCommented:
Don't miss the following lines from SHFILEOPSTRUCT Structure MSDN topic:

pFrom
Although this member is declared as a null-terminated string, it is used as a buffer to hold multiple file names. Each file name must be terminated by a single NULL character. An additional NULL character must be appended to the end of the final name to indicate the end of pFrom.

The same is about pTo.
0
AlexFMCommented:
BTW, possibly there is pure managed way to make such operation. Ask this in .NET or C# area. Any C# or VB.NET code may be easily translated to managed C++.
0
richjo100Author Commented:
Thanks AlexFM. Ideally I want to increase the value of the progress bar during the copy. I was thinking of have a separate thread to do this. i will look in to it
Richard
0
richjo100Author Commented:
Hi AlexFM,

Am having some problems with using SHFileOperation. I have included shell32.lib but everytime I try and run any code with #include <shellapi.h> and I get loads of syntax errors. What is going on? Do I have to over-ride some function to get it to work?

Thanks
Richard
0
AlexFMCommented:
Create Windows Forms application with ShellTest name and paste this code to Form1.h file:

#pragma once

#include <windows.h>
#include <shellapi.h>

namespace ShellTest
{
    using namespace System;
    using namespace System::ComponentModel;
    using namespace System::Collections;
    using namespace System::Windows::Forms;
    using namespace System::Data;
    using namespace System::Drawing;
    using namespace System::Runtime::InteropServices;

    public __gc class Form1 : public System::Windows::Forms::Form
    {  
        public:
            Form1(void)
            {
                InitializeComponent();
            }

        protected:
            void Dispose(Boolean disposing)
            {
                if (disposing && components)
                    {
                    components->Dispose();
                    }
                __super::Dispose(disposing);
            }
        private: System::Windows::Forms::Button *  button1;

        private:
            /// <summary>
            /// Required designer variable.
            /// </summary>
            System::ComponentModel::Container * components;

            /// <summary>
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            /// </summary>
            void InitializeComponent(void)
                {
                this->button1 = new System::Windows::Forms::Button();
                this->SuspendLayout();
                //
                // button1
                //
                this->button1->Location = System::Drawing::Point(80, 96);
                this->button1->Name = S"button1";
                this->button1->Size = System::Drawing::Size(112, 24);
                this->button1->TabIndex = 0;
                this->button1->Text = S"button1";
                this->button1->Click += new System::EventHandler(this, button1_Click);
                //
                // Form1
                //
                this->AutoScaleBaseSize = System::Drawing::Size(5, 13);
                this->ClientSize = System::Drawing::Size(292, 266);
                this->Controls->Add(this->button1);
                this->Name = S"Form1";
                this->Text = S"Form1";
                this->ResumeLayout(false);

                }  
        private: System::Void button1_Click(System::Object *  sender, System::EventArgs *  e)
                 {
                     ArrayList* list = new ArrayList();

                     list->Add(new String(L"C:\\tmp\\file1.txt"));
                     list->Add(new String(L"C:\\tmp\\file2.txt"));

                     CopyFiles(list, L"C:\\Tmp1");
                 }

        void CopyFiles(ArrayList* list, String* dest)
        {
            int i, len, bufferLen;
            SHFILEOPSTRUCTW s;
            IntPtr pFrom, pTo;

            s.hwnd = (HWND)this->Handle.ToInt32();
            s.wFunc = FO_COPY;

            len = list->Count;
            bufferLen = 2;

            for ( i = 0; i < len; i++ )
            {
                String* str = dynamic_cast<String*>(list->Item[i]);

                if ( str )
                    bufferLen += (str->Length + 1);
            }

            bufferLen *= 2;
               
            pFrom = Marshal::AllocCoTaskMem(bufferLen);

            char* pCurrent = (char*) pFrom.ToInt32();

            for ( i = 0; i < len; i++ )
            {
                String* str = dynamic_cast<String*>(list->Item[i]);

                if ( str )
                {
                    IntPtr tmp = Marshal::StringToCoTaskMemUni(str);
                    int l = (str->Length + 1)*2;
                    memcpy(pCurrent, (void*)tmp.ToInt32(), l);
                    Marshal::FreeCoTaskMem(tmp);
                    pCurrent += l;

                }
            }

            *pCurrent++ = 0;
            *pCurrent = 0;

            s.pFrom = (LPCWSTR)pFrom.ToInt32();


            {
                int l = (dest->Length + 2)*2;
                pTo = Marshal::AllocCoTaskMem(l);

                char* pCurrent = (char*) pTo.ToInt32();

                l -= 2;
                IntPtr tmp = Marshal::StringToCoTaskMemUni(dest);
                memcpy(pCurrent, (void*)tmp.ToInt32(), l);
                Marshal::FreeCoTaskMem(tmp);
                pCurrent += l;

                *pCurrent++ = 0;
                *pCurrent = 0;

                s.pTo = (LPCWSTR)pTo.ToInt32();
            }

            s.fFlags = 0;
            s.hNameMappings = NULL;
            s.lpszProgressTitle = NULL;

            SHFileOperationW(&s);


            Marshal::FreeCoTaskMem(pFrom);
            Marshal::FreeCoTaskMem(pTo);
        }

    };
}

You can use CopyFiles function in your program. button1_Click shows how to use this function. This program copies files C:\tmp\file1.txt and C:\tmp\file2.txt to directory C:\Tmp1.

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual C++.NET

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.