2

My boss wants me to write a DLL in C++ (MSVC++2010) which can perform a Volume Shadow Copy which he can call from VB6 (or at a later point other languages) and which can give status updates back while it is not finished. He calls it "events".

I have the feeling that I finally need to learn COM (I'd rather not ...) ... also, a callback function comes to my mind, but it's surely impossible to hand over function pointers from VB6 to C++?

Can someone outline what I have to learn and how this can be accomplished, with or without COM?

EDIT: to answer a question, the work flow is supposed to be:

  1. VB6 app determines which files to back up

  2. I am given a path and make a temporary volume shadow copy which includes this path and give back a mount point (or similar)

    • during this step, I regularly tell the VB6 app how far I am

  3. VB6 app makes backup of shadow copy and then deletes shadow copy.

Felix Dombek
  • 13,664
  • 17
  • 79
  • 131

6 Answers6

5

You can pass a pointer to your "display progress" function from the VB app to the C++ DLL app using the AddressOf operator:

Declare Function CallMyDll ...

Sub DisplayStatus(ByVal SomeParameter As Long)
    ' ...
End SUb

Sub DoSomething()
    Call CallMyDll(AddressOf DisplayStatus)
End Sub

Some not so obvious gotchas:

  1. You have to declare your C++ function pointer using the __stdcall calling convention. (Thanks, Alexandre C!)

  2. In your VB callback function, explicitly mark your parameters as by-value using the keyword ByVal. Similarly, in your C++ function pointer, don't mark your parameters as by-reference.

  3. If you want to pass a string to the callback, or retrieve a string from it, you have to take into consideration that VB Strings are not equal to C char*s, C++ std::strings, or Microsoft's CStrings. VB Strings must be mapped to Microsoft's rather obscure BSTR data type.

  4. I forgot a very important thing: Your callback has to be inside a VB Module (i.e., it has to be a "mere function", not a class' or a form's method).

isekaijin
  • 19,076
  • 18
  • 85
  • 153
  • It works in VBA too. Beware, convention is usually `stdcall` (it is in VBA) so you have to specify it explicitly in your C code. BSTR types can be handled with `CComBstr`, `std::wstring` and some magic code you write once in your life. – Alexandre C. Mar 17 '11 at 17:22
  • Thanks! Actually, retrieving a string is not a problem: the BSTR also points to a zero-terminated C string. Just the other way doesn't work, because `BSTR`'s contain the length at `BSTR-1`, afaik. And you are right with `__stdcall`. – Felix Dombek Mar 17 '11 at 17:29
  • @Felix: the converse is done with `SysAllocString`. Nothing difficult if you let VB free the string. – Alexandre C. Mar 17 '11 at 17:36
  • 1
    @Alexandre C: I like C++, I like OOP, I *love* templates, but dealing with VB puts me in hack mode. – isekaijin Mar 17 '11 at 17:37
  • yeah, at my job we are required to do "functional programming" with VB too. This sucks. – Alexandre C. Mar 18 '11 at 09:56
2

Your boss can call functions exported by a DLL with the Declare statement. That doesn't scale well but is fine for a simple API. Your function should be exported with the extern "C" and __declspec(dllexport) declarators, use the __stdcall calling convention and use only simple argument types.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
1

When it has to provide these status updates?
While setting up VSS? Or while backing up the data and such? In the latter case VSS just returns a path, which can be used directly from VB.
But for setup... it might make sense too, because it can be fairly slow, but I guess you can turn it into a state machine - put all the VSS API calls into a big switch() and make a function which would call them one by one and update the state var.

Update: I mean something like this. Init() and Step() are your functions exported by your dll and called from VB.
Alternatively, you can spawn a thread to do all that (still in the dll) and return status updates after like Sleep(100) in Step().

int s; // state

int Init( void ) { s=0; }

int Step( void ) {

  switch( s ) {

    default: break;

    case 0: 
    CoInitialize(0); break;

    case 1: 
    r = CreateVssBackupComponents(&vssc); 
    if( FAILED(r) ) s=-1;
    if( vssc==0 ) s=-2;
    break;

    case 2: 
    r = vssc->InitializeForBackup();
    if( FAILED(r) ) s=-3; 
    break;

    case 3: 
    r = vssc->SetBackupState( FALSE, FALSE, VSS_BT_COPY, FALSE );
    if( FAILED(r) ) s=-4;
    break;

    [...]

  }

  s += (s>=0);
}
Shelwien
  • 2,160
  • 15
  • 17
  • You mean, VB6 has a thread which checks in regular intervals if a variable has changed that I have been given by reference? – Felix Dombek Mar 17 '11 at 15:43
  • That's also one of the solutions I'll suggest to him! Special thanks for the detailed procedure, you have obviously worked with volume shadow copies before! I have looked over the Windows SDK sample (VSHADOW.EXE 3.0) which is quite hard stuff for a beginner – Felix Dombek Mar 17 '11 at 17:45
1

I would do the job without COM. Instead, I'd have the VB part send a window handle to the DLL, and the DLL will post a message to the window telling its progress.

You could use COM, but that's kind of in the range of swatting a fly with a sledgehammer.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
1

Take the unix-y route. Create a PROGRAM that performs the copy and outputs a progress indicator to std out. Make the VB app parse this output to grab the completion percentage.

KitsuneYMG
  • 12,753
  • 4
  • 37
  • 58
1

I would do this:

  • the VB app calls the function in your DLL asking to start the shadow copy. Your Dll starts a thread that performs the shadow copy and returns an ID (a thread id?) back to the VB app

  • The VB app calls periodically a function "Progress" in your DLL passing the operation ID received before: the progress function returns an integer 0-100 to indicate the progress

  • This way the VB app can launch several operations in parallel.

The thread that performs the copy should update a "progress" variable every now and then. Another function in the DLL that stops the copy would be useful too.

Paolo Brandoli
  • 4,681
  • 26
  • 38
  • This is quite similar to the idea I had in mind after @Shelwien's answer (before his clarification). There is a possibility that this will be the way I choose. It depends upon whether it is necessary to launch several backups concurrently, but I doubt that. Still, it's a very nice and clean option which I will definitely consider. – Felix Dombek Mar 17 '11 at 23:55