How to convert FILETIME from WinApi to DateTime in vb6? - winapi

How to convert FILETIME from WinApi (e.g. from result of a call to this WINAPI function to DateTime in vb6? (e.g. if I want to use it as input to the DateTime.DateDiff function.)

Public Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type
Public Type SYSTEMTIME
wYear As Integer
wMonth As Integer
wDayOfWeek As Integer
wDay As Integer
wHour As Integer
wMinute As Integer
wSecond As Integer
wMilliseconds As Integer
End Type
Private Declare Function FileTimeToSystemTime Lib "kernel32" (lpFileTime As FILETIME, lpSystemTime As SYSTEMTIME) As Long
Private Declare Function SystemTimeToVariantTime Lib "OLEAUT32.DLL" (lpSystemTime As SYSTEMTIME, vtime As Date) As Long
Dim st As SYSTEMTIME
Dim dt As Date
' convert a FILETIME to SYSTEMTIME first
FileTimeToSystemTime ft, st
' convert the SYSTEMTIME to a Variant date (VT_DATE)
SystemTimeToVariantTime st, dt

Related

Converting __int64 to FileTime

I have a code which is getting SystemTime as FileTime using
uint64 currentUtcFileTime;
GetSystemTimeAsFileTime((FILETIME*)&currentUtcFileTime);
Now I want to convert this uint64 back to FILETIME.
I searched for long and finally got this solution -
FILETIME ftime;
__int64 i64;
*(__int64 *)&ftime = i64;
It worked well for me :)

How to make WinHttpCrackUrl work in 64-bit

I have Visual Basic for Applications code that uses WinHttp and works flawlessly with 32-bit Office 2010 running on 32-bit Windows XP. The same code fails to run properly on 64-bit Office 2013 on 64-bit Windows 8, even though it compiles fine.
The problem is that WinHttpCrackUrl() returns an error 87 "The parameter is incorrect" on Windows 8.
I have double-checked and triple-checked that all pointers are declared as LongPtr in the code where appropriate. What am I doing wrong?
Here is the code that runs fine on 32-bit Excel/Windows, but fails to run on 64-bit Excel/Windows:
Private Type URL_COMPONENTS
dwStructSize As Long
lpszScheme As LongPtr
dwSchemeLength As Long
nScheme As Long
lpszHostName As LongPtr
dwHostNameLength As Long
nPort As Long
lpszUserName As LongPtr
dwUserNameLength As Long
lpszPassword As LongPtr
dwPasswordLength As Long
lpszUrlPath As LongPtr
dwUrlPathLength As Long
lpszExtraInfo As LongPtr
dwExtraInfoLength As Long
End Type
Private Declare PtrSafe Function WinHttpCrackUrl Lib "WinHTTP" ( _
ByVal pwszUrl As LongPtr, _
ByVal dwUrlLength As Long, _
ByVal dwFlags As Long, _
ByRef lpUrlComponents As URL_COMPONENTS) As Long
Sub Test()
Dim result as Long
Dim URLComp As URL_COMPONENTS
Dim mURL as String
mURL = "http://www.stackoverflow.com" & vbNullChar
With URLComp
.dwStructSize = Len(URLComp)
.dwHostNameLength = -1
.dwSchemeLength = -1
.dwUrlPathLength = -1
End With
result = WinHttpCrackUrl(StrPtr(mURL), 0, 0, URLComp)
' Prints 1 on 32-bit Excel/Windows (indicating success)
' Prints 0 on 64-bit Excel/Windows (indicating failure)
Debug.Print result
' Prints 87 on 64-bit Excel/Windows ("The parameter is incorrect.")
Debug.Print err.LastDllError
End Sub
The struct is aligned in the C++ code, but VBA structs are packed. In 32 bit, for your struct, it does not matter since all members have alignment 4. But in 64 bit the pointers need 8 byte alignment and the struct has some extra padding. Put it in like this:
Private Type URL_COMPONENTS
dwStructSize As Long
padding1 As Long
lpszScheme As LongPtr
dwSchemeLength As Long
nScheme As Long
lpszHostName As LongPtr
dwHostNameLength As Long
nPort As Long
lpszUserName As LongPtr
dwUserNameLength As Long
padding2 As Long
lpszPassword As LongPtr
dwPasswordLength As Long
padding3 As Long
lpszUrlPath As LongPtr
dwUrlPathLength As Long
padding4 As Long
lpszExtraInfo As LongPtr
dwExtraInfoLength As Long
padding5 As Long
End Type
I guess you'll want some conditional compilation to switch better 32 and 64 bit versions but I must confess to having no idea how to do that with VBA.

Why FindFirstFileW always return -1?

FindFirstFileA (ANSI) works as it should, while FindFirstFileW (Unicode) not.
The Wide version always return -1 (INVALID_HANDLE_VALUE), but why?
'Declarations:
Option Explicit
Private Const MAX_PATH As Long = 260
Private Const ALTERNATE As Long = 14
Private Const INVALID_HANDLE_VALUE As Long = -1
Private Type FILETIME
dwLowDateTime As Long
dwHighDateTime As Long
End Type
Private Type WIN32_FIND_DATA
dwFileAttributes As Long
ftCreationTime As FILETIME
ftLastAccessTime As FILETIME
ftLastWriteTime As FILETIME
nFileSizeHigh As Long
nFileSizeLow As Long
dwReserved0 As Long
dwReserved1 As Long
cFileName As String * MAX_PATH
cAlternate As String * ALTERNATE
End Type
Private Declare Function FindFirstFileW Lib "kernel32" _
(ByVal lpFileName As String, _
ByRef lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function FindFirstFileA Lib "kernel32" _
(ByVal lpFileName As String, _
ByRef lpFindFileData As WIN32_FIND_DATA) As Long
Private Declare Function FindClose Lib "kernel32" _
(ByVal hFindFile As Long) As Long
Public Declare Function GetLastError Lib "kernel32" () As Integer
'Test Functions:
Public Function TestA(ByVal sf As String) As Long
If Len(sf) < 3 Then Exit Function
Dim wfd As WIN32_FIND_DATA
TestA = FindFirstFileA(sf, wfd)
'Debug.Print "AE:" & GetLastError()
If TestA <> INVALID_HANDLE_VALUE Then FindClose TestA
End Function
Public Function TestW(ByVal sf As String) As Long
If Len(sf) < 3 Then Exit Function
Dim wfd As WIN32_FIND_DATA
TestW = FindFirstFileW(sf, wfd)
'Debug.Print "WE:" & GetLastError()
If TestW <> INVALID_HANDLE_VALUE Then FindClose TestW
End Function
'Example test
Private Sub Command1_Click()
Dim sDir As String
sDir = "C:\new"
MsgBox "A: " & TestA(sDir) & vbCrLf _
& "W: " & TestW(sDir)
End Sub
P.S. Thanks to Carey Gregory, but I still need a bit of help to translate Wide version. What's the equivalent of C/C++ WCHAR in VB6?
//WinBase.h
typedef struct _WIN32_FIND_DATAA {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
CHAR cFileName[ MAX_PATH ];
CHAR cAlternateFileName[ 14 ];
#ifdef _MAC
DWORD dwFileType;
DWORD dwCreatorType;
WORD wFinderFlags;
#endif
} WIN32_FIND_DATAA, *PWIN32_FIND_DATAA, *LPWIN32_FIND_DATAA;
typedef struct _WIN32_FIND_DATAW {
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
WCHAR cFileName[ MAX_PATH ]; //WCHAR in VB6?
WCHAR cAlternateFileName[ 14 ]; //WCHAR in VB6?
#ifdef _MAC
DWORD dwFileType;
DWORD dwCreatorType;
WORD wFinderFlags;
#endif
} WIN32_FIND_DATAW, *PWIN32_FIND_DATAW, *LPWIN32_FIND_DATAW;
WIN32_FIND_DATA has two versions just like the functions that use them: an ANSI version and a Unicode version. You need to define WIN32_FIND_DATAA and WIN32_FIND_DATAW and use them appropriately with the 'A' and 'W' functions.
Well, after re-reading this article I found my mistake. It's not about separate WIN32_FIND_DATAW type, but need to declare FindFirstFileW arguments as Long and ByVal.
Private Declare Function FindFirstFileW Lib "kernel32" _
(ByVal lpFileName As Long, _
ByVal lpFindFileData As Long) As Long
And also need to call them with StrPtr and VarPtr.
TestW = FindFirstFileW(StrPtr(sf), VarPtr(wfd))

GetConsoleScreenBufferInfoEx fails due To an invalid parameter

I am trying to call the GetConsoleScreenBufferInfoEx function from a console application. If it matters, the application is a 32 bit application running on 64 bit Windows 7. The language is RealBasic.
I believe I have defined all the structures correctly, and the buffer output handle works for every other API function that is being called:
Declare Function GetConsoleScreenBufferInfoEx Lib "Kernel32" (cHandle As Integer, ByRef info As CONSOLE_SCREEN_BUFFER_INFOEX) As Boolean
Declare Function GetLastError Lib "Kernel32" () As Integer
Declare Function GetStdHandle Lib "Kernel32" (hIOStreamType As Integer) As Integer
Const STD_OUTPUT_HANDLE = -11
Dim stdHandle As Integer = GetStdHandle(STD_OUTPUT_HANDLE)
Dim err As Integer
Dim info As CONSOLE_SCREEN_BUFFER_INFOEX
If GetConsoleScreenBufferInfoEx(stdHandle, info) Then
Break
Else
err = GetLastError //Always 87, Invalid parameter
Break
End If
Structures:
Structure CONSOLE_SCREEN_BUFFER_INFOEX
cbSize As Integer
dwSize As COORD
CursorPosition As COORD
Attribute As UInt16
srWindow As SMALL_RECT
MaxWindowSize As COORD
PopupAttributes As UInt16
FullScreenSupported As Boolean
ColorTable(15) As UInt32
Structure COORD
X As UInt16
Y As UInt16
Structure SMALL_RECT
Left As UInt16
Top As UInt16
Right As UInt16
Bottom As UInt16
I've gone over this 20 times and nothing looks wrong to me. I've used the COORD and SMALL_RECT structures many times before, so I don't think I made any translation errors on them. The CONSOLE_SCREEN_BUFFER_INFOEX structure, however, is seeing its first use by me here, and I sense that the error lies somewhere in my translation of it.
You need to set the cbSize parameter of the CONSOLE_SCREEN_BUFFER_INFOEX before you send it in. GetConsoleScreenBufferInfoEx will check that it is the correct size and that's why it's returning an invalid parameter.
So before the call to GetConsoleScreenBufferInfoEx add:
info.cbSize = 96
Or better yet Real Basic does allow you to access the size of the structure:
info.cbSize = GetConsoleScreenBufferInfoEx.Size
Which should handle the calculation for you.

Calling a simple VC DLL from VB6

I have a simple DLL written with VC6 with one function:
__declspec(dllexport) int myfunc(long a, unsigned char *b, unsigned char *c, unsigned char *d, unsigned char *e)
And im calling it from vb6 using:
Declare Function myfunc Lib "mylib.dll" (ByVal a As Long, ByVal b As String, ByVal c As String, ByVal d As String, ByVal e As String) As Long
....
dim a as long
dim b as string
dim c as string
dim d as string
dim e as string
dim r as long
r=myfunc(a,b,c,d,e)
Im getting "bad dll calling convention" error but I cant figure out why. Any ideas?
Generally speaking, 'bad DLL...' means what it says. VB6 requires the _stdcall convention (like the Win API) for any external functions it calls.
Try adding __stdcall to the C function prototype and see what happens.
Check out the Universal DLL function caller, by Paul Caton:
http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=70195&lngWId=1
It will allow you to call pretty much any type of function from VB6.

Resources