VB Script to create guid based on creted date time - vba

I have the following VB Scrtip that I use in excel to generate a GUID. Ideally I want the GUID to be based on date time created not just randomly. Is there anyway to amend this script to build the GUID based on date and time?
Private Type GUID
Data1 As Long
Data2 As Long
Data3 As Long
Data4(8) As Byte
Private Declare Function CoCreateGuid _
Lib "ole32.dll" _
(ByRef pGUID As GUID) As Long
Private Declare Function StringFromGUID2 _
Lib "ole32.dll" _
(ByRef rGUID As Any, _
ByVal lpstrCLSID As Long, _
ByVal cbMax As Long) As Long
Function CreateGUID() As String
Dim b() As Byte
Dim BuffSize As Long
Dim RetVal As Long
Dim MyGUID As GUID
BuffSize = 40
ReDim b(BuffSize * 2) As Byte
RetVal = CoCreateGuid(MyGUID)
RetVal = StringFromGUID2(MyGUID, VarPtr(b(0)), BuffSize)
CreateGUID = Left$(b, RetVal - 1)
End Function
Many thanks

GUIDs do normally have a date/time component, among other data. If you want them not to be GUIDs, you can make up any string you want, but you need to come up with your own method of avoiding collisions, especially with real GUIDs.

Related

Initialise a new VARIANT from a void* pointer + a datatype

I am using these two functions to read a value from a multidimensional safearray
Private Declare Sub SafeArrayGetElement Lib "OleAut32.dll" ( _
ByVal pSafeArray As LongPtr, _
ByRef rgIndices As Long, _
ByRef dataBuffer As Any)
Private Declare Function SafeArrayGetElemsize Lib "OleAut32.dll" (ByVal pSafeArray As LongPtr) As Long
SafeArrayGetElement
SafeArrayGetElemsize
This is an example of how I use them:
Dim arr(1 to 2, 1 to 4) As Long 'or Integer, String, Object etc. Any datatype
Dim indices() As Long
ReDim indices(1 to CountOfDimensons(arr)) 'here arr is a 2D array so there are two indices
indices(1) = 1
indices(2) = 3
Dim resultBuffer() As Byte 'somewhere to read the data from the array into, big enough to hold whatever that is
ReDim resultBuffer(1 to SafeArrayGetElemsize(ArrPtr(arr)))
SafeArrayGetElement ArrPtr(arr), indices(1), resultBuffer(1) 'equivalent to resultBuffer = arr(1,3)
Now I have a resultBuffer with some data, and I can use VarType(arr) xor vbArray to get the vartype of the data. It could be Long, Integer, Variant etc...
How can I use those two pieces of information to create a variant? My idea was to have a big select case statement on the varType, declare variables of each type. Then use memcopy to hydrate those variables. Then assign their values to a new variant. However this is very over complicated. There must be an easier way to initialise a variant from some data.
Basically cast the void* to a long* or variant* etc. and then copy that into a Variant?

Using TaskDialogIndirect in 64-Bit VBA

Problem description
I try to get code working under 64-Bit VBA which works fine in 32-Bit VBA.
It is regarding Common Controls TaskDialogs.
I use Microsoft Access, but the problem should be the same in other VBA hosts.
One part works fine in both (32- and 64-Bit) VBA, the other part doesn't.
TaskDialog API working well in both (32- and 64-Bit) VBA
You can start the procedure TestTaskDlg for a test.
Option Explicit
'Original API definition:
'------------------------
'HRESULT TaskDialog(
' HWND hwndOwner,
' HINSTANCE hInstance,
' PCWSTR pszWindowTitle,
' PCWSTR pszMainInstruction,
' PCWSTR pszContent,
' TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons,
' PCWSTR pszIcon,
' int *pnButton
');
Private Declare PtrSafe Function TaskDialog Lib "Comctl32.dll" _
(ByVal hWndParent As LongPtr, _
ByVal hInstance As LongPtr, _
ByVal pszWindowTitle As LongPtr, _
ByVal pszMainInstruction As LongPtr, _
ByVal pszContent As LongPtr, _
ByVal dwCommonButtons As Long, _
ByVal pszIcon As LongPtr, _
ByRef pnButton As Long _
) As Long
'Works fine with 32-Bit VBA and 64-Bit VBA:
Public Sub TestTaskDlg()
Debug.Print TaskDlg("Title", "MainInstructionText", "ContentText")
End Sub
Public Function TaskDlg( _
sWindowTitle As String, _
sMainInstruction As String, _
sContent As String _
) As Long
On Local Error GoTo Catch
Dim clickedButton As Long
TaskDlg = TaskDialog(0, _
0, _
StrPtr(sWindowTitle), _
StrPtr(sMainInstruction), _
StrPtr(sContent), _
0, _
0, _
clickedButton)
Debug.Print "Clicked button:", clickedButton
Done:
Exit Function
Catch:
MsgBox Err.Description, , Err.Number
Resume Done
End Function
TaskDialogIndirect API working well only in 32-Bit VBA
You can start the procedure TestTaskDlgIndirect for a test.
In 64-Bit VBA it returns E_INVALIDARG (0x80070057 | -2147024809), pointing to invalid arguments somehow...
If I use Len() instead of LenB() and comment this three lines of code, it shows a proper (empty) dialog, so the call of TaskDialogIndirect should be correct.
tdlgConfig.pszWindowTitle = StrPtr(sWindowTitle)
tdlgConfig.pszMainInstruction = StrPtr(sMainInstruction)
tdlgConfig.pszContent = StrPtr(sContent)
Does anybody have an idea why it is not working in 64-bit VBA?
In my opinion I already converted the types from Long to LongPtr properly.
I expect it is a problem with the values/pointers which will be stored in the structure at runtime.
Maybe some Hi-/Low-Byte stuff?
Any help appreciated. :-)
Option Explicit
'Original API definition:
'------------------------
'typedef struct _TASKDIALOGCONFIG {
' UINT cbSize;
' HWND hwndParent;
' HINSTANCE hInstance;
' TASKDIALOG_FLAGS dwFlags;
' TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons;
' PCWSTR pszWindowTitle;
' union {
' HICON hMainIcon;
' PCWSTR pszMainIcon;
' } DUMMYUNIONNAME;
' PCWSTR pszMainInstruction;
' PCWSTR pszContent;
' UINT cButtons;
' const TASKDIALOG_BUTTON *pButtons;
' int nDefaultButton;
' UINT cRadioButtons;
' const TASKDIALOG_BUTTON *pRadioButtons;
' int nDefaultRadioButton;
' PCWSTR pszVerificationText;
' PCWSTR pszExpandedInformation;
' PCWSTR pszExpandedControlText;
' PCWSTR pszCollapsedControlText;
' union {
' HICON hFooterIcon;
' PCWSTR pszFooterIcon;
' } DUMMYUNIONNAME2;
' PCWSTR pszFooter;
' PFTASKDIALOGCALLBACK pfCallback;
' LONG_PTR lpCallbackData;
' UINT cxWidth;
'} TASKDIALOGCONFIG;
Public Type TASKDIALOGCONFIG
cbSize As Long 'UINT
hWndParent As LongPtr 'HWND
hInstance As LongPtr 'HINSTANCE
dwFlags As Long 'TASKDIALOG_FLAGS
dwCommonButtons As Long 'TASKDIALOG_COMMON_BUTTON_FLAGS
pszWindowTitle As LongPtr 'PCWSTR
' Union
' {
hMainIcon As LongPtr 'Union means that the biggest type has to be declared: So LongPtr
' hMainIcon 'HICON
' pszMainIcon 'PCWSTR
' };
pszMainInstruction As LongPtr 'PCWSTR
pszContent As LongPtr 'PCWSTR
cButtons As Long 'UINT
pButtons As LongPtr 'TASKDIALOG_BUTTON *pButtons;
nDefaultButton As Long 'INT
cRadioButtons As Long 'UINT
pRadioButtons As LongPtr 'TASKDIALOG_BUTTON *pRadioButtons;
nDefaultRadioButton As Long 'INT
pszVerificationText As LongPtr 'PCWSTR
pszExpandedInformation As LongPtr 'PCWSTR
pszExpandedControlText As LongPtr 'PCWSTR
pszCollapsedControlText As LongPtr 'PCWSTR
'Union
'{
hFooterIcon As LongPtr 'Union means that the biggest type has to be declared: So LongPtr
' hFooterIcon 'HICON
' pszFooterIcon 'PCWSTR
'};
pszFooter As LongPtr 'PCWSTR
pfCallback As LongPtr 'PFTASKDIALOGCALLBACK
lpCallbackData As LongPtr 'LONG_PTR
cxWidth As Long 'UINT
End Type
'Original API definition:
'------------------------
'HRESULT TaskDialogIndirect(
' const TASKDIALOGCONFIG *pTaskConfig,
' int *pnButton,
' int *pnRadioButton,
' BOOL *pfVerificationFlagChecked
');
Private Declare PtrSafe Function TaskDialogIndirect Lib "Comctl32.dll" ( _
ByRef pTaskConfig As TASKDIALOGCONFIG, _
ByRef pnButton As Long, _
ByRef pnRadioButton As Long, _
ByRef pfVerificationFlagChecked As Long _
) As Long
'Works fine with 32-Bit VBA. But with 64-Bit VBA it returns E_INVALIDARG (0x80070057 | -2147024809)
Public Sub TestTaskDlgIndirect()
Debug.Print TaskDlgIndirect("Title", "MainInstructionText", "ContentText")
End Sub
Public Function TaskDlgIndirect( _
sWindowTitle As String, _
sMainInstruction As String, _
sContent As String _
) As Long
On Local Error GoTo Catch
Dim tdlgConfig As TASKDIALOGCONFIG
tdlgConfig.cbSize = LenB(tdlgConfig)
'Usually LenB() should be the right way to use, but when I use Len() and comment the three texts below, it shows a proper empty dialog!
tdlgConfig.pszWindowTitle = StrPtr(sWindowTitle)
tdlgConfig.pszMainInstruction = StrPtr(sMainInstruction)
tdlgConfig.pszContent = StrPtr(sContent)
Dim clickedButton As Long
Dim selectedRadio As Long
Dim verificationFlagChecked As Long
TaskDlgIndirect = TaskDialogIndirect(tdlgConfig, clickedButton, _
selectedRadio, verificationFlagChecked)
Debug.Print "Clicked button:", clickedButton
Done:
Exit Function
Catch:
MsgBox Err.Description, , Err.Number
Resume Done
End Function
Update
Some new insights:
It seems that TASKDIALOGCONFIG uses a 1-byte packing internally.
In 32-bit VBA (which uses 4-byte padding for structs) this didn't matter because all members of the struct were of type Long and so 4 byte, so no padding occured at all.
Also in this constellation there is no difference in using Len(tdlgConfig), which calculates the sum of the datatypes only, and LenB(tdlgConfig), which calculates the real size of the struct indeed.
Both result in 96 bytes here.
But in 64-bit VBA (which uses 8-byte padding for structs) some members of the struct are of type Long (4 byte) and some are LongLong (8 byte) (declared as LongPtr for 32-bit compatibility).
This results to VBA applies padding and that is the reason why Len(tdlgConfig) returns 160 and LenB(tdlgConfig) 176.
So because my test without providing any texts (commenting the mentioned 3 lines of code) displays a dialog only when I use Len(tdlgConfig) (instead of LenB(tdlgConfig)) leads to the same conclusion, that the 64-bit API expects a structure of 160 bytes only.
So to provide a struct of 160 bytes I used this for a test:
Public Type TASKDIALOGCONFIG
cbSize As Long
dummy2 As Long
dummy3 As Long
dummy4 As Long
dummy5 As Long
dummy6 As Long
dwCommonButtons As Long
dummy8 As Long
dummy9 As Long
dummy10 As Long
dummy11 As Long
dummy12 As Long
dummy13 As Long
dummy14 As Long
dummy15 As Long
dummy16 As Long
dummy17 As Long
dummy18 As Long
nDefaultButton As Long
dummy20 As Long
dummy21 As Long
dummy22 As Long
dummy23 As Long
dummy24 As Long
dummy25 As Long
dummy26 As Long
dummy27 As Long
dummy28 As Long
dummy29 As Long
dummy30 As Long
dummy31 As Long
dummy32 As Long
dummy33 As Long
dummy34 As Long
dummy35 As Long
dummy36 As Long
dummy37 As Long
dummy38 As Long
dummy39 As Long
dummy40 As Long
End Type
Now both, Len(tdlgConfig) and LenB(tdlgConfig) return 160.
Calling the empty dialog without texts still runs well.
And I now can set dwCommonButtons and nDefaultButton (both type Long) and it works correct so far.
For example:
Public Enum TD_COMMON_BUTTON_FLAGS
TDCBF_OK_BUTTON = &H1& '// Selected control returns value IDOK
TDCBF_YES_BUTTON = &H2& '// Selected control returns value IDYES
TDCBF_NO_BUTTON = &H4& '// Selected control returns value IDNO
TDCBF_CANCEL_BUTTON = &H8& '// Selected control returns value IDCANCEL
TDCBF_RETRY_BUTTON = &H10& '// Selected control returns value IDRETRY
TDCBF_CLOSE_BUTTON = &H20& '// Selected control returns value IDCLOSE
End Enum
'typedef DWORD TASKDIALOG_COMMON_BUTTON_FLAGS; // Note: _TASKDIALOG_COMMON_BUTTON_FLAGS is an int
Public Enum TD_COMMON_BUTTON_RETURN_CODES
IDOK = 1
IDCANCEL = 2
IDRETRY = 4
IDYES = 6
IDNO = 7
IDCLOSE = 8
End Enum
tdlgConfig.dwCommonButtons = TDCBF_YES_BUTTON Or TDCBF_NO_BUTTON
tdlgConfig.nDefaultButton = IDNO
So I can expect the size of the struct is fine and now I have to find out how to set the LongLong (LongPtr) types...
Finally I got it working to set the icon to be used and a string in the struct in 64-Bit VBA.
This is the new struct, where I named the members for the main icon and the main instruction text additionally:
Public Type TASKDIALOGCONFIG
cbSize As Long
dummy2 As Long
dummy3 As Long
dummy4 As Long
dummy5 As Long
dummy6 As Long
dwCommonButtons As Long
dummy8 As Long
dummy9 As Long
hMainIcon1 As Long
hMainIcon2 As Long
pszMainInstruction1 As Long
pszMainInstruction2 As Long
dummy14 As Long
dummy15 As Long
dummy16 As Long
dummy17 As Long
dummy18 As Long
nDefaultButton As Long
dummy20 As Long
dummy21 As Long
dummy22 As Long
dummy23 As Long
dummy24 As Long
dummy25 As Long
dummy26 As Long
dummy27 As Long
dummy28 As Long
dummy29 As Long
dummy30 As Long
dummy31 As Long
dummy32 As Long
dummy33 As Long
dummy34 As Long
dummy35 As Long
dummy36 As Long
dummy37 As Long
dummy38 As Long
dummy39 As Long
dummy40 As Long
End Type
Because the LongLong values in the struct now all are split into separate Long values, I couldn't set them in a common way.
With some try and error I found a way to set the icon. It is enough to set the first Long value in the same way it has to be done in 32-Bit VBA:
Const TD_SECURITY_ICON_OK As Integer = -8
tdlgConfig.hMainIcon1 = &HFFFF And TD_SECURITY_ICON_OK
Setting the pointer to a string also was a bit tricky. I finally declare the CopyMemory API sub...
Private Declare PtrSafe Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" ( _
ByVal destination As LongPtr, _
ByVal source As LongPtr, _
ByVal dataLength As LongPtr)
...and use it like this to set a string reference in the struct:
CopyMemory VarPtr(tdlgConfig.pszMainInstruction1), VarPtr(StrPtr("My main instruction")), 8
Finally I can use the function TaskDialogIndirect like this:
Dim clickedButton As Long
Dim selectedRadio As Long
Dim verificationFlagChecked As Long
Call TaskDialogIndirect(tdlgConfig, clickedButton, _
selectedRadio, verificationFlagChecked)
Debug.Print "Clicked button:", clickedButton
The rest is pure diligence to set the other texts etc. and make the code executable for 32-bit and 64-bit using case distinctions.
Thanks again to GSerg for replying.
This is an old thread at this point but since I was just making a TaskDialogIndirect class I wanted to be compatible with VBA7x64, I came across it and saw there's a lot of misunderstandings that were never cleared up. I've had a hell of a time in the past year dealing with all sorts of packing/alignment issues as I move code to 64bit, so thought I good explainer was in order for anyone else who stumbles upon this question.
VBA will pad the structure under x64. That's the correct behavior-- not because every API expects unpadded structures, as GSerg suggested, but because this API does. If you look in the SDK header where these things are defined, CommCtrl.h, right before the Task Dialog definitions, you'll see this:
#include <pshpack1.h>
Then after the Task Dialogs,
#include <poppack.h>
What these headers do is adjust the alignment. pshpack1 means no packing is applied from the point where it's included to where poppack restores the default native packing rules. So this API, unlike most APIs, requires an unpadded structure. It's fairly uncommon for this to be the case; I don't know why it is here, but it is.
VBA does not provide any option to not pad a structure. So that means using an 8 byte data type is not going to work. But the API interprets the structure according to how it thinks the memory is laid out.
As to the link to the URL_COMPONENTS, I don't know what else was done, maybe it's actually 32bit Office and the structure is passed through without WOW64 converting it (like the event trace API), but you can verify the LenB and offsets are all the same with or without those padding members.
I found the easiest way to implement this API was to just declare
#If VBA7 Then
#If (Win64 <> 0) And (TWINBASIC = 0) Then
Private Type TASKDIALOG_BUTTON_VBA7
data(11) As byte
End Type
Private Type TASKDIALOGCONFIG_VBA7
data(159) As Byte
End Type
Private m_uButtons_VBA7() As TASKDIALOG_BUTTON_VBA7
Private m_uRadioButtons_VBA7() As TASKDIALOG_BUTTON_VBA7
Private uTDC_VBA7 As TASKDIALOGCONFIG_VBA7
Those are the correct, packing free sizes.
The normal structure still there for all other modes (twinBASIC is a 100% compatible successor to VB6/VBA supporting building 64bit exes using VBA7 syntax), right before calling the API, I copy all of the regular structure to their correct offsets, including the button arrays:
#If (VBA7 <> 0) And (TWINBASIC = 0) And (Win64 <> 0) Then
'Special handling for 64bit VBA7, which doesn't support our manually aligned structure.
ReDim m_uButtons_VBA7(uTDC.cButtons)
Dim i As Long
If uTDC.cButtons Then
For i = 0 to uTDC.cButtons - 1
CopyMemory m_uButtons_VBA7(i).data(0), m_uButtons(i).nButtonID, 4
CopyMemory m_uButtons_VBA7(i).data(4), m_uButtons(i).pszButtonText, 8
next i
End If
ReDim m_uRadioButtons_VBA7(uTDC.cRadioButtons)
If uTDC.cRadioButtons Then
For i = 0 to uTDC.cRadioButtons - 1
CopyMemory m_uRadioButtons_VBA7(i).data(0), m_uRadioButtons(i).nButtonID, 4
CopyMemory m_uRadioButtons_VBA7(i).data(4), m_uRadioButtons(i).pszButtonText, 8
next i
End If
Dim ptrBtn As LongPtr, ptrRbn As LongPtr
ptrBtn = VarPtr(m_uButtons_VBA7): ptrRbn = VarPtr(m_uRadioButtons_VBA7)
CopyMemory uTDC_VBA7.data(0), uTDC.cbSize, 4: CopyMemory uTDC_VBA7.data(4), uTDC.hWndParent, 8: CopyMemory uTDC_VBA7.data(12), uTDC.hInstance, 8
CopyMemory uTDC_VBA7.data(16), uTDC.dwFlags, 4: CopyMemory uTDC_VBA7.data(20), uTDC.dwCommonButtons, 4: CopyMemory uTDC_VBA7.data(24), uTDC.pszWindowTitle, 8
CopyMemory uTDC_VBA7.data(32), uTDC.pszMainIcon, 8: CopyMemory uTDC_VBA7.data(40), uTDC.pszMainInstruction, 8: CopyMemory uTDC_VBA7.data(48), uTDC.pszContent, 8
CopyMemory uTDC_VBA7.data(56), uTDC.cButtons, 4: CopyMemory uTDC_VBA7.data(60), ptrBtn, 8: CopyMemory uTDC_VBA7.data(68), uTDC.nDefaultButton, 4
CopyMemory uTDC_VBA7.data(72), uTDC.cRadioButtons, 4: CopyMemory uTDC_VBA7.data(76), ptrRbn, 8: CopyMemory uTDC_VBA7.data(84), uTDC.nDefaultRadioButton, 4
CopyMemory uTDC_VBA7.data(88), uTDC.pszVerificationText, 8: CopyMemory uTDC_VBA7.data(96), uTDC.pszExpandedInformation, 8: CopyMemory uTDC_VBA7.data(104), uTDC.pszExpandedControlText, 8
CopyMemory uTDC_VBA7.data(112), uTDC.pszCollapsedControlText, 8: CopyMemory uTDC_VBA7.data(120), uTDC.pszFooterIcon, 8: CopyMemory uTDC_VBA7.data(128), uTDC.pszFooter, 8
CopyMemory uTDC_VBA7.data(136), uTDC.pfCallback, 8: CopyMemory uTDC_VBA7.data(144), uTDC.lpCallbackData, 8: CopyMemory uTDC_VBA7.data(156), uTDC.CXWidth, 4
hr = TaskDialogIndirect_VBA7(uTDC_VBA7, pnButton, pnRadButton, pfVerify)
#Else
hr = TaskDialogIndirect(uTDC, pnButton, pnRadButton, pfVerify)
#End If
(If you wanted to see the full class, it's on GitHub)

VBA and GetRawInputDeviceList

I am working in Access 2013 and try to get GetRawInputDeviceList, GetRawInputDeviceInfo, RegisterRawInputDevices and GetRawInputData equivalents for VBA with no success. I have also searched in vain for a procedure, function or module to get a list of connected HID devices to a computer to pick out a barcode scanner. This is the beginning of the third week so I am on my knees begging for assistance. Do any of you all have a module you're willing to share, a link to a website where this is dealt with? Any help is greatly appreciated.
Using the GetRawInputDeviceList API from VBA would be pretty tricky because of the pRawInputDeviceList parameter. Unless you're willing to jump through a ton of hoops to manage your own memory and manually handle the resulting array of RAWINPUTDEVICELIST in raw memory, you'll be better off coming at this from another direction.
Most barcode scanners I've dealt with present themselves to Windows as a keyboard. One possible solution would be to use a WMI query to enumerate attached Win32_Keyboard devices:
Private Sub ShowKeyboardInfo()
Dim WmiServer As Object
Dim ResultSet As Object
Dim Keyboard As Object
Dim Query As String
Query = "SELECT * From Win32_Keyboard"
Set WmiServer = GetObject("winmgmts:root/CIMV2")
Set ResultSet = WmiServer.ExecQuery(Query)
For Each Keyboard In ResultSet
Debug.Print Keyboard.Name & vbTab & _
Keyboard.Description & vbTab & _
Keyboard.DeviceID & vbTab & _
Keyboard.Status
Next Keyboard
End Sub
Note: If it doesn't turn up there, you can enumerate all of the USB devices by querying CIM_USBDevice: Query = "SELECT * From Win32_Keyboard"
EDIT: Per the comments, the above code won't return the handle needed to register to receive raw input events. This should get you started though - the RegisterRawInputDevices and GetRawInputData aspects are beyond the scope of what will easily go in an answer. Take a hack at it, and if you run into any problems post your code in another question.
Declarations:
Private Type RawInputDeviceList
hDevice As Long
dwType As Long
End Type
Private Type RidKeyboardInfo
cbSize As Long
dwType As Long
dwKeyboardMode As Long
dwNumberOfFunctionKeys As Long
dwNumberOfIndicators As Long
dwNumberOfKeysTotal As Long
End Type
Private Enum DeviceType
TypeMouse = 0
TypeKeyboard = 1
TypeHID = 2
End Enum
Private Enum DeviceCommand
DeviceName = &H20000007
DeviceInfo = &H2000000B
PreParseData = &H20000005
End Enum
Private Declare Function GetRawInputDeviceList Lib "user32" ( _
ByVal pRawInputDeviceList As Long, _
ByRef puiNumDevices As Long, _
ByVal cbSize As Long) As Long
Private Declare Function GetRawInputDeviceInfo Lib "user32" Alias "GetRawInputDeviceInfoW" ( _
ByVal hDevice As Long, _
ByVal uiCommand As Long, _
ByVal pData As Long, _
ByRef pcbSize As Long) As Long
Private Declare Function GetLastError Lib "kernel32" () As Long
Sample of retrieving device names with GetRawInputDeviceInfo:
Private Sub SampleCode()
Dim devices() As RawInputDeviceList
devices = GetRawInputDevices
Dim i As Long
For i = 0 To UBound(devices)
'Inspect the type - only looking for a keyboard.
If devices(i).dwType = TypeKeyboard Then
Dim buffer As String
Dim size As Long
'First call with a null pointer returns the string length in size.
If GetRawInputDeviceInfo(devices(i).hDevice, DeviceName, 0&, size) = -1 Then
Debug.Print "GetRawInputDeviceInfo error " & GetLastError()
Else
'Size the string buffer.
buffer = String(size, Chr$(0))
'The second call copies the name into the passed buffer.
If GetRawInputDeviceInfo(devices(i).hDevice, DeviceName, StrPtr(buffer), size) = -1 Then
Debug.Print "GetRawInputDeviceInfo error " & GetLastError()
Else
Debug.Print buffer
End If
End If
End If
Next i
End Sub
Private Function GetRawInputDevices() As RawInputDeviceList()
Dim devs As Long
Dim output() As RawInputDeviceList
'First call with a null pointer returns the number of devices in devs
If GetRawInputDeviceList(0&, devs, LenB(output(0))) = -1 Then
Debug.Print "GetRawInputDeviceList error " & GetLastError()
Else
'Size the output array.
ReDim output(devs - 1)
'Second call actually fills the array.
If GetRawInputDeviceList(VarPtr(output(0)), devs, LenB(output(0))) = -1 Then
Debug.Print "GetRawInputDeviceList error " & GetLastError()
Else
GetRawInputDevices = output
End If
End If
End Function
Sorry about the side scrolling.

VBA - generating unique numbers in code

Is there any way in which I can generate a unique number in code ?
I had an idea of using system time for that, but eventually could not implement it.
You can use the Now() then format the output to a number:
Sub unique()
Dim t As Date
t = Now()
Range("A1").NumberFormat = "#"
Range("A1") = CStr(Format(t, "yyyymmddhhMMss"))
End Sub
This would be unique.
As #Vasily pointed out, without formatting the cell as string and placing the number as a sting the value gets truncated to scientific notation.
especially for such cases the GUID (Global Unique IDentifyer) was invented. It may be a little bit oversized ... but just that you have seen it:
Option Explicit
Public Type TYP_GUID
bytes(15) As Byte
End Type
Public Declare Function CoCreateGuid Lib "OLE32.dll" _
(guid As TYP_GUID) As Long
Public Function newGUID() As TYP_GUID
Dim uGUID As TYP_GUID
CoCreateGuid uGUID
newGUID = uGUID
End Function
whenever you call newGUID() you will become a value that should be really unique in world. You can try and call it as often as you want ... you will never get the same value a second time.
it's also possible to convert such GUID's to string:
Option Explicit
Public Type TYP_GUID
bytes(15) As Byte
End Type
Public Declare Function CoCreateGuid Lib "OLE32.dll" _
(guid As TYP_GUID) As Long
Public Declare Function StringFromGUID2 Lib "OLE32.dll" _
(guid As TYP_GUID, _
ByVal lpszString As String, _
ByVal iMax As Long) As Long
Public Function newGUID() As TYP_GUID
Dim uGUID As TYP_GUID
CoCreateGuid uGUID
newGUID = uGUID
End Function
Public Function newGUID_String() As String
Dim sBuffer As String
Dim lResult As Long
sBuffer = VBA.Space(78)
lResult = StringFromGUID2(newGUID, sBuffer, Len(sBuffer))
newGUID_String = Left$(StrConv(sBuffer, vbFromUnicode), lResult - 1)
End Function

Reading/Writing INI w/ variable Section Name

Good afternoon folks -
I'm working on reading/writing an external file that is created and managed by a 3rd party that uses .INI structured files as its scripting language. I've got a wrapper working pretty well however, the section names are static with a unique number at the end ([GENERAL-1]) so that you have have the same task more than once. I am using VB.NET w/ VS2008.
My code below can successfully read a key from a section that is hardcoded but I'd like the key to be generic.
INI
test.ini
[GENERAL-1]
SUPPRESSTASKERRORS=Y
TASKERRORSENDJOB=Y
Code:
Declare Function GetPrivateProfileString Lib "kernel32.dll" Alias
"GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As
String, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As
Long, ByVal lpFileName As String) As Long
Declare Function WriteProfileString Lib "kernel32.dll" Alias
"WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As
String, ByVal lpString As String, ByVal lpFileName As String) As Long
' Read INI file
Dim uname As String ' receives the value read from the INI file
Dim slength As Long ' receives length of the returned string
Dim OriginalMJB As String = "c:\test\test.ini"
uname = Space(1024)
slength = GetPrivateProfileString("General-1", "SUPPRESSTASKERRORS", "anonymous",
uname, 1024, OriginalMJB)
Notice the General-1 above, if I have the value hardcoded as -1 I can read the input .ini file without a problem. Any thoughts on how I can get and use the value left of the hyphen?
Any help is appreciated!
--George
Here's one way. From here you should be able to make SectionNo equal the specific section you want.
Dim section As String = "General"
Dim SectionNo as String = "-"
Dim Number as Integer = 1
SectionNo += Number.ToString
slength = GetPrivateProfileString(section + SectionNo, "SUPPRESSTASKERRORS", "anonymous", uname, 1024, OriginalMJB)
Here's a couple of options
Dim SectionName As String = "General-1"
Dim SectionCategorie As String = ""
Dim Section As String = ""
'Using Split - It returns an array so you can load the results into an array
'or just call it and load the specific index each time.
SectionCategorie = Split(SectionName, "-")(0)
Section = Split(SectionName, "-")(1)
'Using Substring
SectionCategorie = SectionName.Substring(0, SectionName.IndexOf("-"))
Section = SectionName.Substring(SectionName.IndexOf("-") + 1)