views:

59

answers:

1

Hi, I'm tearing my hair out trying to solve this one, any insights will be much appreciated:

I have a C++ exe which acquires data from some hardware in the main thread and processes it in another thread (thread 2).

I use a c++ dll to supply some data processing functions which are called from thread 2.

I have a requirement to make another set of data processing functions in VB6. I have thus created a VB6 dll, using the add-in vbAdvance to create a standard dll.

When I call functions from within this VB6 dll from the main thread, everything works exactly as expected.

When I call functions from this VB6 dll in thread 2, I get an access violation.

I've traced the error to the CopyMemory command, it would seem that if this is used within the call from the main thread, it's fine but in a call from the process thread, it causes an exception. Why should this be so? As far as I understand, threads share the same address space.

Here is the code from my VB dll

Public Sub UserFunInterface(ByVal in1ptr As Long, ByVal out1ptr As Long, ByRef nsamples As Long)
Dim myarray1() As Single
Dim myarray2() As Single
Dim i As Integer
ReDim myarray1(0 To nsamples - 1) As Single
ReDim myarray2(0 To nsamples - 1) As Single


With tsa1din(0)  ' defined as safearray1d in a global definitions module
    .cDims = 1
    .cbElements = 4
    .cElements = nsamples
    .pvData = in1ptr
End With

With tsa1dout
    .cDims = 1
    .cbElements = 4
    .cElements = nsamples
    .pvData = out1ptr
End With
CopyMemory ByVal VarPtrArray(myarray1), VarPtr(tsa1din(0)), 4
CopyMemory ByVal VarPtrArray(myarray2), VarPtr(tsa1dout), 4

For i = 0 To nsamples - 1
    myarray2(i) = myarray1(i) * 2
Next i
ZeroMemory ByVal VarPtrArray(myarray1), 4
ZeroMemory ByVal VarPtrArray(myarray2), 4


End Sub

UPDATE:-

Thanks for all your comments, I have actually found a work around that does what I need it to do. I've put all the pointer manipulations for the arrays into a seperate function in the VB dll, I call this function from the main thread of the C++ app during the setup routines. The processing thread then calls another routine in the dll which contains only the calculations to be performed on the array data (and no CopyMemory commands). This appears to work.

+1  A: 

You have to initialize COM on second thread and initialize VB6 run-time before calling the VB6 DLL export. So, first call CoInitilize on the worker thread, second create a dummy object from VB6 ActiveX DLL (abd release it immediately), then call the function.

You can do both of these steps inside your export but it's much trickier. You can not call intrinsic VB function, nor declare'd API functions, only typelib imported ones. My thread entry function start like this:

'--- initialize COM libs
Call CoInitialize(0)
'--- create a VB6 object
Call CoCreateInstance(CLSIDFromProgID(PROGID_DUMMY), Nothing, CLSCTX_INPROC_SERVER, VBGUIDFromString(STR_IID_IUnknown), Nothing)
On Error GoTo ...

where all of the API functions are from a threading typelib. Here is a function which can tell you if VB runtime is up on current thread

Private Function pvIsVbRuntime() As Boolean
    Dim lIdx            As Long

    lIdx = GetModuleHandle("MSVBVM60.DLL")
    lIdx = GetProcAddress(lIdx, "__vbaSetSystemError")
    Call RtlMoveMemory(lIdx, ByVal lIdx + 9, 4)
    Call RtlMoveMemory(lIdx, ByVal lIdx, 4)
    If TlsGetValue(lIdx) <> 0 Then
        pvIsVbRuntime = True
    End If
End Function

All API function imported from typelib too. It's hacky and I've tested it on SP6 only.

Anyway, I think your sort of problems should be well documented with the vbAdvance add-in. You'd better "prepare" worker thread in C++ code.

wqw
Thanks for your time in responding, I will try and implement this. Unfortunately the vbAdvance documentation doesn't cover any of this.
Roshan