Fortran DLL in vb6:error about "character" and "read sentence" - dll

When trying to send a character to DLL,it seems not to be right(shown in "result_20170207").
Below is code about DLL:
subroutine char_1( pfilename )
!DEC$ ATTRIBUTES stdcall,DLLEXPORT::char_1
!DEC$ ATTRIBUTES ALIAS:"char_1"::char_1
!DEC$ ATTRIBUTES reference :: pfilename
character(len=512)::pfilename
!integer::i !(about ERROR TWO )
write(*,*)trim(pfilename)
!read(*,*)i !(about ERROR TWO )
open(unit=18,file="result_20170207",status='replace',action='write')
write(18,*)"This is dll"
write(18,*)trim(pfilename)
write(18,*)"the third line"
close(18)
end subroutine
Below is code in vb6.0:
Private Declare Sub char_1 Lib "D:\try_vb\char_1\char_1_dll\char_1_dll\Debug\char_1_dll.dll" (ByVal char As String)
Private Sub Command1_Click()
Dim char As String
char = "c:/desktop/aaa.txt"
Text1.Text = char
Call char_1(char)
End Sub
Private Sub Command2_Click()
End
End Sub
ERROR ONE:
The error happened in the result file is:
wrong result
I think it is because of the character.Why does it happen and how can i fix it? Thank you!
ERROR TWO:
By the way,when I add the code "read(,)i",another error is met.Shown below:
severe(39)
Codes I add are shown in the code which are commented out:
I am puzzled about the two problems.
According to MarkJ's answer,I have change my DLL code like below(ERROR ONE ; code in "String_Functions module" is omitted here):
subroutine char_1( pfilename )
!DEC$ ATTRIBUTES stdcall,DLLEXPORT::char_1
!DEC$ ATTRIBUTES ALIAS:"char_1"::char_1
!DEC$ ATTRIBUTES reference :: pfilename
use String_Functions !--------here--------!
character(len=512)::pfilename
character(len=512)::filename
integer::ii
ii=Clen(pfilename) !--------here--------!
filename=Ctrim(pfilename) !--------here--------!
open(unit=18,file="result_20170207",status='replace',action='write')
write(18,*)"This is dll"
write(18,*)filename(1:ii) !--------here--------!
write(18,*)"the third line"
close(18)
end subroutine

About your error (1) VB6 is sending a null-terminated string. The string contains an ASCII zero character which indicates the end of the string. Fortran TRIM is not sufficient to remove this character. Try the CTRIM routine below.
! ------------------------
PURE INTEGER FUNCTION Clen_trim(s) ! returns same result as LEN_TRIM unless:
CHARACTER(*),INTENT(IN) :: s ! last char non-blank is null, if true:
INTEGER :: i ! then len of C string is returned, note:
! Ctrim is only user of this function
i = INDEX(s, CHAR(0))
IF ( i == 0 ) i = LEN_TRIM(s)
Clen_trim = i
END FUNCTION Clen_trim
! ----------------
FUNCTION Ctrim(s1) RESULT(s2) ! returns same result as TRIM unless:
CHARACTER(*),INTENT(IN) :: s1 ! last non-blank char is null in which
CHARACTER(Clen_trim(s1)) :: s2 ! case trailing blanks prior to null
s2 = s1 ! are output
END FUNCTION Ctrim

Related

Why does my function assume a missing argument is there?

I have a function which updates a form, "LoadingInterface". The function looks like this:
Private Sub updateLoadingBar(Optional tekst As String, Optional barOnePerc As Long, Optional barTwoPerc As Long)
If Not IsMissing(tekst) Then
LoadingInterface.Label1.Caption = tekst
End If
If Not IsMissing(barOnePerc) Then
LoadingInterface.Bar.Width = barOnePerc * 1.68
LoadingInterface.prosent.Caption = barOnePerc & "%"
LoadingInterface.prosent.Left = barOnePerc * 1.68 / 2 - 6
End If
If Not IsMissing(barTwoPerc) Then
LoadingInterface.SubBar.Width = barTwoPerc * 1.68
End If
LoadingInterface.Repaint
End Sub
I then call the function like this, expecting it to only update the textfield, since the other two arguments are missing.
Call updateLoadingBar(tekst:="Test")
This works fine for updating Label1, but unfortunately the other two values are updated too - it seems that not including any values in the function-call makes VBA assume the two variables values are 0. What's more, it appears that the IsMissing function does not detect that the two values are missing when the function is called, which is the bigger problem. Stepping through the code using F8 confirms that all the if-statements are indeed entered.
Is there any way to make the code skip the two lowermost if-statements in my function, if no values are provided for the parameters barOnePerc and barTwoPerc?
IsMissing only works if the argument is declared as a Variant.
I don't think you can validly distinguish between 0 and no passed parameter for the Long. In that case you would need to declare as Variant in the signature. You can later cast if required.
I guess you could put a default (unlikely number) and test for that. Note: I wouldn't advise this. This just screams "Bug".
IsMissing:
IsMissing returns a Boolean value indicating whether an optional Variant argument has been passed to a procedure.
Syntax: IsMissing(argname)
The required argname argument contains the name of an optional Variant
procedure argument.
Remarks: Use the IsMissing function to detect
whether or not optional Variant arguments have been provided in
calling a procedure. IsMissing returns True if no value has been
passed for the specified argument; otherwise, it returns False.
Both methods:
Option Explicit
Public Sub Test()
RetVal
RetVal2
End Sub
Public Function RetVal(Optional ByVal num As Long = 1000000) As Long
If num = 1000000 Then
MsgBox "No value passed"
RetVal = num
Else
MsgBox "Value passed " & num
RetVal = num
End If
End Function
Public Function RetVal2(Optional ByVal num As Variant) As Long
If IsMissing(num) Then
MsgBox "No value passed"
Else
MsgBox "Value passed " & num
RetVal2 = CLng(num)
End If
End Function

Using character as argument to function

I would like to be able to only pass q as argument to a function, so that the user does not have to enter a string "q".
I have a function defined in a module
Function doThis(val As Variant)
MsgBox CStr(val)
' Here is a comparison of val with other strings and additional code
End
I call it from my worksheet:
=doThis(q)
And the messagebox returns
Error 2029
I have tried with String and Boolean as value type as well, but only variant fires the function.
Is it possible to receive a q as argument?
Quite simple. First create a Defined Name for q
Secondly in a standard module:
Function doThis(val As Variant)
MsgBox CStr(val)
doThis = ""
End Function
Finally in the worksheet:

ByRef in VB.NET

I have written the following code in VB.NET:
Dim obj As Object
obj = "00"
Test(obj)
MsgBox(obj)
Private Sub Test(ByRef num As Integer)
End Sub
Private Sub Test(ByVal num As Integer)
End Sub
When the value "00" is passed "ByRef" in the method "Test" it converts to 0. But if the value "00" is passed "ByVal" it keeps the same value as "00". How the passed value is being converted only depending of the signature?
In VB6 although the default passing type is "ByRef", still the same code keeps the same value("00")
Could anybody explain the reason behind this contradictory behaviour in VB6 and VB.NET?
The way you are doing it, the ByRef changes the type of the object from string to integer. By default, integer do not have trailling "0" when covnerted to strings.
This example below might help you understand what is hapenning.
Sub Main()
Dim o1 As Object = "00"
Dim o2 As Object = "00"
Console.WriteLine(o1.GetType().ToString())
Test1(o1)
Console.WriteLine(o1.GetType().ToString())
Console.WriteLine(o2.GetType().ToString())
Test2(o2)
Console.WriteLine(o2.GetType().ToString())
Console.ReadLine()
End Sub
Sub Test1(ByVal num As Integer)
End Sub
Sub Test2(ByRef num As Integer)
End Sub
Output
System.String
System.String
System.String
System.Int32
I suggest you always turn Option Strict On, this will remove a lot of confusion.
The object is of type System.String. It cannot be passed ByRef to a method, it is of the wrong type. So the compiler has to work around it and rewrites the code:
Dim obj As Object
obj = "00"
Dim $temp As Integer
$temp = CInt(obj)
Test($temp)
obj = $temp '' <=== Here
MsgBox(obj)
The indicated statement is the one that changes the object from a string to an integer. Which, converted again to a string by the MsgBox() call, produces "0" instead of "00".
Notable is that C# does not permit this and generate a compile error. This rewriting trick is rather nasty, if the method itself changes the original object then you'll have a very hard time guessing what is going on since that doesn't change the passed argument value.
ByRef means that value passes by reference and in function will be used the same value what has been sent.
ByVal means that value passes by value (function creates a copy of passed value) and you use only copy of value.

ByRef argument type mismatch in Excel VBA

I'm working with VBA. I wrote a user define function that takes a string, process it and return a cleaned string. I am not sure what is wrong with it. I am not able to call it and ask it to process my string and return it. I am thinking there are a mistake in the way I am defining or returning it.
Public Function ProcessString(input_string As String) As String
' The temp string used throughout the function
Dim temp_string As String
For i = 1 To Len(input_string)
temp_string = Mid(input_string, i, 1)
If temp_string Like "[A-Z, a-z, 0-9, :, -]" Then
return_string = return_string & temp_string
End If
Next i
return_string = Mid(return_string, 1, (Len(return_string) - 1))
ProcessString = return_string & ", "
End Function
And I use this function like this
Worksheets(data_sheet).Range("C2").Value = ProcessString(last_name)
Last name is a string variable, usually looks like this Lastname*****, and I am trying to remove all the stars behind it. Have it return Lastname without the stars.
I received Compile error: ByRef arugment type mismatch when I tried to run this. I am using Windows XP with Office 2003.
EDIT: I added the basic struction of the code I have, I have about 20 lines of the similar code. Doing the same thing for each field I need.
Private Sub CommandButton2_Click()
' In my original production code I have a chain of these
' Like this Dim last_name, first_name, street, apt, city, state, zip As String
Dim last_name As String
' I get the last name from a fixed position of my file. Because I am
' processing it from another source which I copied and pasted into excel
last_name = Mid(Range("A4").Value, 20, 13)
' Insert the data into the corresponding fields in the database worksheet
Worksheets(data_sheet).Range("C2").Value = ProcessString(last_name)
I suspect you haven't set up last_name properly in the caller.
With the statement Worksheets(data_sheet).Range("C2").Value = ProcessString(last_name)
this will only work if last_name is a string, i.e.
Dim last_name as String
appears in the caller somewhere.
The reason for this is that VBA passes in variables by reference by default which means that the data types have to match exactly between caller and callee.
Two fixes:
1) Force ByVal -- Change your function to pass variable ByVal: Public Function ProcessString(ByVal input_string As String) As String, or
2) Dim varname -- put Dim last_name As String in the caller before you use it.
(1) works because for ByVal, a copy of input_string is taken when passing to the function which will coerce it into the correct data type. It also leads to better program stability since the function cannot modify the variable in the caller.
I don't know why, but it is very important to declare the variables separately if you want to pass variables (as variables) into other procedure or function.
For example there is a procedure which make some manipulation with data: based on ID returns Part Number and Quantity information. ID as constant value, other two arguments are variables.
Public Sub GetPNQty(ByVal ID As String, PartNumber As String, Quantity As Long)
the next main code gives me a "ByRef argument mismatch":
Sub KittingScan()
Dim BoxPN As String
Dim BoxQty, BoxKitQty As Long
Call GetPNQty(InputBox("Enter ID:"), BoxPN, BoxQty)
End sub
and the next one is working as well:
Sub KittingScan()
Dim BoxPN As String
Dim BoxQty As Long
Dim BoxKitQty As Long
Call GetPNQty(InputBox("Enter ID:"), BoxPN, BoxQty)
End sub
I changed a few things to work with Option Explicit, and the code ran fine against a cell containing "abc.123", which returned "abc.12,". There were no compile errors.
Option Explicit ' This is new
Public Function ProcessString(input_string As String) As String
' The temp string used throughout the function
Dim temp_string As String
Dim i As Integer ' This is new
Dim return_string As String ' This is new
For i = 1 To Len(input_string)
temp_string = Mid(input_string, i, 1)
If temp_string Like "[A-Z, a-z, 0-9, :, -]" Then
return_string = return_string & temp_string
End If
Next i
return_string = Mid(return_string, 1, (Len(return_string) - 1))
ProcessString = return_string & ", "
End Function
I'll suggest you post more of your relevant code (that calls this function). You've stated that last_name is a String, but it appears that may not be the case. Step through your code line by line and ensure that this is actually the case.
While looping through your string one character at a time is a viable method, there's no need. VBA has built-in functions for this kind of thing:
Public Function ProcessString(input_string As String) As String
ProcessString=Replace(input_string,"*","")
End Function
Something is wrong with that string try like this:
Worksheets(data_sheet).Range("C2").Value = ProcessString(CStr(last_name))
It looks like ByRef needs to know the size of the parameter. A declaration of
Dim last_name as string
doesn't specify the size of the string so it takes it as an error. Before using
Worksheets(data_sheet).Range("C2").Value = ProcessString(last_name)
The last_name has to be declared as
Dim last_name as string *10 ' size of string is up to you but must be a fix length
No need to change the function. Function doesn't take a fix length declaration.
For me the problem here was that I was declaring multiple variables in a row instead of separate rows.
For example, I was trying to pass i as an integer to my function.
Dim i,j as integer - gets me the error
Dim i as integer - doesn't get the error

multiple argument subs vba

Using VBA with Access 2010, I have a sub:
Public Sub setInterest(account As String, dmonth As Integer)
...somecode...
End Sub
And I am calling it with
setInterest("myAccount",3)
And I get syntax errors.
Modifying the sub to only take one argument and leaving out the 3 gives no errors, the problem is only when I have 2 arguments.
When using multiple arguments, you can either write:
setInterest "myAccount", 3
Or
Call setInterest("myAccount", 3)
In both examples you can name the arguments:
setInterest account:="myAccount", dmonth:= 3
I add this answer, for Why your syntax works with one argument ?
Public Sub setInterest(account As String)
'...somecode...
End Sub
setInterest ("myAccount")
Note :
When there is not any , between ( and ), VBA thinks it's a formula and exactly one argument.
When formula calculate the result will be like this:
Dim str As String
str = ("TEST")
Debug.Print str
[Output:]
TEST