Link to home
Start Free TrialLog in
Avatar of Bobeo
Bobeo

asked on

Converting a long into a String in VB

Hi guys,

I'm writing a windows DLL in VB6 using vbAdvance. This DLL is then having it's functions called by an external program.

I've noticed that the arguments for strings are coming through as longs.

For example:

An argument called sDefaultText, which must be textual, is coming through as 1936287828.

Is there a way to convert it to a VB string?
Avatar of 512Thz
512Thz

You probably mean the argument address which is passed as a VB long (32 bits address)

What happens? You expect a string to get modified by you call but the variable is unchanged???

Please give more details, since you r questionis somewhat vague.
Avatar of SStory
Visit:
http://vb.mvps.org/hardcore/

Open "Chapter 2", "Dealing with Strings"

See the section on "Getting Strings from the Windows API"

This should explain it to you.  Most APis are C style... they pass a pointer to the first Char... vb sees that as a long.  Vb stores strings differently.  Read this section to understand...actually the whole chapter is worth reading.
Also with many APIs' you must declare a string and create it with enough space reserved already.
Many just make a string with X number of spaces in it already and pass that.
Avatar of Bobeo

ASKER

Cheers SStory. I think I understand a little better the differences now, but am still stumped as to how to convert from one to another. Basically, I have the following pseudo code that I need to build a VB function from:

int CM_MSGS_ExecsExpiryWarning( IN MSGSBLOCK *Msgs, IN char *DefaultText, int NumExecsLeft ){
MessageBox( NULL, "Your program will stop working after a few more executions", "", MB_OK )
}

And my VB code in my DLL currently looks like this:

Sub CM_MSGS_ExecsExpiryWarning(MSGS As MsgsBlock, sDefaultText As Long, lNumExecsLeft As Long)
MsgBox CStr(sDefaultText)
End Sub

The message box is showing the number I mentioned before, 1936287828. I stumped as to how to convert it to a VB string.
Did you try to just define sDefaultText as String in the VB function signature ?
The following should do the conversion.
' put this in the common area
 Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal ByteLen As Long)
 
'
Sub CM_MSGS_ExecsExpiryWarning(MSGS As MsgsBlock, sDefaultText As Long, lNumExecsLeft As Long)
  Dim buffer(32000) As Byte ' put the maximum here
  Dim len   As Integer
  Dim index As Long
  Dim ptr   As Long
  Dim sText As String
 
  Ptr = sDefaultText 
  Index =
  Do
    CopyMemory buffer(Index), Ptr, 1
    Index = Index +1
    Ptr = Ptr + 1
    If buffer(Index) = 0 Then
       Exit Loop
    End If
  Loop
 
  ReDim Preserve buffer(Index)
  sText = StrConv(buffer, vbUnicode)
  MsgBox CStr(sText)
 
End Sub

Open in new window

' When passing the strings you must pass them byval and convert them.


' Try
Private declare sub lib "yourdll" ( MSGS as MsgBlock, Byval sDefaultText As String, Byval  lNumExecsLeft As Long)

Sub CM_MSGS_ExecsExpiryWarning(MSGS As MsgsBlock, Byval sDefaultText As String, Byval  lNumExecsLeft As Long)
MsgBox StrConv(sDefaultText, vbUnicode)
End Sub
Both of the above are correct that you do need a declare when calling from vb.net into a DLL from something like C++ which appears to be what you are doing.

I think what egl1044 says should work except if lNumExecsLeft is supposed to be an int in C then it would be a short in VB.NET and not a long....this may or may not matter.
The C type "Char *" is a byte array not a String (which is in Unicode that is 2 bytes per character)
I think the OP is trying to re-create the function to use in his own DLL in vb6. I don't think he is trying to call the function already exported in a DLL from another language
Avatar of Bobeo

ASKER

It makes sense that char is a byte array. Based on that, how would I write the VB function to handle that? Can VB handle byte arrays as arguments?

egl1044 is correct that I'm writing my own DLL, which I'm then saving as a Windows DLL and a third party program is calling on.
Look at the code I just posted (I left blank the initilization on Index because I don't know if you have base 0 or Base 1)

Did you try it?

Note that is has to loop in order to find the NULL (char(0)) which denotes the end of a string in C

If you can provide the length to the VB function ( from C function strlen(DefaultText)  ) than the loop can be avoided (And the code be more elegant)
I forgot to answer your question. No you can not just specify the argument "ByVal sDefaulText as Byte()" because VB adds control fields to arrays (and Strings) therefore you need the "CopyMemory" API to bring the data to a valid array.
Avatar of Bobeo

ASKER

I did try your code 512, but when I try to compile it using vbAdvance it says "Array already dimensioned" on the ReDim Preserve line.

The plot does thicken a little though. I've found s C++ example of the sub I'm trying to create.

Does this help?
void _stdcall CM_MSGS_ExecsExpiryWarning( MSGSBLOCK *Msgs, char *DefaultText, int ExecsToGo ) 
	{
	char buffer[100];
 
	wsprintf( buffer, "This program will only run %d times more\n", ExecsToGo );
	MessageBox(NULL,buffer,"CopyMinder Message DLL",MB_OK);
 
	}

Open in new window

Private Declare sub CM_MSGS_ExecsExpiryWarning ( byval msgs as long, byval DefaultText as long, byval ExecsTogo as long)


Sub CallAPI(ByVal mSgs As Long, ByVal DefaultText As String, ByVal ExecsTogo As Long)
   
    Dim b()     As Byte
    b = StrConv(DefaultText, vbFromUnicode)
    Call CM_MSGS_ExecsExpiryWarning(VarPtr(mSgs), VarPtr(b(0)), 0)

End Sub
Avatar of Bobeo

ASKER

Thanks egl, but your code tells me how to call the function, not how to write the function. It's my third party program that will be calling the function, I need to write a function to tell it what to do when it calls it.

In other words, I basically need the code on my above post converting to VB6.
Then we can first calculate the required length, and then copy the data.
' put this in the common area
 Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal ByteLen As Long)
 
'
Sub CM_MSGS_ExecsExpiryWarning(MSGS As MsgsBlock, sDefaultText As Long, lNumExecsLeft As Long)
  Dim buffer() As Byte 
  Dim len   As Integer
  Dim index As Long
  Dim sText As String
 
  Index = 0
  Do
    If buffer(Index) = 0 Then
       Exit Loop
    End If
    Index = Index +1
  Loop
 
  ReDim buffer(Index)
  CopyMemory buffer(0), sDefaultText, Index
  sText = StrConv(buffer, vbUnicode)
  MsgBox CStr(sText)
 
End Sub

Open in new window

I think you should explain what it is your trying to do. Your trying to pass a string value to a long value which im not sure why it didn't crash. If you want to pass a string by using a long value then you need to pass it by its string pointer using StrPtr(). Otherwise you can just pass it as a String. In order to make any correlation to your function we need to know what it is your doing and how the clients is setup. The function below is the the simple conversion, now the question is what are you doing inside this function and what are you passing as values from the client. Then we could change the procedure around to relate to what it is your doing.

Public Sub CM_MSGS_ExecsExpiryWarning(msgs As MSGBLOCK, ByVal DefaultText As String, ByVal ExecsTogo As Long)


End Sub
' This should work as an example to pass a string pointer to your dll

Option Explicit

Public Type MSGBLOCK    'Sample
    StructLength    As Long
    Data            As Long
End Type

Private Declare Function lstrlenW Lib "kernel32" ( _
        ByVal lpString As Long) As Long

Private Declare Sub push Lib "kernel32" Alias "RtlMoveMemory" ( _
    Destination As Any, _
    ByVal Source As Long, _
    ByVal Length As Long)

Private Function Wrapper(ByVal lPtr As Long) As String
' should add some error checking but for example
    Dim lLen As Long
    Dim bytes() As Byte
    lLen = lstrlenW(lPtr)
    lLen = (lLen * 2)
    ReDim bytes(lLen) As Byte
    push bytes(0), lPtr, lLen
    Wrapper = bytes
End Function

Public Sub CM_MSGS_ExecsExpiryWarning(msgs As MSGBLOCK, ByVal DefaultText As Long, ByVal ExecsTogo As Long)
' note: probrably not wise to use msgbox's because it's not safe
 MsgBox (DefaultText)           'before
 MsgBox Wrapper(DefaultText)    'after
 MsgBox msgs.StructLength       ' passed
 
End Sub



' To call it in VB6

Option Explicit

Private Type MSGBLOCK    'Sample (8 bytes)
    StructLength    As Long
    Data            As Long
End Type

Private Declare Sub CM_MSGS_ExecsExpiryWarning Lib "e:\test.dll" (msgs As MSGBLOCK, ByVal DefaultText As Long, ByVal ExecsTogo As Long)

Dim msgs As MSGBLOCK

Private Sub Command1_Click()
   
    msgs.StructLength = Len(msgs)   '// sample
   
    Call CM_MSGS_ExecsExpiryWarning(msgs, StrPtr("hello world"), 1)
   
End Sub
ASKER CERTIFIED SOLUTION
Avatar of Bobeo
Bobeo

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial