Reading Floating Point values in VB.NET does not give expected values - vb.net

Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Integer, ByVal lpBaseAddress As Integer, ByRef lpBuffer As Integer, ByVal nSize As Integer, ByRef lpNumberOfBytesWritten As Integer) As Integer '--- Should anyone of these be single instead?
Dim p = Process.GetProcessesByName("insert random process name here")
If p.Count > 0 Then
Dim ReadBuffer As Integer = 0 '--- Should this one be single instead?
ReadProcessMemory(p(0).Handle, &H400000, ReadBuffer, 4, 0)
Dim Result As Single = ReadBuffer
Msgbox(Result.ToString)
End If
I could be using integer in the wrong places and should probably use single but when I do I still don't get the correct values. For example, I've changed the ReadBuffer data type to Single. The error could also lie here: Declare Function ReadProcessMemory on one or several of the Integer's.
This is an example value I expect to get: 16.6317005 and the value I do get instead is: -9,808334E+08, or 1,1471E-25 or something else, all depending on where I change the integer data type to single. I never get the correct value and I've tried all combinations.
Or is it perhaps just a conversion issue?
So I guess my main question is - What's wrong with my code? How do I properly read Floating Point values in VB.NET?

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?

CH341DLL.DLL + I2C not works properly with VB.NET

I write VB.NET class for implement CH341DLL.DLL functionality. The method CH341StreamI2C() is used for stream write and read into device. This way I've imported the method CH341StreamI2C() from DLL:
<DllImport("CH341DLL.DLL", SetLastError:=True, CallingConvention:=CallingConvention.StdCall)>
Private Shared Function CH341StreamI2C(ByVal iIndex As Integer, ByVal iWriteLength As Integer, ByRef iWriteBuffer As IntPtr, ByVal iReadLength As Integer, ByRef oReadBuffer As IntPtr) As Boolean
End Function
For check how this method works, I use I2C humidity and temperature sensor HTU21D. It's IIC address is 40h, and register where temperature is getting is E3h. So I invoke method CH341StreamI2C() like this:
Dim writeBuffer as Byte() = {&H40, &hE3} 'Address+Command
Dim s As String = Encoding.Unicode.GetString(writeBuffer)
Dim writeBufPtr As IntPtr = Marshal.StringToHGlobalAuto(s) 'Get pointer for write buffer
Dim wLen As Integer = writeBuffer.Length
Dim readBufPtr As IntPtr = IntPtr.Zero 'Init read pointer
Dim rLen as Integer = 3 'Sensor must return 3 bytes
Dim res As Boolean = CH341StreamI2C(0, wLen, writeBufPtr, rLen, readBufPtr)
I use logic analyzer to see what is on the SDA and SCL lines. And result is unpredictable. For example, if call previous code 4 times, that's the result:
It's seen, that physically CH341 device writes unpredictable values in the line. This is not DLL error, because other applications use this method and the result is correct. For note, other methods, e.g. CH341ReadI2C() and CH341WriteI2C(), that reads/writes only one byte per time, acts correct in my code.
What is the probably reason of the such behavior? May be, I've marshalled write buffer incorrect? How is the right way to do this?
If this is what you're using, the original declaration is:
BOOL WINAPI CH341StreamI2C(ULONG iIndex, ULONG iWriteLength, PVOID iWriteBuffer, ULONG iReadLength, PVOID oReadBuffer);
Since the buffer parameters are PVOIDs, you should be able to just marshal them directly to byte arrays:
<DllImport("CH341DLL.DLL", SetLastError:=True, CallingConvention:=CallingConvention.StdCall)>
Private Shared Function CH341StreamI2C(ByVal iIndex As Integer, ByVal iWriteLength As Integer, ByVal iWriteBuffer As Byte(), ByVal iReadLength As Integer, ByVal oReadBuffer As Byte()) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Arrays are reference types (classes) which means you always refer to them via their memory pointer. Thus when you pass them to a function (P/Invoked or not) you're actually passing the array's pointer, rather than the array itself. This is really helpful when P/Invoking since it often lets you pass the arrays as-is.

StartDocPrinter WinAPI call fails in VBA

Objective: Print from string or rich text box to any printer desired, using the Win API functions specified.
Problem: Calling StartDocPrinter in VBA Access always returns 0.
Info: The code below runs through, without breaking. OpenPrinter appears to get a good handle. When StartDocPrinter is called, it returns 0.
Using the following code I have tried,
Saving different info to dDocInfo and per #David_Heffernan recommendation, declared DOCINFO properties as Long and set values to 0.
When .pDatatype = vbNullstring, GetLastError returns,
Error 124 (invalid level) when StartDocPrinter parameter Level = 1
Error 6 (invalid handle) when StarDocPrinter parameter Level = ByVal 1, though an apparent valid handle shows in hPrinter
When .pDatatype = "RAW", GetLastError returns 0 regardless.
When .pDatatype = 'vbNullString and either DOCINFO property is set to a string, GetLastError returns 0 regardless.
Changing the parameters of the WinAPI functions (ByRef DOCINFO)
Checking into access privilege issues. It appears, from other's code, that setting the last OpenPrinter parameter to 0 should set the requested access to the printer to be PRINTER_ACCESS_USE. Is it possible GetLastError is not returning access denial errors?
Converting multiple references' code from C++ to VBA, but converting or not including pointers is confusing. Am I not converting StartDocPrinter(printer, 1, (LPBYTE) &docInfo); correctly?
Code:
Declarations:
Type DOCINFO
pDocName As String
pOutputFile As String
pDatatype As String
End Type
Public Declare Function OpenPrinter Lib "winspool.drv" Alias "OpenPrinterA" (ByVal pPrinterName As String, hPrinter As Long, ByVal pDefault As Long) As Long
Public Declare Function StartDocPrinter Lib "winspool.drv" Alias "StartDocPrinterA" (hPrinter As Long, Level As Long, dDocInfo As DOCINFO) As Long
Function:
Public Function printRawData(sPrinterName As String, lData As String) As Boolean
Dim bStatus As Boolean, hPrinter As Long, dDocInfo As DOCINFO, lJob As Long, nWritten As Integer
' Open a handle to the printer.
bStatus = OpenPrinter(sPrinterName, hPrinter, 0)
If bStatus Then
' Fill in the structure with info about this "document."
dDocInfo.pDocName = vbNullString
dDocInfo.pOutputFile = vbNullString
dDocInfo.pDatatype = "RAW"
' Inform the spooler the document is beginning.
lJob = StartDocPrinter(hPrinter, 1, dDocInfo) 'Returns 0 :(
Debug.Print hPrinter, sPrinterName, lJob, GetLastError()
If lJob > 0 Then
' Start a page.
bStatus = StartPagePrinter(hPrinter)
If bStatus Then
' Send the data to the printer.
bStatus = WritePrinter(hPrinter, lData, Len(lData), nWritten)
EndPagePrinter (hPrinter)
End If
' Inform the spooler that the document is ending.
EndDocPrinter (hPrinter)
End If
' Close the printer handle.
ClosePrinter (hPrinter)
End If
' Check to see if correct number of bytes were written.
If Not bStatus Or (nWritten <> Len(lData)) Then
printRawData = False
Else
printRawData = True
End If
End Function
References/Relevant Questions:
- http://support.microsoft.com/kb/154078 Basis document for this code. Edit: Found where ByVal was missed on a few declarations here.
- Send Raw Data to ZPL Printer using Visual Basic (MS Access 2000) This person seems to use nearly identical code effectively, so why can't I? The answer to this question is written in C++.
- http://www.cplusplus.com/forum/general/26184/ The code here is also written in C++ and I am unsure how to convert.
- http://codingdomain.com/visualbasic/win32api/datatypes/ Guidance I'm using on converting datatypes and pointers, which I don't fully understand.
- StartDocPrinter(hPrinter, 1, di) returns false Some code was provided here but no answer. This is where I got idea to give errors provided.
- excel bva code to send command to usb printer I tried this and do not have the required access privileges. I would still like to know how to use the above code correctly, even if this is what I would end up doing.
With #HansPassant's link to the MS KB, I discovered the error in my code. The declaration of the StartDocPrinter function was missing ByVal for the hPrinter and Level parameters.
Public Declare Function StartDocPrinter Lib "winspool.drv" Alias "StartDocPrinterA" (ByVal hPrinter As Long, ByVal Level As Long, dDocInfo As DOCINFO) As Long
Your declaration for the DOCINFO structure does not look correct. It should be declared as:
Type DOCINFO
cbSize As Integer
lpszDocName As String
lpszOutput As String
lpszDatatype As String
fwType As Integer
End Type
The cbSize should be initialized to the size of the structure in bytes, and fwType should be set to 0.

Send text from VB to Delphi apps, using SendMessage

) I am trying to send a short text from a VB app to Delphi app.. here is the
VB Code: Sender Program "Sender"
Public Class SendData
Const WM_COPYDATA = &H4A
Public Structure CopyDataStruct
Public dwData As Integer
Public cbData As Integer
Public lpData As String
End Structure
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As _
CopyDataStruct) As Long
Private Sub SendData(ByVal cds)
Dim iHwnd As Long
Dim SS As String = "Test String less than 30 Char"
Dim cds As CopyDataStruct
cds.dwData = 0
cds.cbData = Len(SS)
cds.lpData = SS
iHwnd = FindWindow(0&, "Receive")
SendMessage(iHwnd, &H4A, Me.Handle, cds)
End Sub
here is the Delphi Code: Receiver program "Receive"
procedure TForm1.HandleCopyDataString(copyDataStruct: PCopyDataStruct);
var
s : string;
begin
s := PChar(CopyDataStruct.lpData);
cdMemo.Lines.Add(Format('Received data "%s" at %s',[s, TimeToStr(Now)]));
end;
procedure TForm1.WMCopyData(var Msg: TWMCopyData) ;
var
s : string;
sText: array[0..255] of Char;
copyDataType : TCopyDataType;
begin
copyDataType := TCopyDataType(Msg.CopyDataStruct.dwData);
s := PChar(Msg.CopyDataStruct.dwData);
Form1.cdMemo.Lines.Add(Format('Data from: %d',[msg.From]));
HandleCopyDataString(Msg.CopyDataStruct);
case Msg.CopyDataStruct.dwData of 0: //we are being sent a string
begin
StrLCopy(sText, Msg.CopyDataStruct.lpData, Msg.CopyDataStruct.cbData);
Form1.Label1.Caption := sText;
end;
end;
end;
What am I doing wrong here? It is possible to send strings from VB to Delphi programs using WM_COPYDATA command, and SendMessage function?
please help me :-)
F
There are a few things wrong with your Delphi code.
The dwData field holds an integer, but you type-cast it to PChar, a pointer, and then assign it to your string. That's not the field where you stored your string data. That's lpData.
The string you pass is not null-terminated. The OS only promises to copy exactly as many bytes as you specify in the cbData field. That's not necessarily a problem, but you need to be aware of it when you read the string later. To assign s to hold the string copied from the other process, use SetString like this:
SetString(s, PAnsiChar(Msg.CopyDataStruct.lpData), Msg.CopyDataStruct.cbData);
You haven't shown what TCopyDataType is, but if it's anything other than an integer or integer-subrange type, you're using it wrong. The dwData field is already a DWord, so you can use it wherever a numeric value is expected.
You're calling StrLCopy wrong. The third parameter should be the size of the destination buffer, not the source. It's meant to prevent buffer overflows by not copying more characters than will fit in the destination. The function expects to be able to detect the size of the source buffer by finding the terminating null character (but we already established that that won't be available). You could fix it like this:
StrLCopy(sText, Msg.CopyDataStruct.lpData,
Min(Length(sText), Msg.CopyDataStruct.cbData));
(Min is in the Math unit.)

Bad Practice to Pass ByRef and ByVal Parameters In Same Routine?

Is it considered bad practice (VB.NET or any language) to code a function with both ByVal and ByRef parameters as used in my getFile function below?
Function getFile(ByVal channel As Integer, _
ByRef Filename As String, _
ByRef Filesize As String) As Integer
...
End Function
...
Dim status As Integer
Dim filename As String
Dim filesize As Integer
For channel In 1 To 16
status = getFile(channel, filename, filesize)
...
Next channel
I usually try to avoid ByRef all together, it often ends up being ugly and confusing.
The fact that you're mixing ByVal and ByRef doesn't affect readibility any more than just having all ByRef IMHO.
For example, if I only need the filename, I'd still need to pass in a filesize variable, which I think makes it a bit uglier. And when reading code it can be easy to miss that a parameter might be changed.
As Assaf also says in his comment, instead I'd normally try to get around the whole issue by having the method return some kind of structure that can contain all the return data. and if it failed I'd throw an exception rather than return the status (assuming that the status is some kind of error).