Unknown SetProcessDpiAwareness Error Result - vb.net

I have 1 computer out of 50 that is returning a non zero result from SetProcessDpiAwareness and I can't find any information on it. I am setting the DPI Awareness to Unaware. I have one computer that is returning a value of 105621835743232(Decimal). It seems to still be setting the DPI Awareness to unaware like it should but gives a return value that is not expected.
Private Declare Function SetProcessDpiAwareness Lib "shcore.dll" (ByVal Value As PROCESS_DPI_AWARENESS) As Long
Private Function SetDPI() As Long
'Results from SetProcessDPIAwareness
'Const S_OK = &H0&
'Const E_INVALIDARG = &H80070057
'Const E_ACCESSDENIED = &H80070005
Dim lngResult As Long
lngResult = SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.Process_DPI_Unaware)
Return lngResult
End Function
This is a clickonce winforms application so I can't use the manifest to set DPI.
Any help with locating documentation would be greatly welcomed!
Thanks in advance.

This API function's return value is a 32 bit integer so you should use Integer rather than Long. Note that Long is a 64 bit type.
I also strongly recommend using p/invoke rather than Declare. The latter exists for compatibility reasons. P/invoke offers much greater control of the import process.

Related

What is the reason that FTD2XX functions do not work in VB.NET after Visual Studio update(?), is there possible fix?

I am developing an app in VB.NET (for a customer with hardware using FTDI USB serial chip. Communication uses FTD2XX library and the respective Nuget package (FTD2XX.Net v1.2.1). After some update of Visual Studio (probably update to 17.1, but I am not sure) all functions except a few stopped working. Current VS version is 17.1.1.
For instance, it is possible to obtain number of devices attached by the FTDI driver:
Friend Declare Function FT_CreateDeviceInfoList Lib "FTD2XX.DLL" (ByRef lngNumDevs As Integer) As Integer
...
Dim ftStatus As Integer
Dim numDevices As Integer
ftStatus = FT_CreateDeviceInfoList(numDevices)
In the above snippet ftStatus result = 0 (i.e. OK) and numDevices is set to 1 (correct).
Problem starts when I want to do something serious:
Friend Declare Function FT_GetComPortNumber Lib "FTD2XX.DLL" (ByVal lnghandle As Integer, ByRef lplComPortNumber As Integer) As Integer
Friend Declare Function FT_Open Lib "FTD2XX.DLL" (ByVal iDevice As Integer, ByRef lnghandle As Integer) As Integer
Friend Declare Function FT_Close Lib "FTD2XX.DLL" (ByVal lnghandle As Integer) As Integer
Dim portHandle as Integer
Dim cpNumber as Long
For i% = 0 To 255
ftStatus = FT_Open(i, portHandle)
If ftStatus = FT_OK Then
ftStatus = FT_GetComPortNumber(portHandle, cpNumber)
ftStatus = FT_Close(portHandle)
' here is some non-essential code registering that port at index i% exists...
End If
Next
In the above code, FT_Open returns ftStatus = 0 (FT_OK) and sets a value for portHandle.
However, the next call, FT_GetComPortNumber, returns ftStatus = 1 (FT_INVALID_HANDLE) and the value passed to cpNumber is 0xFFFF (shows as positive, but in fact should be -1, I guess...). What is worse, FT_Close() also returns FT_INVALID_HANDLE and the port remains open. I verified it by trying to open the port from another app - access denied.
Sometimes it seems that FT_Write and FT_Read functions work despite this mess, but in my last try I could not any communication with the hardware at all.
I tried to use System.IO.Ports.SerialPort as possible workaround but that does not work at all. On top of that, I need to use bit-bang on RTS, because it controls supply voltage and reset of the hardware connected to the other side of the FTDI chip. Without possibility to bring RTS down for hundreds of milliseconds and then hold it up all the time I cannot control the hardware. AFAIK System.IO.Ports.SerialPort provides no possibility to do that.
What could be the solution?
After much trial and error, it appears to be a problem in compile configuration.
Open Solution properties Window and click on the Compile tab.
Then click on "Advanced Compile Options"
If the "Remove integer overflow checks" checkbox is not checked, check it!
I have no idea how an integer overflow check can garble a 32-bit number not involved in any arithmetic operation whatsoever, but this is what really happened. I consider this a bug in Visual Basic compiler used in Visual Studio 17.1.6 (and a number of previous versions), but I did not dig deeper in this topic.
#HansPassant writes:
The declarations are wrong, it must be lnghandle As IntPtr. The difference between Integer and IntPtr matter when you run the app in 64-bit mode. Prone to happen when targeting .NETCore, as likely in VS2022.
-- Hans Passant

Correct declaration of DWord in VBA

I'm using an API function which returns a DWORD
Because I want intellisense on the LoWord and HiWord, rather than using a Long:
Declare Sub myAPI(ByRef outVariable As Long)
...as suggested in this list of WinAPI -> VBA datatype conversions, I'm using a type:
Public Type DWORD 'same size as Long, but intellisense on members is nice
'#Ignore IntegerDataType
LoWord As Integer
'#Ignore IntegerDataType
HiWord As Integer
End Type
Declare Sub myAPI(ByRef outVariable As DWORD)
However RubberDuck's IntegerDataType inspection reminded me that on 32 bit systems VBA converts 2-byte Integers to 4-byte Longs internally, so I'm wondering whether my DWORD declaration is really 4 consecutive bytes as expected, or 8.
I'm not familiar enough with pointers and bits & bytes to picture in my head what's going on, but I imagine the API somehow knows to fill only the lower half of each part, as I've been getting the results I expect (I think) from the API.
Your user defined type is 4 bytes is size, because Integer is 2 bytes in size.
You can check this for yourself:
Dim dw as DWORD
Dim size as Integer
size = LenB(dw)

VB.NET - Problems with SHQueryRecycleBin

I'm currently working on an application for my personal use. The idea is that you can open it up and reach all kind of stats of your computer (Recycle bin, Drives, Network and much more). Now I was working with the SHQueryRecycleBin from Win API.
Though I have some problems. And I've tried to look over outdated solutions for VB6 or VB.NET solutions that simply didn't work. I used the code reference from this source and to retrieve the size and count of files I used this source.
I put it in an timer, and after those 100 ticks (as I set it) were ran, I got this error:
File I/O of a structure with field 'cbSize' of type 'UInt32' is not valid.
The type of cbSize is UInteger which (apparently) automatic changes to an UInt32 - I think it's based on the system.
You should note that I'm on an Windows 7 x86 (64-bit). If have an solution for this or another piece of code that is easier than use Win API, let me know.
I have looked at the System.Management but wanted an bullet proof code that could interact with most systems.
I don't have vb.net handy to test, but the following code works perfectly well in vb6:
In a module:
Public Type SHRECYCLEBININFO
cbSize As Long
i64Size As Currency
i64NumItems As Currency
End Type
Public Declare Function SHQueryRecycleBin Lib "shell32.dll" Alias "SHQueryRecycleBinA" (ByVal pszRootPath As String, pSHQueryRBInfo As SHRECYCLEBININFO) As Long
And in a form:
Private Sub Command1_Click()
Dim info As SHRECYCLEBININFO
Dim res As Long
info.cbSize = Len(info)
res = SHQueryRecycleBin("C:\", info)
MsgBox "size: " & (info.i64Size * 10000) & " bytes" & vbCrLf & "items: " & (info.i64NumItems * 10000)
End Sub
Note the use of type "currency" - this is because vb6 doesn't have a normal data type for 64-bit integers. Type Currency is using 8 bytes, but keeps 4 decimal places, hence the multiplication by 10000 to get the results.

Is there a VB.NET equivalent of C# out parameters?

Does VB.NET have a direct equivalent to C# out function parameters, where the variable passed into a function does not need to be initialised?
No, there is no equivalent of the out keyword in VB.
However, VB does automatically initialise all local variables in a method, so you can use ByRef without needing to explicitly initialise the variable first.
Example:
Sub Main()
Dim y As Integer
Test(y)
End Sub
Sub Test(ByRef x As Integer)
x = 42
End Sub
(If you examine code in the framework (for example Double.TryParse), you may see the <OutAttribute> added to parameters, but that only makes a difference when the call is marshalled for COM interop or platform invoke.)
No, there is no equivalent construct that allows a non-initialised variable to be passed to a method without a warning, but, as mentioned in my question and answer specifying an <Out()> attribute on a ByRef parameter definition, although VB ignores it, is treated by C# as an out parameter.
So, I would pre-initialise reference variables to Nothing and specify <Out()> ByRef to signify the intention (that will work if C# users ever access your methods).
If you feel you know when you intend to access the default Nothing in otherwise unassigned reference variables you can set the "Warning configuration" "Use of variable prior to assignment" to "None" at the Project level (Project Properties > Compile, and you probably want to set Configuration to "All Configurations" before changing this setting), or, in VS2015 (VB.NET 14), you can use #Disable Warning BC42030.
C# version
void TestFunc(int x, ref int y, out int z) {
x++;
y++;
z = 5;
}
Vb.net version
Sub TestFunc(ByVal x As Integer, ByRef y As Integer, ByRef z As Integer)
x += 1
y += 1
z = 5
End Sub
Found the answer here
Update
As stated in the comment do not forget to initialze your parameter that will be used in the out slot
I had the problem in VB.NET that I called a function "by ref" that passed an array back.
Even though the compiler flagged it as a warning it was fine. The fix is super simple and probably good programming practice.
I changed
Dim m_arr_values() as Integer
fnRetArray(m_arr_values)
to
' Even though 'Nothing' is the default value, setting it
' stops the compiler complaining.
Dim m_arr_values() as Integer = Nothing
fnRetArray(m_arr_values)
It also helps when coding if variable names are descriptive...
Sub fnCreatePalette(ByRef arr_in_pal() As color, ByRef arr_out_pal() as uinteger)
...
End Sub
VB has the attribute which should be the same as C# out but today you still get a warning even if you use it. There are details about fixing it in vblang area of github. https://github.com/dotnet/vblang/issues/67.
You can use the pass by reference method in VB.NET.
You need the Out parameter mechanism in C#, because it doesn't let you use any variable without initializing it.
VB.NET doesn't need a special keyword as it automatically does it by itself.
Just use ByRef.
Use keyword ByRef before variable.

Error when reading registry with Visual Basic 6 running on Win7

I have inherited a VB6 application from a friend of a family member, who wants to have some enhancements done to it.
I haven’t developed in VB for more than 3 years (I’m currently developing in MS Dynamics Ax).
I’ve recently upgraded my hardware and am now running Win7. The last time I worked with the app (about a year and a half ago) was on a WinXP platform, and everything worked fine. Now when I run the app (through code) on Win7, I get an error when trying to read from the registry.
Yes, I am running VB as administrator.
The code to read from the registry is:
Public Function sReadRegistry(ByVal hKeyRoot As Long, _
ByVal sSubKey As String, _
ByVal sValueName As String) As String
Dim r As Long
Dim sData As String * 255
Dim lDataSize As Long
Dim sTempVal As String
Dim readValue As String
lDataSize = 255
'Get the Value Requested
lDataSize = 255
r = VRegReadString(hKeyRoot, sSubKey, sValueName, sData, lDataSize)
If r Then
sTempVal = ""
Else
sTempVal = Left$(sData, lDataSize - 1)
End If
sReadRegistry = sTempVal
End Function
The “VRegReadString “ is declared within a module; and is declared as follows:
Declare Function VRegReadString Lib "VREG" (ByVal hKeyRoot As Long, ByVal sSubKey As String, ByVal sValueName As String, ByVal sData As String, ByRef lDataSize As Long) As Long
It complains about the “VREG” library…
The error I get is: “File not found: VREG”.
Is there a reference or component that I forgot to select? Can somebody please help with a solution?
Thanks in advance.
Seeing that the function declaration is an import from an external library called "VREG", you are probably missing the actual library itself, i.e. VREG.DLL. Unfortunately, this doesn't seem to be a common library, so you'd have to come up with it yourself.
Good news is, though, accessing the registry is not really hard and can be done with just the bare Windows API, especially seeing that VREG.DLL does not really seem to add a good deal of abstraction to the regular API. Take a look at these functions:
Registry Functions
...which you can use to easily re-write registry access, provided you fail to procure the needed DLL from somewhere.