curl_formadd returns `CURL_FORMADD_OPTION_TWICE` on first call - libcurl

I'm trying to construct an HTTP form using libcurl but I can't get it to work properly. Every time I call curl_formadd it returns CURL_FORMADD_OPTION_TWICE. The only information about this error indicates that libcurl thinks I'm trying to add two form elements with the same name, even though its the first call to curl_formadd and I'm only adding one element!
Declare Function curl_global_init Lib "libcurl" (flags As Integer) As Integer
Declare Function curl_formadd Lib "libcurl" (FirstItem As Ptr, LastItem As Ptr, Option1 As Integer, Value1 As Ptr, Option2 As Integer, Value2 As Ptr, EndMarker As Integer) As Integer
Const CURLFORM_COPYCONTENTS = 2
Const CURLFORM_COPYNAME = 1
Const CURLFORM_END = 17
Dim formname, formvalue As MemoryBlock
formname = "NAME"
formvalue = "CONTENTS"
If curl_global_init(3) = 0 Then
Dim first, last As Ptr
Dim err As Integer
err = curl_formadd(first, last, CURLFORM_COPYNAME, formname, CURLFORM_COPYCONTENTS, formvalue, CURLFORM_END)
Break
' err is 2 (CURL_FORMADD_OPTION_TWICE)
End If
What is this error trying to tell me?

Reading the 'man' page for curl_formadd(), it says there:
The pointers firstitem and lastitem should both be pointing to NULL in the first call to this function.
You, however, pass NULL for these.
You also seem to be passing the strings incorrectly. Try defining the Value1 and Value2 parameters "as CString", then pass normal Strings, not Memoryblocks.
Lastly, you gave CURLFORM_COPYCONTENTS the wrong code. It's not 2 but 4. See the CURLformoption enum in curl.h: "CFINIT(NOTHING)" gets value 0, and every item past that gets one higher, so CFINIT(COPYCONTENTS) gets 4.
Here's the code that works for me:
Declare Function curl_global_init Lib "libcurl" (flags As Integer) As Integer
Declare Function curl_formadd Lib "libcurl" (ByRef FirstItem As Ptr, ByRef LastItem As Ptr, Option1 As Integer, Value1 As CString, Option2 As Integer, Value2 As CString, EndMarker As Integer) As Integer
Const CURLFORM_COPYCONTENTS = 4
Const CURLFORM_COPYNAME = 1
Const CURLFORM_END = 17
const CURL_GLOBAL_ALL = 3
Dim formname, formvalue As String
formname = "NAME"
formvalue = "CONTENTS"
If curl_global_init(CURL_GLOBAL_ALL) = 0 Then
Dim first, last As Ptr
Dim err As Integer
err = curl_formadd(first, last, CURLFORM_COPYNAME, formname, CURLFORM_COPYCONTENTS, formvalue, CURLFORM_END)
Break
' err is 0
End If

Related

displaying >255 chars in excel cell

Excel shows #VALUE! when the my UDF returns more than 255 chars string.
xlwings is 0.7.1 and excel is 2007 which, as per Microsoft, can contain up to 32767 chars in a cell.
Where could be the problem?
As best I can tell, Py.CallUDF (used by xlwings udfs) returns a 2D Variant array.
It also appears that for some reason returning a Variant array with string lengths greater than 255 from a pure VBA UDF results in a #VALUE error when called in excel. Placing a watch on the array in the VBA editor shows the data is intact, it's just not getting passed to excel correctly. A little searching returned several questions around max string lengths in VBA, but nothing that specifically addressed this issue.
Returning String arrays or single Strings with > 255 characters appears to work fine though.
Here are a few pure VBA examples showing the problem:
Return Variant Array:
Function variant_long_string(n)
Dim temp(0 To 0, 0 To 0) As Variant
temp(0, 0) = String(n, "a")
variant_long_string = temp
End Function
Calling from Excel, returns (fails for N > 255):
255 aaaaaaaaaaaaa....aaaaaaaaa
256 #VALUE!
Return Element of Variant Array:
Function variant_long_string_element(n)
Dim temp(0 To 0, 0 To 0) As Variant
temp(0, 0) = String(n, "a")
variant_long_string_element = temp(0, 0)
End Function
Calling from Excel, returns (succeeds for N > 255):
255 aaaaaaaaaaaaa....aaaaaaaaa
256 aaaaaaaaaaaaa....aaaaaaaaaa
Return String Array:
Function string_long_string(n)
Dim temp(0 To 0, 0 To 0) As String
temp(0, 0) = String(n, "a")
string_long_string = temp
End Function
Calling from Excel, returns (succeeds for N > 255):
255 aaaaaaaaaaaaa....aaaaaaaaa
256 aaaaaaaaaaaaa....aaaaaaaaaa
Workaround
If your python UDF only returns a single string value, like this:
#xw.func
def build_long_string(n):
res = 'a'*int(n)
return res
xlwings will autogenerate the following VBA Macro in the xlwings_udfs module:
Function build_long_string(n)
If TypeOf Application.Caller Is Range Then On Error GoTo failed
build_long_string = Py.CallUDF(PyScriptPath, "build_long_string", Array(n), ThisWorkbook)
Exit Function
failed:
build_long_string = Err.Description
End Function
As a quick patch to get your UDF working, changing that macro slightly to this:
Function build_long_string(n)
If TypeOf Application.Caller Is Range Then On Error GoTo failed
temp = Py.CallUDF(PyScriptPath, "build_long_string", Array(n), ThisWorkbook)
build_long_string = temp(0, 0)
Exit Function
failed:
build_long_string = Err.Description
End Function
allows string >255 length to make it to Excel successfully. You could do something similar for an array result, you'd just have to convert the Variant array to a String array by looping/reassigning all the values from temp to the result.
Based on #schoolie's suggestion above of converting 2D Variant array to 2D String array, I modified the source of VBA function generation logic in my local xlwings:
In udfs.generate_vba_wrapper()
replace:
vba.write('{fname} = Py.CallUDF("{module_name}", "{fname}", {args_vba}, ThisWorkbook)\n',
module_name=module_name,
fname=fname,
args_vba=args_vba,
)
with:
vba.write('r = Py.CallUDF("{module_name}", "{fname}", {args_vba}, ThisWorkbook)\n',
module_name=module_name,
fname=fname,
args_vba=args_vba,
)
vba.write('ReDim strarray(UBound(r, 1), UBound(r, 2)) As String\n')
vba.write('For i = 0 To UBound(r, 1)\n')
vba.write(' For j = 0 To UBound(r, 2)\n')
vba.write(' strarray(i, j) = CStr(r(i, j))\n')
vba.write(' Next\n')
vba.write('Next\n')
vba.write('{fname} = strarray\n', fname=fname)
The other option is to patch the generated VB macro in VB editor after doing an 'Import Python UDFs'. However This change will be lost if you reimport. Code is already given above by #schoolie

VBA Excel DLL Argument Issue - 6th Arg

so I've got this VBA code that calls DLL code. The DLL code works fine, the VBA code works fine UNTIL I go to call the DLL function from the VBA. For some reason it's not passing the 6th argument correctly. I tested by adding a 7th argument and passing the same value in the 6th and 7th arguments - the 7th passes fine, the 6th passes the same large (incorrect) value. I have no clue what is going on.
VBA:
Option Explicit
' Declare the LMM Function that's in the DLL
Declare PtrSafe Function GenCudaLMMPaths Lib "C:\Path to DLL\LMMExcel.dll" Alias "GenerateCUDALMMPaths" (xTimes#, xRates#, xVols#, xRData#, ByRef ArrLen As Long, ByRef NPaths As Long) As Long
' Generate LMM Paths on Click
Sub LMM_Click()
Dim Times#(), Rates#(), Vols#()
Dim x As Long
Dim y As Long
Dim rTimes As Range
Dim rRates As Range
Dim rVols As Range
Dim cell As Range
Dim sz As Long
sz = 15
' Resize
ReDim Times(sz), Rates(sz), Vols(sz)
' Fill in Data
Set rTimes = Sheets("Market").Range("C2:Q2")
x = 1
For Each cell In rTimes
Times(x) = cell.Value
x = x + 1
Next
Set rRates = Sheets("Market").Range("C5:Q5")
x = 1
For Each cell In rRates
Rates(x) = cell.Value
x = x + 1
Next
Set rVols = Sheets("Market").Range("C4:Q4")
x = 1
For Each cell In rVols
Vols(x) = cell.Value / 10000
x = x + 1
Next
'Call the Function
Dim np As Long
np = Sheets("LMM").Range("C2").Value
Dim useCuda As Boolean
If Sheets("LMM").Range("C3").Value = "GPU" Then
useCuda = True
Else
useCuda = False
End If
Dim rData#()
Dim rValue
ReDim rData(np * sz * (sz + 3))
rValue = GenCudaLMMPaths(Times(1), Rates(1), Vols(1), rData(1), sz, np)
If rValue = -1 Then
'No CUDA Card
MsgBox ("Your system doesn't have a CUDA Enabled GPU")
ElseIf rValue = 1 Then
'Error Occurred
MsgBox ("An error occurred while trying to generate LMM paths")
ElseIf rValue = 0 Then
'Success
' Need to reformat return data
Dim fmtData()
ReDim fmtData(np * sz, sz)
Dim i, j, k
For i = 0 To np - 1
For j = 0 To np - 1
For k = 0 To np - 1
fmtData(((i * sz) + j) + 1, k + 1) = rData(((i * sz * sz) + (j * sz) + k) + 1)
Next k
Next j
Next i
'Fill in data
Sheets("LMM").Range("A8:K" & (np * sz)) = fmtData
Else
'Too many requested paths for this CUDA card
MsgBox ("In order to prevent GPU Lock-up, you cannot request more than " & rValue & " paths.")
Sheets("LMM").Range("C2").Value = rValue
End If
End Sub
DLL Function Declaration:
int __stdcall GenerateCUDALMMPaths(double* arrTimes, double* arrRates, double* arrVols, double* retData, int& ArrLength, int& NPaths);
DEF File:
LIBRARY "CUDAFinance"
EXPORTS
CheckExcelArray = CheckExcelArray
GenerateLMMPaths = GenerateLMMPaths
GenerateCUDALMMPaths = GenerateCUDALMMPaths
Anyone have any idea here? I'm completely lost.
I just run into the same problem and got it solved as follows.
Since you already have a long variable in the six arguments function, import the NPaths together with Arrlen as an array without adding a 7th argument:
1) In VBA:
Declare a two elements array:
Dim NArrLenNPaths(1) as long
Then, assign values:
NArrLenNPaths(0) contains ArrLen and NArrLenNPaths(1) the NPaths value.
Keep the function delcaration in VBA but when calling it put NArrLenNPaths(0) as 6th argument. Do not put a 7th argument. The C++ will retreive both values as follows.
2) In C++ use a pointer instead:
Change the 6th argument to
int* NArrLenNPaths
then retreive the values by
int NArrLen = NArrLenNPaths[0];
int NPaths = NArrLenNPaths[1];

How to emulate .net Int64 in VB6?

How can store an Int64 number in VB6, to work with Win32 functions?
Is there a way to define a type like Int64 in .net? And simply evaluate the number.
I think many of VB6 programmers need something like this,
Because some of the Win32 API's use _int64 as their parameters.
I wrote a function to cast a currency into an API compatible structure.
Put these codes in a module file.
Private Declare Sub CopyMemory lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
Private Const SIZEOF_INT64 As Long = 8
Public Type Int64 'LowPart must be the first one in LittleEndian systems
'required part
LowPart As Long
HighPart As Long
'optional part
SignBit As Byte 'define this as long if you want to get minimum CPU access time.
End Type
'with the SignBit you can emulate both Int64 and UInt64 without changing the real sign bit in HighPart.
'but if you want to change it you can access it like this mySign = (myVar.HighPart And &H80000000)
'or turn on the sign bit using myVar.HighPart = (myVar.HighPart Or &H80000000)
Public Function CInt64(ByVal vCur As Currency) As Int64
vCur = (CCur(vCur) * 0.0001#)
Call CopyMemory(CInt64, vCur, SIZEOF_INT64)
End Function
Now you can simply use CInt64 to create an Int64 number.
ex:
myRetVal = Win32APIFunctionWithOneInt64Param(CInt64(10000000))
'----OR
Dim myNum As Int64
myNum = CInt64(10000000)
And for more operations:
Public Sub Op_Ev(Dest As Int64, Src As Int64) 'for setting the value.
Call CopyMemory(Dest, Src, SIZEOF_INT64)
End Sub
Public Function Op_Eq(V1 As Int64, V2 As Int64) As Boolean 'for equal comparison.
Op_Eq = (V1.LowPart = V2.LowPart) : If Not Op_Eq Then Exit Function
Op_Eq = (V1.HighPart = V2.HighPart)
End Function
Public Function Op_Gr(V1 As Int64, V2 As Int64, Optional ByVal IsUnsignedComparison As Boolean = False) As Boolean 'for grater comparison.
If IsUnsignedComparison Then
Dim H1 As Long, H2 As Long 'don't change the location of these definitions to optimize the function to prevent to execute two or more {SUB ESP, 4}
H1 = (V1.HighPart And &H7FFFFFFF) : H2 = (V2.HighPart And &H7FFFFFFF)
Op_Gr = (H1 > H2) : If (H1 <> H2) Then Exit Function
Dim HBS1 As Long, HBS2 As Long 'don't change the type of these two vars to byte to keep alignment for local variables.
HBS1 = ((V1.HighPart And &H80000000) / &H80000000) 'export the sign bit and shift it to the right.
HBS2 = ((V2.HighPart And &H80000000) / &H80000000) 'export the sign bit and shift it to the right.
Op_Gr = (HBS1 > HBS2) : If (HBS1 <> HBS2) Then Exit Function
Else
Op_Gr = (V1.HighPart > V2.HighPart) : If (V1.HighPart <> V2.HighPart) Then Exit Function
End If
Op_Gr = (V1.LowPart > V2.LowPart)
End Function
Public Function Op_Ls(V1 As Int64, V2 As Int64, Optional ByVal IsUnsignedComparison As Boolean = False) As Boolean 'for less comparison.
If IsUnsignedComparison Then
Dim H1 As Long, H2 As Long 'don't change the location of these definitions to optimize the function to prevent to execute two or more {SUB ESP, 4}
H1 = (V1.HighPart And &H7FFFFFFF) : H2 = (V2.HighPart And &H7FFFFFFF)
Op_Ls = (H1 < H2) : If (H1 <> H2) Then Exit Function
Dim HBS1 As Long, HBS2 As Long 'don't change the type of these two vars to byte to keep alignment for local variables.
HBS1 = ((V1.HighPart And &H80000000) / &H80000000) 'export the sign bit and shift it to the right.
HBS2 = ((V2.HighPart And &H80000000) / &H80000000) 'export the sign bit and shift it to the right.
Op_Ls = (HBS1 < HBS2) : If (HBS1 <> HBS2) Then Exit Function
Else
Op_Ls = (V1.HighPart < V2.HighPart) : If (V1.HighPart <> V2.HighPart) Then Exit Function
End If
Op_Ls = (V1.LowPart < V2.LowPart)
End Function
Public Function Op_Cmp(V1 As Int64, V2 As Int64, Optional ByVal IsUnsignedComparison As Boolean = False) As Long 'for comparison.
If Op_Gr(V1, V2, IsUnsignedComparison) Then
Op_Cmp = 1
ElseIf Op_Ls(V1, V2, IsUnsignedComparison) Then
Op_Cmp = -1
Else
Op_Cmp = 0
End If
End Function

Find index of string in large file performance

I have a "container" containing data. The size is +- 100MB.
In the container there a several "dataids's" that mark the begin of something.
Now I need to get an index for an given dataid. (dataid for example: '4CFE7197-0029-006B-1AD4-000000000012')
I have tried several approaches. But at this moment "ReadAllBytes" is the most performant.
ReadAll -> average of 0.6 seconds
Using oReader As New BinaryReader(File.Open(sContainerPath, FileMode.Open, FileAccess.Read))
Dim iLength As Integer = CInt(oReader.BaseStream.Length)
Dim oValue As Byte() = Nothing
oValue = oReader.ReadBytes(iLength)
Dim enc As New System.Text.ASCIIEncoding
Dim sFileContent As String = enc.GetString(oValue)
Dim r As Regex = New Regex(sDataId)
Dim lPosArcID As Integer = r.Match(sFileContent).Index
If lPosArcID > 0 Then
Return lPosArcID
End If
End Using
ReadByteByByte -> average of 1.4 seconds
Using oReader As BinaryReader = New BinaryReader(File.Open(sContainerPath, FileMode.Open, FileAccess.Read))
Dim valueSearch As StringSearch = New StringSearch(sDataId)
Dim readByte As Byte
While (InlineAssignHelper(readByte, oReader.ReadByte()) >= 0)
index += 1
If valueSearch.Found(readByte) Then
Return index - iDataIdLength
End If
End While
End Using
Public Class StringSearch
Private ReadOnly oValue() As Byte
Private iValueIndex As Integer = -1
Public Sub New(value As String)
Dim oEncoding As New System.Text.ASCIIEncoding
Me.oValue = oEncoding.GetBytes(value)
End Sub
Public Function Found(oNextByte As Byte) As Boolean
If oValue(iValueIndex + 1) = oNextByte Then
iValueIndex += 1
If iValueIndex + 1 = oValue.Count Then Return True
Else
iValueIndex = -1
End If
Return False
End Function
End Class
Public Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
target = value
Return value
End Function
I find it hard to believe that there is no faster way.
0.6 seconds for a 100MB file is not an acceptable time.
An other approach that I tried, is to split in chuncks of X bytes (100, 1000, ..). But was alot slower.
Any help on an approach I can try?

getting error on building a FORTRAN program

this is my one subroutine in fortran program
subroutine selfile(name)
! call Window dialog to select file
use dfwin
type T_OPENFILENAME
sequence
real lStructSize,hwndOwner,hInstance,lpstrFilter,lpstrCustomFilter,nMaxCustFilter,nFilterIndex,lpstrFile,nMaxFile,nMaxFileTitle
real lpstrInitialDir,lpstrTitle,Flags,lpstrDefExt,lpfnHook,lpTemplateName
end type T_OPENFILENAME
type(T_OPENFILENAME):: ofn
character*100 filter_spec
character*512 file_spec
integer status
character*(*)name
! set filter specification and string to return the file specification.
file_spec=''C
filter_spec = 'Data Files'C//'*.dat'C// &
'Text Files'C//'*.txt'C// &
'All files'C//'*'C//''C
ofn%lStructSize = SIZEOF(ofn)
ofn%hwndOwner = NULL
ofn%hInstance = NULL
ofn%lpstrFilter = loc(filter_spec)
ofn%lpstrCustomFilter = NULL
ofn%nMaxCustFilter = 0
ofn%nFilterIndex = 1
ofn%lpstrFile = loc(file_spec)
ofn%nMaxFile = sizeof(file_spec)
ofn%nMaxFileTitle = 0
ofn%lpstrInitialDir = NULL
ofn%lpstrTitle = loc('D Y N S I M'C)
ofn%Flags = OFN_PATHMUSTEXIST
ofn%lpstrDefExt = loc('dat'C)
ofn%lpfnHook = NULL
ofn%lpTemplateName = NULL
end
! Call GetOpenFileName and check status
status = GetOpenFileName(ofn)
if (status == 0) then
name=''
else
name=file_spec
endif
end subroutine selfile
but i am getting error like..
Illegal use of constant "D Y N S I M"
Illegal number or type of arguments to loc
Illegal use of constant "dat"
Unmatched ENDSUBROUTINE statement
The loc() function is nonstandard, but a short search tells me that its argument can not be a literal constant.