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?
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?
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.
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.
Many just make a string with X number of spaces in it already and pass that.
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.
int CM_MSGS_ExecsExpiryWarning
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
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
' 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
' Try
Private declare sub lib "yourdll" ( MSGS as MsgBlock, Byval sDefaultText As String, Byval lNumExecsLeft As Long)
Sub CM_MSGS_ExecsExpiryWarning
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.
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
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.
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)
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.
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?
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);
}
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(mS gs), VarPtr(b(0)), 0)
End Sub
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
End Sub
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.
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
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
Public Sub CM_MSGS_ExecsExpiryWarning
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
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
' 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
Dim msgs As MSGBLOCK
Private Sub Command1_Click()
msgs.StructLength = Len(msgs) '// sample
Call CM_MSGS_ExecsExpiryWarning
End Sub
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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.