-
Feb 26th, 2019, 05:34 AM
#1
Thread Starter
New Member
[RESOLVED] Trying to Figure Out How to Communicate with a C DLL
I've been writing code in C and C++ for many years (since the 80s), but I have little experience with VB. For the past few years I've been maintaining a suite of programs most written in C back in the late 80s with a VB6 program written in the early 90s. The programs were updated for Windows 95 and again around 2006, but are essentially the same at their core.
The VB program has been communicating with the C programs via DDE and the C programs communicate with one another with Windows messages and a DLL with a block of shared memory.
I've been working on a new way to communicate between the VB6 program and the C programs but VB6 is a lot harder to debug than C. I created a logging function in the DLL to see if things are getting called and if the arguments are getting passed correctly (I can't figure out how to step into a DLL like you can when debugging C/C++).
All apps that use the DLL need to call an init call when the program is initializing. From the log I can see the initialization call is happening. However the call that is supposed to transfer data via the DLL is getting weird data.
The function has three arguments, and integer which is supposed to be between 1 and 10, a string pointer, and another integer. The first integer is 3, and the last is 0. But the log shows the first integer as 12517379. The last argument is 0. The string also comes through as three garbage characters: T{¿
There is obviously something off with string, but the integer came through weird too. I converted it to hex and it's: BF0003(H) which might be a valid memory address, but doesn't look typical. Addresses are usually at least divisible by 2, and usually some higher power of 2 (8, 16, or 32).
At the moment I'm just trying to get communication from the VB program to the C programs, but the VB program needs to be able to retrieve blocks of text data, sometimes as much as 16K, from the C programs as well as be able to receive messages from the C programs. Is there a way for VB6 programs to receive messages from a DLL or a C program? If so, how?
Obviously I'm doing something wrong, but I'm baffled. I have a feeling I'm going to have a lot of questions before I'm done.
Thanks in advance...
-
Feb 26th, 2019, 06:11 AM
#2
Re: Trying to Figure Out How to Communicate with a C DLL
Can you show us how you've declared that c-function in VB6?
The common pitfall are ByVal/ByRef-Parameters
Last edited by Zvoni; Tomorrow at 31:69 PM.
----------------------------------------------------------------------------------------
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------------------
People call me crazy because i'm jumping out of perfectly fine airplanes.
---------------------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad
-
Feb 26th, 2019, 06:32 AM
#3
Re: Trying to Figure Out How to Communicate with a C DLL
You can just create a shared COM object and communicate between 2 programs without any additional DLLs - COM will make all work to ensure the synchronous calls from your VB app to the C++/C app and vice versa. The advantages are you can transparently communicate between 32/64 bit apps, you can place second app to other machine, you can use the object from any application and scripts.
From VB and C/C++ it looks like just call a method.
Last edited by The trick; Feb 26th, 2019 at 10:35 AM.
-
Feb 26th, 2019, 07:01 AM
#4
Re: Trying to Figure Out How to Communicate with a C DLL
You might want to consider DispCallFunc API which can call a variety of DLL conventions, including C_DECL. You can find many examples of that API on this site.
-
Feb 26th, 2019, 08:46 AM
#5
Re: Trying to Figure Out How to Communicate with a C DLL
You might want to start by posting some C code and the VB code that is calling it. As already pointed out - the most obvious issues are calling convention, and the VB declaration.
-
Feb 26th, 2019, 09:19 AM
#6
Re: Trying to Figure Out How to Communicate with a C DLL
Last edited by dz32; Apr 26th, 2019 at 11:07 AM.
-
Feb 26th, 2019, 08:31 PM
#7
Thread Starter
New Member
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by Zvoni
Can you show us how you've declared that c-function in VB6?
The common pitfall are ByVal/ByRef-Parameters
On the VB side I did have ByRef for the string and when I changed it to ByVal the string came through. The first integer is still wrong though. The decalaration in VB looks like:
Private Declare Function ttw_SendToApp Lib "TTWLib32" (ByVal xfertype As Integer, ByVal CommandStr As String, ByVal bExpectReturn As Integer) As Integer
On the DLL side it is declared:
int WinApi ttw_SendToApp(int type, LPSTR cmd, int bExpectReturn);
I'm thinking about the return data. It can be a large block of data sometimes. On the C side it's stored in a handle that gets transferred in some way by the DDE system currently. If I used a ByRef in the return would I return the handle, or a pointer to the data in the locked handle and how would I get at the data on the VB side?
-
Feb 26th, 2019, 09:14 PM
#8
Thread Starter
New Member
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by dz32
if the vb code is in a dll and the c code is in a dll, build an vb exe test harness for the functions so you can test the transfer mechanism.
you can debug the c dll code in your standard c IDE by setting it to launch the vb exe on start of debug session, compile your vb exe with debug symbols you will actyallu be able to step into the vb code from teh C dll to watch it. you will also be able to set breakpoints in your C DLL normally it works perfectly, I might have a video on this somewhere I will look.
There are several good ways to communicate across the two languages. The one i use the most if the WM_COPYDATA message which gives you like 1024 bytes per message possible. You can chunk messages but if you need a bigger buffer it might be easier to use another method. example: http://sandsprite.com/openSource.php?id=70
If you are building a list or have to keep adding data you can also use a callback: http://sandsprite.com/openSource.php?id=53
shared memory is not a bad way to go, even if you use a virtual alloc to hold it temporarily and just return a memory pointer and size from C then call virtualFree from vb.
just using sendmessage itself can be enough if you can living with its user parameters and one long return value.
to start though I would break out the declare statements and C stubs into a test project so you take all the complexity out and can watch the data transfers explicitly in the debugger. Its probably just a mismatch from expecting a pointer type and a byref or byval mismatch between the vb declare and the C code.
some more misc info: http://sandsprite.com/CodeStuff/Why_...l_Use_VB6/?p=4
The VB program is an EXE. I didn't know you could make VB DLLs. I found where I could compile with symbols in VB and did run it in the debugger. I figured that one out, VB defines an integer as a 16 bit variable and 32 bit C defines it as a 32 bit variable. When I changed the call to expect an int16, it worked.
I tried going to the sandsprite.com links you sent, but my virus checker thinks that's a trojan site. It's probably a false positive, I was able to get there with Tor and downloaded the zips. They scanned fine. It looks like there may be some useful information there. I'll digest it this evening. Thanks a bunch.
-
Feb 26th, 2019, 09:28 PM
#9
Thread Starter
New Member
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by The trick
You can just create a shared COM object and communicate between 2 programs without any additional DLLs - COM will make all work to ensure the synchronous calls from your VB app to the C++/C app and vice versa. The advantages are you can transparently communicate between 32/64 bit apps, you can place second app to other machine, you can use the object from any application and scripts.
From VB and C/C++ it looks like just call a method.
I'm not creating any new DLLs, I'm just expanding on an existing DLL that is integral to the suite of programs. I think it pretty much does what a COM object would do now.
Originally Posted by LaVolpe
You might want to consider DispCallFunc API which can call a variety of DLL conventions, including C_DECL. You can find many examples of that API on this site.
I'll check that out.
Thanks
-
Feb 27th, 2019, 01:31 AM
#10
Re: Trying to Figure Out How to Communicate with a C DLL
Integer <> int, Integer = short.
Code:
Private Declare Function ttw_SendToApp Lib "TTWLib32" (ByVal xfertype As Long, ByVal CommandStr As String, ByVal bExpectReturn As Long) As Long
-
Feb 27th, 2019, 12:31 PM
#11
Re: Trying to Figure Out How to Communicate with a C DLL
Trick already caught the wrong types being used. But you should also be aware of VB's automatic
Unicode<-->ANSI conversions when using string parameters.
-
Feb 27th, 2019, 12:47 PM
#12
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by DEXWERX
But you should also be aware of VB's automatic
Unicode<-->ANSI conversions when using string parameters.
Should be ok in that respect. His DLL declared the parameter as LPSTR vs LPWSTR
If _cdecl is in play here; isn't there a risk of the stack not being cleaned up properly, even after the params issue is resolved?
-
Feb 27th, 2019, 02:12 PM
#13
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by LaVolpe
Should be ok in that respect. His DLL declared the parameter as LPSTR vs LPWSTR
If _cdecl is in play here; isn't there a risk of the stack not being cleaned up properly, even after the params issue is resolved?
yes but he's showing a WINAPI/STDCALL calling convention on the C function... although he's got it spelled with an odd case, maybe typo?
-
Feb 27th, 2019, 07:46 PM
#14
Thread Starter
New Member
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by DEXWERX
yes but he's showing a WINAPI/STDCALL calling convention on the C function... although he's got it spelled with an odd case, maybe typo?
Yes, it's a typo. In my code I actually use a #define instead of WINAPI. I copied, pasted, and made the change by hand. Some of the modules from the DLL are directly linked into some stand alone utilities used with the system, and conditionally at compile time the WINAPI is left out in that instance.
Interesting about the Unicode/ANSI conversion. The DLL and the old programs are ASCII, in fact they predate the standard C library, but the new C++ program that will replace them is Unicode. It's something to keep in mind as I'm working with these functions.
Edit: I do have an odd behavior when I post a reply. I click the Post button once, but then get an error saying I can't post more than 1 post per 30 seconds. When I look at the thread, my post went through. It seems to be trying to post twice in quick succession. I'm using Firefox on Windows 7 BTW.
-
Feb 27th, 2019, 07:48 PM
#15
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by DEXWERX
yes but he's showing a WINAPI/STDCALL calling convention on the C function...
Thanx, I figured that out later. Not fluent with that programming language, so did some quick research and saw that WINAPI is a macro for stdCall
-
Feb 27th, 2019, 08:36 PM
#16
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by wdolson
I do have an odd behavior when I post a reply. I click the Post button once, but then get an error saying I can't post more than 1 post per 30 seconds. When I look at the thread, my post went through. It seems to be trying to post twice in quick succession. I'm using Firefox on Windows 7 BTW.
Happening with all of us -- been happening for several months now. Many people that don't visit here often don't realize that it's a "bug" of sorts, wait a few seconds, and then click to try again which results in double posts.
-
Feb 27th, 2019, 08:47 PM
#17
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by LaVolpe
Happening with all of us -- been happening for several months now. Many people that don't visit here often don't realize that it's a "bug" of sorts, wait a few seconds, and then click to try again which results in double posts.
If you click on Reply, and then click on "Go Advance" below the reply window, then you can reply and avoid that bug, but it is usually quicker to ignore the error.
-
Mar 1st, 2019, 05:01 AM
#18
Thread Starter
New Member
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by passel
If you click on Reply, and then click on "Go Advance" below the reply window, then you can reply and avoid that bug, but it is usually quicker to ignore the error.
Good to know it isn't just me.
I do have another question. For transferring data back, it looks like I need to use SAFEARRAY such as with this example:
https://www.codeproject.com/Articles...m-VC-DLL-to-VB
All the examples I can find have the array defined on the VB side. Is that required? The data being returned from the DLL is variable in size. For example one thing returned is a list of files of a certain type in a directory. That might be 0 or it could be hundreds. I suppose I could handshake back and forth to figure out how many items are in the array coming back, allocate the space on the VB side, then transfer it, but that seems awkward. The data is transferred with DDE now and the received data is just put into a control on the hidden DDE form on the VB side, then used by the application.
I think I can just reuse the controls on the DDE form without actually calling DDE on the VB side. The way things are now, I don't see any array sizes defined, it just gets whatever it gets and plunks it in the control.
-
Mar 1st, 2019, 05:39 AM
#19
Re: Trying to Figure Out How to Communicate with a C DLL
Last edited by dz32; Apr 26th, 2019 at 11:06 AM.
-
Mar 1st, 2019, 07:10 AM
#20
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by wdolson
I suppose I could handshake back and forth to figure out how many items are in the array coming back, allocate the space on the VB side, then transfer it, but that seems awkward.
Many windows functions do that: Send a null data-length parameter to return size of data, then size the array, and call again with both the array pointer and the non-null data length parameter.
Another option could be to use the same structure that WM_COPYDATA uses. That structure contains the size of the data and a pointer to the data. However, the VB side would need to copy that data to a local array via pointer/RtlMoveMemory or other methods. Your function would return a pointer to the structure.
-
Mar 1st, 2019, 11:19 AM
#21
Re: Trying to Figure Out How to Communicate with a C DLL
You can just return a stream (SHCreateMemStream/CreateStreamOnHGlobal).
-
Mar 1st, 2019, 12:31 PM
#22
Fanatic Member
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by wdolson
Good to know it isn't just me.
I do have another question. For transferring data back, it looks like I need to use SAFEARRAY such as with this example:
https://www.codeproject.com/Articles...m-VC-DLL-to-VB
All the examples I can find have the array defined on the VB side. Is that required? The data being returned from the DLL is variable in size. For example one thing returned is a list of files of a certain type in a directory. That might be 0 or it could be hundreds. I suppose I could handshake back and forth to figure out how many items are in the array coming back, allocate the space on the VB side, then transfer it, but that seems awkward. The data is transferred with DDE now and the received data is just put into a control on the hidden DDE form on the VB side, then used by the application.
I think I can just reuse the controls on the DDE form without actually calling DDE on the VB side. The way things are now, I don't see any array sizes defined, it just gets whatever it gets and plunks it in the control.
I think you could call SafeArrayRedim() from the C DLL to expand the SAFEARRAY, fill it with info, then return, so it's one call that way.
Another one call option is to use BSTR, with NULL's in the middle to separate the list of files. Call SysReAllocStringLen() from the C DLL to resize the VB string. In the VB side, you have to declare the parameter "As Long", and pass "VarPtr(StrVar)". This passes BSTR to the DLL. Using "StrPtr(StrVar)" doesn't pass BSTR, but a pointer to Unicode string(And in C-side, you have to declare it as "WCHAR *StrVar" in this case). The latter case is how we call the W version of Win32 API functions.
Declaring the parameter in VB "As String" always triggers ANSI<->Unicode conversion back and forth, so make sure to use "As Long".
-
Mar 5th, 2019, 08:26 PM
#23
Thread Starter
New Member
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by qvb6
I think you could call SafeArrayRedim() from the C DLL to expand the SAFEARRAY, fill it with info, then return, so it's one call that way.
Another one call option is to use BSTR, with NULL's in the middle to separate the list of files. Call SysReAllocStringLen() from the C DLL to resize the VB string. In the VB side, you have to declare the parameter "As Long", and pass "VarPtr(StrVar)". This passes BSTR to the DLL. Using "StrPtr(StrVar)" doesn't pass BSTR, but a pointer to Unicode string(And in C-side, you have to declare it as "WCHAR *StrVar" in this case). The latter case is how we call the W version of Win32 API functions.
Declaring the parameter in VB "As String" always triggers ANSI<->Unicode conversion back and forth, so make sure to use "As Long".
Sorry for the delay getting back. I was called away on some bug fixing in another area of the project.
The way things work now is via DDE, which can only be with strings with VB6. There are a handful for formats though that appear to have a format code embedded in the return string. Everything is abstracted through the DDE interface in VB, so I can't tell for sure. On the C side there are some formatting routines that format the data based on these codes. One format is an array of strings null terminated. The DDE form has several different controls and different calls give a different control. That may be where the system figures out which return format it expects, but I'm not sure. On the VB side there are around 400 different DDE calls, so there is a lot of bits to inspect and sort through.
-
Mar 14th, 2019, 02:30 AM
#24
Thread Starter
New Member
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by qvb6
I think you could call SafeArrayRedim() from the C DLL to expand the SAFEARRAY, fill it with info, then return, so it's one call that way.
Another one call option is to use BSTR, with NULL's in the middle to separate the list of files. Call SysReAllocStringLen() from the C DLL to resize the VB string. In the VB side, you have to declare the parameter "As Long", and pass "VarPtr(StrVar)". This passes BSTR to the DLL. Using "StrPtr(StrVar)" doesn't pass BSTR, but a pointer to Unicode string(And in C-side, you have to declare it as "WCHAR *StrVar" in this case). The latter case is how we call the W version of Win32 API functions.
Declaring the parameter in VB "As String" always triggers ANSI<->Unicode conversion back and forth, so make sure to use "As Long".
I've been working on another part of this, but I'm getting back to the interface. I'm a little confused here, could you clarify?
As I understand it, on the VB side I do:
Code:
Private Declare Function ttw_VBGetAppResult Lib "TTWLib32" (ByVal StrVar As Long, ByVal getError As Short) As Integer
In the calling function in VB
Code:
Dim StrVar As Long
Dim ret As Integer
ret = ttw_VBGetAppResult(VarPtr(StrVar), True)
And then in the DLL, I'm not sure how to handle this. Should the function be declared to use a WCHAR *StrVar, or a BSTR *StrVar.
SysReAllocString takes a BSTR * and an OLECHAR *. OLECHAR is just typedef WCHAR. The old programs are ASCII, but strings can be converted with the USES_CONVERSION macros. However, how can I allocated enough space for the entire string array? I may need to write a custom ASCII to WCHAR converter for the array, but the stumbling block is how to allocate enough space in the BSTR. SysReAllocString is just going to allocate space for the first string in the array.
I can just do a straight up GlobalReAlloc for the string array and the BSTR header, but it doesn't look like there is a function like SysReAllocString that can realloc a BSTR for a string array. Or am I missing something?
Thanks
-
Mar 14th, 2019, 03:40 AM
#25
Re: Trying to Figure Out How to Communicate with a C DLL
Two things:
1) There is no "Short" in VB
ByVal getError As Integer (16 Bit signed)
ByVal getError As Long (32 Bit signed)
And since it looks like a Boolean use as Long (or Boolean directly)
Of course it has to match on the C-side.
If you can't change the function on the C-side i'd consider going with a constant.
Const CFALSE As Integer = 0
Const CTRUE As Integer = 1
I just don't like implicit conversion.....
2)
what qvb6 was alluding to:
Dim StrVar As String (you have As Long)
ret = ttw_VBGetAppResult(VarPtr(StrVar), True)
and that, on the C-side, you construct a single String from your Array of Strings (with NUL-characters in between separating the members).
On the VB-side then it's a simple split with Chr(0) as Delimiter.
You could even go the "classic" WINAPI-way calling the function first with a Null-Pointer (eg. with "&0"), telling your function to return the number of space needed in the String-Variable.
On the VB-side you allocate memory based on this result, and call your function a second time, this time sending the allocated string as shown above.
Last edited by Zvoni; Mar 14th, 2019 at 03:55 AM.
Last edited by Zvoni; Tomorrow at 31:69 PM.
----------------------------------------------------------------------------------------
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------------------
People call me crazy because i'm jumping out of perfectly fine airplanes.
---------------------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad
-
Mar 14th, 2019, 03:48 AM
#26
Thread Starter
New Member
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by Zvoni
Two things: There is no "Short" in VB
ByVal getError As Integer (16 Bit signed)
ByVal getError As Long (32 Bit signed)
what qvb6 was alluding to:
Dim StrVar As String (you have As Long)
ret = ttw_VBGetAppResult(VarPtr(StrVar), True)
and that, on the C-side, you construct a single String from your Array of Strings (with NULL-characters in between separating the members).
On the VB-side then it's a simple split with Chr(0) as Delimiter
My bad about the Short.
qvb6 said that the variable had to be declared as Long in VB, but I probably misunderstood. Am I going to have to manually reallocate space for the BSTR on the C side to handle the entire string array?
-
Mar 14th, 2019, 04:05 AM
#27
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by wdolson
My bad about the Short.
qvb6 said that the variable had to be declared as Long in VB, but I probably misunderstood. Am I going to have to manually reallocate space for the BSTR on the C side to handle the entire string array?
Yes, in the Declare of your Function, not the Variable itself.
StrVar is a String
VarPtr(StrVar) takes the Address of the Variable
As for ReAlloc: Probably yes.
Take all strings, count the Characters (resp. the Bytes), remember the needed NUL's you need to insert, and you're set.
String1="This is Teststring1" --> 19 Characters, 38 Bytes
String2="This is another TestString" --> 26 Characters, 52 Bytes
Result --> 45 Characters (90 Bytes) + 1 Character for NUL (2 Bytes)
As mentioned above you can do the allocation on the VB-side
Something along the lines
VB-Side:
First Call
ret = ttw_VBGetAppResult(&0, True)
C-Side:
If AddressOFStringVariable=0 Then CalculateNeededLength
In your function result you return in this example 46 (45 characters + 1 NUL-Character)
VB-Side:
StrVar=Space(ret) (Or StrVar=String(Ret, "*"))
Second call
ret = ttw_VBGetAppResult(VarPtr(StrVar),True)
C-Side (I'm simluating as if the DLL was written in VB)
If AddressOFStringVariable=0 Then
CalculateNeededLength
Else
Put the String into the Address provided
Set Result to something (Status?)
NukeTheWhiteHouse
End IF
Last edited by Zvoni; Mar 14th, 2019 at 04:08 AM.
Last edited by Zvoni; Tomorrow at 31:69 PM.
----------------------------------------------------------------------------------------
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------------------
People call me crazy because i'm jumping out of perfectly fine airplanes.
---------------------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad
-
Mar 14th, 2019, 08:54 PM
#28
Fanatic Member
Re: Trying to Figure Out How to Communicate with a C DLL
In post #6 in this thread, the last link that dz32 posted contain important links that you may want to check out, such as "Writing C DLLs for use with VB", and "SafeArrays in C".
If the C DLL is using ANSI, then using BSTR and SAFEARRAY(to handle an array of Strings, and resize them) would be extra work. Less coding method is to let VB do the Unicode to ANSI conversion. Example:
VB Side:
Code:
Private Declare Function ttw_VBGetAppResult Lib "TTWLib32" (ByRef StrVar As String, ByRef getError As Integer) As Long
Dim sIn As String
Dim sOut() As String
Dim getError As Integer ' 16 Bits
sIn = String(1000, 0) ' Allocate buffer, 1000 x Null. You can also use Space(1000)
ret = ttw_VBGetAppResult(sIn, getError)
'sIn = Left(sIn, ret) ' Trim extra characters, assuming ret=actual buffer size
sOut = Split(sIn, Chr(0))
For i = 0 To UBound(sOut)
Debug.Print i; "'" & sOut(i) & "'"
Next
Because the first parameter is declared "As String" in ttw_VBGetAppResult(), VB would create a temp buffer, converts the string to ANSI, and when the function returns, if the parameter was declared ByVal, the result is thrown away(Not what you want), and if it was declared ByRef, then the temp buffer(which was changed by the C DLL) is converted back to Unicode. String length would stay the same.
Using BSTR/SAFEARRAY means extra work on the C DLL, so in VB side coding is easier(No need to allocate buffer then use Split). Changing the first parameter from "As String" to "As Long" let us turn off ANSI<->Unicode conversion, and pass either BSTR(VarPtr), or WCHAR(StrPtr). BSTR consists of the length in bytes of the string(4 bytes), not including the null, immediately followed by the Unicode string, and followed by a null char(2 bytes).
As for callbacks, in VB side you could use AddressOf operator to pass a function pointer to the C DLL. However, I think you can't use other than the GUI thread to make the callback. Another option is to use SendMessage/PostMessage to send a message to VB from the C DLL if it's multithreaded.
-
Mar 15th, 2019, 03:41 AM
#29
Thread Starter
New Member
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by qvb6
In post #6 in this thread, the last link that dz32 posted contain important links that you may want to check out, such as "Writing C DLLs for use with VB", and "SafeArrays in C".
If the C DLL is using ANSI, then using BSTR and SAFEARRAY(to handle an array of Strings, and resize them) would be extra work. Less coding method is to let VB do the Unicode to ANSI conversion. Example:
VB Side:
Code:
Private Declare Function ttw_VBGetAppResult Lib "TTWLib32" (ByRef StrVar As String, ByRef getError As Integer) As Long
Dim sIn As String
Dim sOut() As String
Dim getError As Integer ' 16 Bits
sIn = String(1000, 0) ' Allocate buffer, 1000 x Null. You can also use Space(1000)
ret = ttw_VBGetAppResult(sIn, getError)
'sIn = Left(sIn, ret) ' Trim extra characters, assuming ret=actual buffer size
sOut = Split(sIn, Chr(0))
For i = 0 To UBound(sOut)
Debug.Print i; "'" & sOut(i) & "'"
Next
Because the first parameter is declared "As String" in ttw_VBGetAppResult(), VB would create a temp buffer, converts the string to ANSI, and when the function returns, if the parameter was declared ByVal, the result is thrown away(Not what you want), and if it was declared ByRef, then the temp buffer(which was changed by the C DLL) is converted back to Unicode. String length would stay the same.
Using BSTR/SAFEARRAY means extra work on the C DLL, so in VB side coding is easier(No need to allocate buffer then use Split). Changing the first parameter from "As String" to "As Long" let us turn off ANSI<->Unicode conversion, and pass either BSTR(VarPtr), or WCHAR(StrPtr). BSTR consists of the length in bytes of the string(4 bytes), not including the null, immediately followed by the Unicode string, and followed by a null char(2 bytes).
As for callbacks, in VB side you could use AddressOf operator to pass a function pointer to the C DLL. However, I think you can't use other than the GUI thread to make the callback. Another option is to use SendMessage/PostMessage to send a message to VB from the C DLL if it's multithreaded.
I have no idea if the VB program is multithreaded or not. It was originally written in 1990 and has been tweaked a number of times over the years. We had someone working to clean up the program and fix a bunch of bugs a few years ago, but we lost his support. We make heavy use of SendMessage and PostMessage in the C and C++ code and I'm well versed with how they work in C/C++, but they are not used anywhere in the VB program. I thought I would post some of the existing DDE code in VB to show how it's done now. There are three different DDE calling functions, but they are variations on the same theme. I was hoping to be able to do a straight replacement of these functions because I don't want to change the code all over the program. There are around 400 places DDE calls are made and rewriting every function would take me a long time.
Here is one of the DDE handler functions (I made a few edits to remove some proprietary details, if the code doesn't look like it will run, that's probably an error from my editing):
Code:
Function DDEExecute2(topicstr As String, execontrol As Control, CmdStr As String, ErrCtl As Control) As Integer
'This function serves as a generic api launcher for api commands that
'expects to receive data after the execute command.
'errctl may be a label or text control - use text control when passing
'large amounts of data because a label has a limit of 2048 (be sure to
'set the text control's MultiLine to true), error messages and request data
'are placed in the caption/text of this control.
'Debug.Print "DDEExecute2 - " & cmdstr
Dim temp As String, msg As String, appname As String
Dim applen As Integer
Dim LinkMethod As String ' Added for use in properly displaying errors via CaptureDDEActivity
Dim DDECompareTimeout As Boolean ' Added to support error handling of timeouts
If Not UTIAppsRunning Then Exit Function
If Testing = 0 Then
execontrol.LinkTimeout = -1
Else
execontrol.LinkTimeout = 3000 '
End If
execontrol.LinkMode = vbLinkNone
'initiate link if first time
'only one instance of the app must be loaded
On Error GoTo applinkerr
execontrol.LinkTopic = topicstr
DoEvents
execontrol.LinkMode = vbLinkManual
On Error GoTo execontrol2ExeErr 'do error handling on error
LinkMethod = "LinkExecute" '
execontrol.LinkExecute CmdStr
CaptureDDEActivity execontrol.LinkTopic, "", CmdStr, "", "DDEExecute2", "LinkExecute", ErrCtl.Name, "N/A" '
DoEvents
execontrol.LinkItem = "result"
DoEvents
'check type of control, different controls have different
'text fields that they display, use the proper field to
'save the current text because a LinkRequest command will
'return data into the text field of the control.
If TypeOf execontrol Is Label Then
temp = execontrol.Caption
ElseIf TypeOf execontrol Is TextBox Then
temp = execontrol.text
End If
execontrol.LinkMode = vbLinkManual
LinkMethod = "LinkRequest" '
execontrol.LinkRequest 'get data
If TypeOf execontrol Is Label Then
CaptureDDEActivity execontrol.LinkTopic, execontrol.LinkItem, "", execontrol.Caption, "DDEExecute2", "LinkRequest", "N/A", "N/A"
Else
CaptureDDEActivity execontrol.LinkTopic, execontrol.LinkItem, "", execontrol.text, "DDEExecute2", "LinkRequest", "N/A", "N/A"
End If
DoEvents
'place data in the error label and restore
'previous control text
If TypeOf execontrol Is Label Then
If TypeOf ErrCtl Is Label Then
ErrCtl.Caption = execontrol.Caption
Else
ErrCtl.text = execontrol.text
End If
execontrol.Caption = temp
ElseIf TypeOf execontrol Is TextBox Then
If TypeOf ErrCtl Is Label Then
ErrCtl.Caption = execontrol.text
Else
ErrCtl.text = execontrol.text
End If
execontrol.text = temp
End If
execontrol.LinkMode = vbLinkNone
Exit Function
execontrol2ExeErr: 'error handling routine
Select Case LinkMethod
Case "LinkExecute"
CaptureDDEActivity execontrol.LinkTopic, "", CmdStr, Err.Description, "DDEExecute2", "LinkExecute", ErrCtl.Name, "N/A"
Case "LinkRequest"
CaptureDDEActivity execontrol.LinkTopic, execontrol.LinkItem, "", Err.Description, "DDEExecute2", "LinkRequest", "N/A", "N/A"
End Select
DDEExecute2 = -1
execontrol.LinkItem = "error"
' 9/6/2011, Added section to capture DDE timeouts during comparisons
DDECompareTimeout = False
If Testing = 1 And Err.Description = "Timeout while waiting for DDE response" Then
MsgBox "Timeout waiting for response." & vbNewLine & vbNewLine & "Please close window" & vbNewLine & "before proceeding with operation.", vbExclamation, PROGRAMNAME
DDECompareTimeout = True
End If
'check type of control, different controls have different
'text fields that they display, use the proper field to
'save the current text because a LinkRequest command will
'return data in the text field of the controls.
If TypeOf execontrol Is Label Then
temp = execontrol.Caption
If DDECompareTimeout = False Then ' 9/6/2011, Added If statement to prevent LinkRequest from being called during compare timeouts
execontrol.LinkRequest
CaptureDDEActivity execontrol.LinkTopic, execontrol.LinkItem, "", execontrol.Caption, "MTCall_frm", "DDEExecute2", "LinkRequest", "N/A", "N/A" ' 8/7/2011 - Added
End If
If TypeOf ErrCtl Is Label Then
ErrCtl.Caption = execontrol.Caption
Else
ErrCtl.text = execontrol.text
End If
execontrol.Caption = temp
ElseIf TypeOf execontrol Is TextBox Then
temp = execontrol.text
execontrol.LinkMode = vbLinkManual
If DDECompareTimeout = False Then ' 9/6/2011, Added If statement to prevent LinkRequest from being called during compare timeouts
execontrol.LinkRequest
CaptureDDEActivity execontrol.LinkTopic, execontrol.LinkItem, "", execontrol.text, "DDEExecute2", "LinkRequest", "N/A", "N/A" ' 8/7/2011 - Added
End If
If TypeOf ErrCtl Is Label Then
ErrCtl.Caption = execontrol.text
Else
ErrCtl.text = execontrol.text
End If
execontrol.text = temp
Else 'cannot place error message in picture box
MsgBox "Error occurred during DDE Execute"
End If
execontrol.LinkMode = vbLinkNone
Exit Function
applinkerr:
'error occurred trying to establish a DDE link with
'an application that is not currently running or has
'multiple instances running.
msg = Error$(Err) 'get error message
msg = msg & NL & NL
applen = InStr(topicstr, "|")
appname = Mid$(topicstr, 1, applen - 1)
msg = msg & "Application " & appname & " is not loaded or more than" & NL & "one instance of " & appname & " is running."
MsgBox msg, 0, "DDE Error"
execontrol.LinkMode = vbLinkNone
DDEExecute2 = -1
Exit Function
End Function
CaptureDDEActivity is a debugging window the programmer cleaning things up implemented. It's a handy tool for viewing DDE traffic.
A typical calling function looks like this:
Code:
Public Function Compare_Summary(TestFileName As String) As Integer
DDEcontrol = ""
ErrorText = ""
OutPut_txt = ""
CmdStr = "[Compare.Summary(" & TestFileName & ")]"
res = DDEExecute2("MyApp|Execute", DDEcontrol, CmdStr, ErrorText)
Compare_Summary = res
OutPut_txt.text = ErrorText
End Function
In some cases ErrorText might be an error if the call had a problem, or it may be the returned string, or an array of strings terminated with a double NULL (on the C side). There is a hidden form called DDE Calls in the program with a number of controls on it. One is a TextBox called Output_txt. There is also a Label called ErrorText, and a TextBox called DDEControl. There also appear to be a few controls I don't think are ever used: a Label called Label1 and a ListBox called pSort_list. I was hoping to reuse the Output_txt and ErrorText without using any of the other DDE controls.
The string array is often fairly small, but it can be 15K in some instances. I have seen the string array null separated, but now I can't find an example. All the arrays I see now in the captured data are tab separated, which would make the return one long string. Maybe I hallucinated the null separated array?
In any case, I don't see where the VB app sets the string size and the C apps just allocate and return what they need to return. I assume the DDE interface is doing some kind of string sizing, but that's hidden from me in the depths of the OS functions.
-
Mar 15th, 2019, 08:28 AM
#30
Fanatic Member
Re: Trying to Figure Out How to Communicate with a C DLL
Standard EXE VB6 Projects are always single threaded. ActiveX EXE Projects can be multithreaded, but I doubt that you have that. If you go to Project-->Properties, the Component tab is disabled for Standard EXE projects.
I haven't used DDE, but it's just one way to exchange data. BSTR/SAFEARRAY is another option, and so is just sending ANSI/Unicode string back and forth. I suggest that you stick with DDE unless you have reasons otherwise. I know that DDE might use HWND_BROADCAST when sending WM_DDE_INITIATE, and that could cause things to freeze for few seconds if any top level window is busy(such as single threaded VB6 apps searching for files without yielding control with DoEvents every now and then).
The SendMessage/PostMessage approach I mentioned require more work in the VB side. You have to either add a hidden control, such as TextBox, and send WM_SETTEXT to it from the C DLL, which is similar to DDE, or subclass a VB form or control and send a custom WM_APP message to it(More work on the VB side). When SendMessage sends to a different thread, it switches to that thread, process the message, then switch back to the original thread before returning. That's why I suggested it, but it seems that your DLL is single threaded too.
So there are several approaches, and sticking with what you have is easier.
-
Mar 15th, 2019, 01:25 PM
#31
Re: Trying to Figure Out How to Communicate with a C DLL
What's the problem to create a COM object and use it to communicate between the APP and VB? If you need to send the data you just call the method, if APP wants to send the data it raises the event.
-
Mar 25th, 2019, 02:01 AM
#32
Thread Starter
New Member
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by qvb6
Standard EXE VB6 Projects are always single threaded. ActiveX EXE Projects can be multithreaded, but I doubt that you have that. If you go to Project-->Properties, the Component tab is disabled for Standard EXE projects.
I haven't used DDE, but it's just one way to exchange data. BSTR/SAFEARRAY is another option, and so is just sending ANSI/Unicode string back and forth. I suggest that you stick with DDE unless you have reasons otherwise. I know that DDE might use HWND_BROADCAST when sending WM_DDE_INITIATE, and that could cause things to freeze for few seconds if any top level window is busy(such as single threaded VB6 apps searching for files without yielding control with DoEvents every now and then).
The SendMessage/PostMessage approach I mentioned require more work in the VB side. You have to either add a hidden control, such as TextBox, and send WM_SETTEXT to it from the C DLL, which is similar to DDE, or subclass a VB form or control and send a custom WM_APP message to it(More work on the VB side). When SendMessage sends to a different thread, it switches to that thread, process the message, then switch back to the original thread before returning. That's why I suggested it, but it seems that your DLL is single threaded too.
So there are several approaches, and sticking with what you have is easier.
I was figuring out how something worked on the C side this week. It looks like I'm going to have to handle the return strings from one of the apps differently than the rest. But the core issue right now is getting any strings back in the proper format. I was able to get a response back, but the string was all 0xfe (FE hex).
The functions in VB are declared as:
Code:
Private Declare Function ttw_SendToApp Lib "TTWLib32" (ByVal xfertype As Integer, ByVal CommandStr As String, ByVal bExpectReturn As Integer) As Integer
Private Declare Function ttw_VBGetAppResult Lib "TTWLib32" (ByRef StrVar As String, ByVal getError As Integer) As Long
Private Declare Function ttw_ClearAppResult Lib "TTWLib32" () As Long
And the VB function looks like:
Code:
Function DDEExecute2(topicstr As String, execontrol As Control, CmdStr As String, ErrCtl As Control) As Integer
Dim temp As String, msg As String, appname As String
Dim applen As Integer
Dim LinkMethod As String
Dim DDECompareTimeout As Boolean
Dim cmdNum As Integer
Dim ret As Integer
Dim errText As String
Dim sIn As String
'Dim sOut() As String
sIn = String(2048, 0) ' Allocate buffer, 1000 x Null
If Not UTIAppsRunning Then Exit Function
execontrol.LinkTopic = topicstr
DoEvents
LinkMethod = "LinkExecute"
' New code
If topicstr = "App1|Execute" Then
cmdNum = 3
ElseIf topicstr = "App2|Execute" Then
cmdNum = 2
ElseIf topicstr = "App3|List" Then
cmdNum = 12
ElseIf topicstr = "App3|Execute" Then
cmdNum = 1
ElseIf topicstr = "App4|Execute" Then
cmdNum = 8
End If
ret = ttw_SendToApp(cmdNum, CmdStr, 1)
CaptureDDEActivity execontrol.LinkTopic, "", CmdStr, "", "DDEExecute2", "LinkExecute", ErrCtl.Name, "N/A"
DoEvents
execontrol.LinkItem = "result"
DoEvents
'check type of control, different controls have different
'text fields that they display, use the proper field to
'save the current text because a LinkRequest command will
'return data into the text field of the control.
If TypeOf execontrol Is Label Then
temp = execontrol.Caption
ElseIf TypeOf execontrol Is TextBox Then
temp = execontrol.text
End If
LinkMethod = "LinkRequest"
' execontrol.LinkRequest 'get data
' GET DATA NEW WAY
ret = ttw_VBGetAppResult(sIn, 0)
If TypeOf execontrol Is Label Then
execontrol.Caption = sIn
CaptureDDEActivity execontrol.LinkTopic, execontrol.LinkItem, "", execontrol.Caption, "DDEExecute2", "LinkRequest", "N/A", "N/A"
Else
execontrol.text = sIn
CaptureDDEActivity execontrol.LinkTopic, execontrol.LinkItem, "", execontrol.text, "DDEExecute2", "LinkRequest", "N/A", "N/A"
End If
DoEvents
'place data in the error label and restore
'previous control text
If TypeOf execontrol Is Label Then
If TypeOf ErrCtl Is Label Then
ErrCtl.Caption = execontrol.Caption
Else
ErrCtl.text = execontrol.text
End If
execontrol.Caption = temp
ElseIf TypeOf execontrol Is TextBox Then
If TypeOf ErrCtl Is Label Then
ErrCtl.Caption = execontrol.text
Else
ErrCtl.text = execontrol.text
End If
execontrol.text = temp
End If
ret = ttw_ClearAppResult()
Exit Function
execontrol2ExeErr: 'error handling routine
Select Case LinkMethod
Case "LinkExecute"
CaptureDDEActivity execontrol.LinkTopic, "", CmdStr, Err.Description, "DDEExecute2", "LinkExecute", ErrCtl.Name, "N/A"
Case "LinkRequest"
CaptureDDEActivity execontrol.LinkTopic, execontrol.LinkItem, "", Err.Description, "DDEExecute2", "LinkRequest", "N/A", "N/A"
End Select
DDEExecute2 = -1
execontrol.LinkItem = "error"
DDECompareTimeout = False
If Testing = 1 And Err.Description = "Timeout while waiting for DDE response" Then
'MsgBox "App is taking a long time to respond." & vbNewLine & vbNewLine & "Check to see if App has crashed and restart if necessary.", vbExclamation, PROGRAMNAME
'MsgBox "Error occured during compare. Timeout waiting for results." & vbNewLine & vbNewLine & "Check 'Compare Trace' summary window for results.", vbExclamation, PROGRAMNAME
MsgBox "Timeout waiting for response", vbExclamation, PROGRAMNAME
DDECompareTimeout = True
End If
'check type of control, different controls have different
'text fields that they display, use the proper field to
'save the current text because a LinkRequest command will
'return data in the text field of the controls.
If TypeOf execontrol Is Label Then
temp = execontrol.Caption
If DDECompareTimeout = False Then
execontrol.LinkRequest
CaptureDDEActivity execontrol.LinkTopic, execontrol.LinkItem, "", execontrol.Caption, "DDEExecute2", "LinkRequest", "N/A", "N/A"
End If
If TypeOf ErrCtl Is Label Then
ErrCtl.Caption = execontrol.Caption
Else
ErrCtl.text = execontrol.text
End If
execontrol.Caption = temp
ElseIf TypeOf execontrol Is TextBox Then
temp = execontrol.text
If DDECompareTimeout = False Then
execontrol.LinkRequest
CaptureDDEActivity execontrol.LinkTopic, execontrol.LinkItem, "", execontrol.text, "DDEExecute2", "LinkRequest", "N/A", "N/A"
End If
If TypeOf ErrCtl Is Label Then
ErrCtl.Caption = execontrol.text
Else
ErrCtl.text = execontrol.text
End If
execontrol.text = temp
Else 'cannot place error message in picture box
MsgBox "Error occurred during DDE Execute"
End If
r = DoEvents()
ret = ttw_ClearAppResult()
Exit Function
On the C side, the functions look like:
Code:
HRESULT CALLTYPE ttw_VBGetAppResult(BSTR *pVarRet, short getError)
{
USES_CONVERSION;
char *b0;
BSTR *b1;
HRESULT ret=-1;
// Size the array for the correct size
if(gApiReturnLength==0 || getError!=FALSE)
return(-1);
if(getError==FALSE)
{
b1 = pVarRet;
*(long *)b1 = gApiReturnLength;
b1 = pVarRet+2;
ret = gApiReturnLength;
wcscpy_s((WCHAR *)b1,gApiReturnLength,A2W(gszReturnApiString+DDE_OFFSET));
}
else
{
b1 = pVarRet;
ret = *(long *)b1 = strlen(gszLastApiError);
b1 = pVarRet+2;
wcscpy_s((WCHAR *)b1,ret,A2W(gszLastApiError));
}
return(ret);
}
From the debugger, I can see that the return strings look fine before I put them into StrVar (pVarRet on the C side) to send them back to the VB program.
I'm obviously doing something wrong. As far as C is concerned, a BSTR is defined as:
Code:
typedef OLECHAR *BSTR;
typedef WCHAR OLECHAR;
And the format of a BSTR is a 4 byte header with the length of the string and the rest as the string in wide characters.
-
Mar 25th, 2019, 03:20 AM
#33
Fanatic Member
Re: Trying to Figure Out How to Communicate with a C DLL
Close. You can't adjust VB's BSTR length in C the way you are doing, because it would cause leaks. To "adjust" the string length, you either call SysStringXXX functions, which creates a new string(That's the way it's done in VB, despite being inefficient, for efficiency, we use the Mid Statement), or let VB trim the extra nulls. The following use the first approach(Using SysStringXXX () functions).
You need to change the VB declaration to the following:
Private Declare Function ttw_VBGetAppResult Lib "TTWLib32" (ByVal StrVar As Long, ByVal getError As Integer) As Long
In C side call SysReAllocString() or SysReAllocStringLen().
And in DDEExecute2() in VB side, call it this way:
ret = ttw_VBGetAppResult(VarPtr(sIn), 0)
That's how you pass BSTR to C ByRef, despite the parameter was declared ByVal, which passes 32-Bit pointer to C side. This also eliminates the need to use "sIn = String(2048, 0)" in VB side.
Approach 2: Letting VB trim the extra nulls:
1 - You still need to change the Declare statement to be the same as above.
2 - In the C side, remove the line that changes VB string length.
3 - In VB, call the function below to trim any extra nulls:
Code:
Private Sub TrimNull(s As String)
Dim pos As Long
pos = InStr(s, Chr$(0))
If pos <> 0 Then
s = Left$(s, pos - 1)
End If
End Sub
Last edited by qvb6; Mar 25th, 2019 at 03:57 AM.
Reason: Please see corrections.
-
Mar 26th, 2019, 03:10 AM
#34
Thread Starter
New Member
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by qvb6
Close. You can't adjust VB's BSTR length in C the way you are doing, because it would cause leaks. To "adjust" the string length, you either call SysStringXXX functions, which creates a new string(That's the way it's done in VB, despite being inefficient, for efficiency, we use the Mid Statement), or let VB trim the extra nulls. The following use the first approach(Using SysStringXXX () functions).
You need to change the VB declaration to the following:
Private Declare Function ttw_VBGetAppResult Lib "TTWLib32" ( ByVal StrVar As Long, ByVal getError As Integer) As Long
In C side call SysReAllocString() or SysReAllocStringLen().
And in DDEExecute2() in VB side, call it this way:
ret = ttw_VBGetAppResult( VarPtr(sIn ), 0)
That's how you pass BSTR to C ByRef, despite the parameter was declared ByVal, which passes 32-Bit pointer to C side. This also eliminates the need to use "sIn = String(2048, 0)" in VB side.
Approach 2: Letting VB trim the extra nulls:
1 - You still need to change the Declare statement to be the same as above.
2 - In the C side, remove the line that changes VB string length.
3 - In VB, call the function below to trim any extra nulls:
Code:
Private Sub TrimNull(s As String)
Dim pos As Long
pos = InStr(s, Chr$(0))
If pos <> 0 Then
s = Left$(s, pos - 1)
End If
End Sub
Thanks a bunch, that finally worked. It also wasn't working because I didn't initialize my buffers on the C DLL side correctly. The programs swap data back and forth using shared memory between instances of the DLL running with each app. When I inherited the project I saw note in the code from the guy who ported the code to 32 bit saying that the shared memory variables had to be initialized, but didn't explain why. I had been initializing up to now, but forgot with two variables with this and it was driving me crazy that data put into the shared memory in one place wasn't seen in another program. Once I noticed I had forgotten to initialize, it worked! I used SysReAllocStringLen() and that did the job.
In another part of the program the VB program needs to answer a SendMessage call from the DLL. Is there a primer somewhere on how to answer SendMessage/PostMessage calls with VB? In this case it should be simple, all the C program needs to do is send a flag.
Thanks again!
-
Mar 26th, 2019, 06:05 AM
#35
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by wdolson
In another part of the program the VB program needs to answer a SendMessage call from the DLL. Is there a primer somewhere on how to answer SendMessage/PostMessage calls with VB? In this case it should be simple, all the C program needs to do is send a flag.
Thanks again!
I take it, the C-side expects an Address to a function? (kinda like: VB sending the Address of a procedure/function to the c-dll "Hey, this is the address to dump all messages from your c-side")
Last edited by Zvoni; Tomorrow at 31:69 PM.
----------------------------------------------------------------------------------------
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------------------
People call me crazy because i'm jumping out of perfectly fine airplanes.
---------------------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad
-
Mar 26th, 2019, 08:03 AM
#36
Thread Starter
New Member
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by Zvoni
I take it, the C-side expects an Address to a function? (kinda like: VB sending the Address of a procedure/function to the c-dll "Hey, this is the address to dump all messages from your c-side")
I was working on a part of the DLL where pointers needed to be passed, but that's working now. For the SendMessage/PostMessage part, one of the C programs needs to send a notification to the VB program that it's finished. So all I need to do is send a flag, ie set lParam or wParam to 1 to tell the VB program that the processing is complete. How it's done currently is that the C program establishes a DDE channel to the VB program, sends a text string saying it's complete, but I'm replacing the DDE mechanism.
If there is a cleaner way than SendMessage/PostMessage, I'm open to suggestions. With the existing structure of the programs it's very easy to add new messages between C programs, but VB is a different animal.
BTW, going through the DLL is much faster than using DDE. With DDE there is noticeable lag as the C programs and the VB program send slews of messages back and forth to set up the processing, but it all happens to fast through the DLL that there is no noticeable lag anymore.
-
Mar 26th, 2019, 09:33 AM
#37
Re: Trying to Figure Out How to Communicate with a C DLL
Originally Posted by wdolson
How it's done currently is that the C program establishes a DDE channel to the VB program, sends a text string saying it's complete, but I'm replacing the DDE mechanism.
If there is a cleaner way than SendMessage/PostMessage, I'm open to suggestions. With the existing structure of the programs it's very easy to add new messages between C programs, but VB is a different animal.
In any way you use, you need an identification of the VB-receiver to pass to the c-dll first, so IMO a callback-function would be my first try, since VB offers the AddressOf-Operator.
That way, when establishing contact with the dll, the VB-program passes the Address to its function in which it would like to receive the notifications of the c-dll.
Since you have access to both sources, you can even setup the prototype of the funtion yourself
Note: AddressOf only works for Functions/Subs in standard bas-modules.
Doesn't work on Object-Methods (Forms, Classes)
Aircode!
Code:
'In a *.bas-module
Private Const C_DLL_SENDER As Long = 1
Private Const C_DLL_MSG_DONE As Long = 100
Private Declare Function HandShake Lib "MyCDll" (ByVal AddrCallBack As Long) As Long 'You have to provide an access to your c-dll to pass the Address
Public Sub MyCallback(ByVal SenderID As Long, ByVal Message As Long)
If SenderID = C_DLL_SENDER And Message=C_DLL_MSG_DONE Then
NukeTheWhiteHouse
Else
GoFeedTheDucks
End If
End Sub
'Somewhere Else
Public Sub EstablishConnectionToDll()
Dim Result As Long
Result=HandShake(AddressOf(MyCallback))
End Sub
I take it you know how to use/call callback-functions from C....
Last edited by Zvoni; Tomorrow at 31:69 PM.
----------------------------------------------------------------------------------------
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------------------
People call me crazy because i'm jumping out of perfectly fine airplanes.
---------------------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad
-
Apr 3rd, 2019, 03:33 AM
#38
Thread Starter
New Member
Re: Trying to Figure Out How to Communicate with a C DLL
Just a note on my progress. It turned out I didn't need the SendMessage functionality. When I went in and found what the VB program was doing while the C program was running, it turned out the VB program was just polling until it got a DDE connection from the C program. All I needed to do was change the loop to poll a flag in the DLL. The C program just changes the flag when it's done and the VB program gets on with it's job. Very fast and a heck of a lot cleaner than the old way.
The VB interface is downright zippy now. It sometimes took a second or two to do things via DDE, but now directly through the DLL there is no noticeable lag at all. That should make the end customers happy.
I ran into another DDE function I had to replace at the end of last week and I wanted to make sure it was going to work OK, but I followed what I did for the other functions and that worked fine.
Thanks so much for all the help!
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|