Pass PByte to a DLL from vb..net - c++

I having troubles to calling a function inside a C DLL (dont know if C or C++)
Basically, I m trying to make a wrapper for the SDK DLL of Gigabyte RGB Fusion API.
I m working in VB.NET with Visual Studio 2013 (if you answer in C# is good too)
Declaring functions with DLLImport
for Ex. This function works fine.
C++
int dllexp_GetMaxDivision(void)
VB.NET
<DllImport("GLedApi.dll", SetLastError:=False, callingConvention:=CallingConvention.Cdecl)> _
Public Shared Function dllexp_GetMaxDivision() As UInt32
EndFunction
calling dllexp_GetMaxDivision works like a charm. The trouble starts when i pass parameters:
DWORD dllexp_GetLedLayout(PBYTE bytArray, int arySize)
Parameters
Name / Type / Description
bytArray / Output / Pointer to a buffer that receives the LED layout on motherboard. The values of the elements in the array are as follows:
Value Meaning
0(NA) No implement LED
1(A_LED) Analog LED
2(D_LED_TYPE1) Digital LED Type 1
3(D_LED_TYPE2) Digital LED Type 2
arySize / Input / Size, in byte of the buffer indicated by bytArray. This value must be a dllexp_GetMaxDivision return value.
DWORD dllexp_SetLedData(PBYTE bytArray, int arySize)
Parameters
Name / Type / Description
bytArray / Input / Pointer to a byte array converted from the LEDSETTING
structure array.
arySize / Input / Size, in bytes, of the buffer indicated by bytArray.
typedef struct _LEDSETTING {
BYTE Reserve0;
BYTE LedMode;
BYTE MaxBrightness;
BYTE MinBrightness;
DWORD dwColor;
WORD wTime0;
WORD wTime1;
WORD wTime2;
BYTE CtrlVal0;
BYTE CtrlVal1;
} LEDSETTING, *PLEDSETTING
This is what I try. I dont get to test with dllexp_SetLedData yet, so I will focus in dllexp_GetLedLayout
<DllImport("GLedApi.dll", setLastError:=False, CallingConvention:=CallingConvention.Cdecl)> _
Public Shared Function dllexp_GetLedLayout(ByRef bytArray As IntPtr, arySize As Int32) As UInt32
End Function
Tried with a byte array, but didn't work. And This:
Dim Param(iMaxDivs - 1) As Byte
For A As Integer = 0 To iMaxDivs - 1
Param(A) = CByte(0)
Next
Dim Tama As Integer = Marshal.SizeOf(Param(0)) * Param.Length
Dim buffer As IntPtr = Marshal.AllocHGlobal(Tama)
Marshal.Copy(Param, 0, buffer, Param.Length)
resp2 = GLed.dllexp_GetLedLayout(buffer, Param.Length)
Marshal.Copy(buffer, Param, 0, Param.Length) '<===== AccessViolationException
Marshal.FreeHGlobal(buffer)
The line of code that retrieve the buffer to the array throw an exception:
'System.AccessViolationException' en mscorlib.dll Attempted to read or
write protected memory. This is often an indication that other memory
is corrupt.
In some other tests, i had PinvokeImbalance sort of... , I guess it was a type mismatch.
The weird thing is that sometimes, the code doesnt throw an exception and proceed, but return the zero-filled array. even when the buffer contains data.
I also try with AllocCoTaskMem but the process crash...,
Can somebody please point me in the right way to do this??
Thanks!

Related

Giving unsigned char** a value in C++

I'm really rusty with C++ and pointers and can't figure out a solution to my problem.
Here is the context:
I have to write a DLL to replace an old one and for one of the functions i must follow this signature:
int GetText(unsigned char** outBuf, unsigned int* outLen);
In the final piece of software, the value of outBuf is displayed in a textbox.
If i understand correctly, outBuf is a pointer to a pointer, or a pointer to char array.
outLen seems to be the length of the data i provide to this buffer.
To test out this function, i'd like to create from scratch a char array and pass it to outBuf.
What i tried so far:
int GetText(unsigned char** outBuf, unsigned int* outLen){
*outBuf = NULL;
*outLen = 0;
//Create a char array from scratch, just to test...
unsigned char text[] = "Hello";
//outLen is the size of text
*outLen = sizeof(text);
//I allocate size to outBuf
*outBuf = new unsigned char[*outLen];
//I copy value of text into outBuf
memcpy(*outBuf, &text, *outLen);
return OK;
};
Is my code to give value to *outBuf and *outLen correct and what could lead it to not working? Because when i try it with the piece of software i was given to test the dll, it fails. It displays a dialog saying Reading failed with status 0.
The function seems to go through as the return value(status) is taken in account. But it doesn't manage to display anything. As if one of the variables i return is empty or null...
here is an extract from the final software (in VB) calling the function. As i'm not allowed to disclose the real code, i changed some value callings:
'lance la lecture
StatusInt = LectureText(NumText, textStr
If StatusInt = 0 Then
'traitement des sauts de ligne
textStr= Replace(textStr, Chr(&HD) + Chr(&HA), Chr(&HA))
textStr= Replace(textStr, Chr(&HA), Chr(&HD) + Chr(&HA))
'copie dans la textbox
Text1.Text = textStr
Else
MsgBox "Lecture failed with err " & Str(StatusInt)
End If
And the function
Public Function LectureText(ByVal NumText, ByRef Text As String) As Long
Dim AddrText As Long
'Dim TextAs String
Dim LengthText As Long
Dim status As Long
'lecture du text
status = GetText(AddrText, LengthText)
'si lecture ok, copie et lib�re le buffer
If status = 0 And LengthText <> 0 Then
'Alloue l'espace n�cessaire pour le text
Text = String(LengthText, vbNullChar)
'copie du text
CopyMemory ByVal Text, ByVal AddrText, LengthText
End If
'retourne status
LectureText= status
End Function
As you can see, if you provide a status = 0, it should not call the dialog saying there is an error. But in my case, it says "Lecture failed with err 0". How is it even possible...
i'm sure it's not the correct way to make things but i don't usually work with C++ and need to spend as little time as possible on this problem. I already searched for 2 days would need to take many things back from the start about C++. Thing i can't offer now.
Thanks in advance for your kind help
The code i wrote was correct.
The fact is my functions needed to be declared with __stdcall to be compatible with VB.
But when compiling the dll with __stdcall, Visual Studio was adding decoration to function names and the existing software in VB was trying to call them by their undecorated names.
So i added a definition file (.def) to my project so that the function names are exported correctly.

CRichEditCtrl::GetLine funtion is not working for Charaters < 4

In visual studio 2015, When I am trying to write less than 4 characters in Rich Text Box, it gives exception (below is the attachment)
After doing Debugging, we came to know that Ensure() is causing the Exception:
int CRichEditCtrl::GetLine(_In_ int nIndex, _Out_writes_to_(nMaxLength, return) LPTSTR lpszBuffer, _In_ int nMaxLength) const
{
ASSERT(::IsWindow(m_hWnd));
ENSURE(sizeof(nMaxLength)<=nMaxLength*sizeof(TCHAR)&&nMaxLength>0);
*(LPINT)lpszBuffer = nMaxLength;
return (int)::SendMessage(m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer);
}
When we are giving less that 4 characters in Rich Text Box,
sizeof(nMaxLength)<=nMaxLength*sizeof(TCHAR)
in this case sizeof(nMaxLength) = 4 and nMaxLength*sizeof(TCHAR) = 3
So, 3<4 is causing the Exception.
Now, I need help in which way Shall I give less than 4 charcters in a Rich Text Box, so that this function works and doesnt gives an Exception.
Sure it works.
This function GETs a line. You have to offer a buffer, large enough to fir the contents. The smallest buffer you are allowed to pass to the message is the size of an integer (4 bytes).
The size of the that you receive is returned by the function.
This code always work for any length
CString strTemp;
nMinLength = min(nLineLength,sizeof(int));
int iLen = m_ItemTextCtrl.GetLine(k, strtemp.GetBuffer(nMinLength), nLineLength);
strTetmp.ReleaseBuffer(iLen);
Passing data to the RTF control is done by streaming in data or using WM_SETTEXT or SetWindowText

Visual Studio 2008 WriteFile

Im using VS2008 for compiling and developing my application. Im required to take Adc input and serial print it through RS232 in a WinCE6 OS over some termianl like putty or hyperterminal. The problem is when I use the WriteFile function it gives me the following error.
Error 3 error C2440: '=' : cannot convert from 'double' to 'char [32]' c:\Users\Sohan\Downloads\uartdemo\uartdemo\src\main.c 137 Sohan_1
I want to take the input from ADC channel and then after converting it to voltage i have to transmitt. I have tried using a constant char string and it works but when i take the input from the channel and then try it doesnt work.
HANDLE portHandle;
DWORD noOfBytesRead = 0;
DWORD bytesTransmitted = 0;
DWORD firstChoice = 0;
BOOL retVal = FALSE;
char c="hello";
char transmit2Buffer[BUFFER_SIZE] = "7.8888v";
char volt[BUFFER_SIZE];
WriteFile(portHandle, transmit2Buffer, strlen(transmit2Buffer), &bytesTransmitted, NULL);
WriteFile(portHandle, volt, strlen(volt), &bytesTransmitted, NULL);
the first write function works but the second doesnt. variable volt will keep on changing so how should i write it.please help..
After searching i have got an answer for my question.
this worked for me..
You can use sprintf() as you have done to convert a double to a string, but I would actually recommend using _snprintf() instead simply because sprintf() has no regard for the fact that strings are fixed length devices in memory and will overflow if you don't watch it. _snprintf() allows you to specify the length of the out string, just be sure to specify the size as one less than the actual allocated memory block, because _snprintf() does not store the terminating null character if it has to cut the output short.
An example us using _snprintf() is:
void ToString(char * outStr, int length, double val)
{
_snprintf(outStr,length,"%f",val);
}
Got this answer on some website!!!

How do the variable length fields in the windows EVENTLOGRECORD structure work?

I've tried, with little success, to identify how the variable length portion of the EVENTLOGRECORD data works.
Winnt.h defines the structure, and the following data, as follows:
typedef struct _EVENTLOGRECORD {
DWORD Length; // Length of full record
DWORD Reserved; // Used by the service
DWORD RecordNumber; // Absolute record number
DWORD TimeGenerated; // Seconds since 1-1-1970
DWORD TimeWritten; // Seconds since 1-1-1970
DWORD EventID;
WORD EventType;
WORD NumStrings;
WORD EventCategory;
WORD ReservedFlags; // For use with paired events (auditing)
DWORD ClosingRecordNumber; // For use with paired events (auditing)
DWORD StringOffset; // Offset from beginning of record
DWORD UserSidLength;
DWORD UserSidOffset;
DWORD DataLength;
DWORD DataOffset; // Offset from beginning of record
//
// Then follow:
//
// WCHAR SourceName[]
// WCHAR Computername[]
// SID UserSid
// WCHAR Strings[]
// BYTE Data[]
// CHAR Pad[]
// DWORD Length;
//
} EVENTLOGRECORD, *PEVENTLOGRECORD;
I can pull out the first chunk which appears to be the source with the following code, but its certainly not the intended method:
memcpy(&strings, pRecord+sizeof(EVENTLOGRECORD), tmpLog->UserSidOffset);
But from the comments in Winnt.h, I'm also getting the computer name.
So can someone explain how to determine the "SourceName" length from the EVENTLOGRECORD structure, and explain what StringOffset, DataLength and DataOffset are?
Thanks.
Note: throughout the answer I'll assume that you have a pointer to that structure like this:
EVENTLOGRECORD * elr;
to shorten the code snippets.
So can someone explain how to determine the "SourceName" length from the EVENTLOGRECORD structure
There's no field that specifies how long it is, but you can determine it quite easily: it is the first field of the record after the well-defined fields, so you can simply do:
WCHAR * SourceName=(WCHAR *)((unsigned char *)elr + sizeof(*elr));
Now, in SourceName you have a pointer to that string; you can easily determine its length with the usual string functions.
By the way, after the terminator of SourceName there should be the the ComputerName string.
and explain what StringLength
There's no StringLength member, what are you talking about?
DataLength and DataOffset are?
An event log is composed also of arbitrary binary data, that is embedded in the record.
The DataOffset member specifies the offset of such data from the beginning of the record, and DataLength specifies how long is that data. If you were to copy that data to a buffer (assuming that it's big enough), you'd do:
memcpy(targetBuffer,(unsigned char *)elr + elr->DataOffset,elr->DataLength);
By the way, instead of reading directly the include files you should read the documentation, it's far easier to understand.
Addendum about StringOffset
The StringOffset field specifies the offset of the strings associated to the event from the beginning of the record.
The StringOffset field works very much like the DataOffset field described above, but there's no corrispondent StringLength field, since the length of each string can be easily determined using the normal string functions (in fact the string section is just made of several NUL-terminated strings put one after the other).
Moreover, the location where the strings section ends can be easily determined examining the DataOffset member, in facts the strings section ends where the data chunk begins. The EVENTLOGRECORD structure also provides the NumStrings field to determine the number of strings contained in the strings section (thanks Remy Lebeau).
If you were to put these strings in a vector<wstring> you'd do something like this (careful, untested code):
vector<wstring> strings;
for(
wchar_t * ptr=(wchar_t *)((unsigned char *)elr + elr->StringOffset);
strings.size()<elr->NumStrings;
ptr+=strings.back().length() + 1
)
strings.push_back(wstring(ptr));
So can someone explain how to determine the "SourceName" length from the EVENTLOGRECORD structure,
From what I can see, SourceName[] and Computername[] are one behind each other, separated by a '\0', with the first starting right behind DataOffset, and the second starting right behind the '\0' of the first, and going up to two bytes before UserSidOffset, with a '\0' trailing.
and explain what StringLength, DataLength and DataOffset are?
StringLength I cannot find (and StringOffset is where Strings[] starts), DataLength is the number of bytes in Data[], and DataOffset is where Data[] starts.
To read the strings, you could do something like this:
// Beware, brain-compiled code ahead!
void f(EVENTLOGRECORD* rec)
{
std::wstring source_name(
reinterpret_cast<const wchar_t*>(
reinterpret_cast<const unsigned char*>( rec
+ sizeof(EVENTLOGRECORD ) ) ) );
std::wstring computer_name(
reinterpret_cast<const wchar_t*>(
reinterpret_cast<const unsigned char*>( rec
+ sizeof(EVENTLOGRECORD )
+ source_name.length()+1 ) ) );
// ...
}
Please read the documentation. It tells you what the StringLength, DataLength and DataOffset members are.
As for the SourceName and ComputerName members, they are both null-terminated strings (with potentially extra padding after ComputerName to align the UserSid member). You saw the ComputerName appear in your buffer because you told memcpy() to copy the raw bytes of both members together. Try using lstrlenW() and lstrcpyW() (or equivilent functions).

How to convert a number to a bytearray in bit endian order

I am trying to uncompress some data created in VB6 using the zlib API.
I have read this is possible with the qUncompress function:
http://doc.trolltech.com/4.4/qbytearray.html#qUncompress
I have read the data in from QDataStream via readRawBytes into a char
array, which I then converted to a QByteArray for decompression. I
have the compressed length and the expected decompressed length but am not getting
anything back from qUncompress.
However I need to prepend the expected decompressed length in big endian format. Has anybody done this and have an example?
I haven't used VB6 in ages, so I hope this is approximately correct. I think that vb6 used () for array indexing. If I got anything wrong, please let me know.
Looking at the qUncompress docs, you should have put your data in your QByteArray starting at byte 5 (I'm going to assume that you left the array index base set to 1 for this example).
Let's say the array is named qArr, and the expected uncompressed size is Size.
In a "big-endian" representation, the first byte is at the first address.
qArr(1) = int(Size/(256*256*256))
qArr(2) = 255 And int(Size/256*256)
qArr(3) = 255 And int(Size/256)
qArr(4) = 255 And int(Size)
Does that make sense?
If you needed little endian, you could just reverse the order of the indexes (qArr(4) - qArr(1)) and leave the calculations the same.
This is how I can convert arbitary data from one format to another.
Private Type LongByte
H1 As Byte
H2 As Byte
L1 As Byte
L2 As Byte
End Type
Private Type LongType
L As Long
End Type
Function SwapEndian(ByVal LongData as Long) as Long
Dim TempL As LongType
Dim TempLB As LongByte
Dim TempVar As Long
TempL.L = LongData
LSet TempLB = TempL
'Swap is a subroutine I wrote to swap two variables
Swap TempLB.H1, TempLB.L2
Swap TempLB.H2, TempLB.L1
LSet TempL = TempLB
TempVar = TempL.L
SwapEndian = TempVar
End Function
If you are dealing with FileIO then you can use the Byte fields of TempLB
The trick is using LSET an obscure command of VB6
If you are using .NET then doing the process is much easier. Here the trick is using a MemoryStream to retrieve and set the individual bytes. Now you could do math for int16/int32/int64. But if you are dealing with with floating point data, using LSET or the MemoryStream is much clearer and easier to debug.
If you are using Framework version 1.1 or beyond then you have the BitConvertor Class which uses arrays of bytes.
Private Structure Int32Byte
Public H1 As Byte
Public H2 As Byte
Public L1 As Byte
Public L2 As Byte
Public Function Convert() As Integer
Dim M As New MemoryStream()
Dim bR As IO.BinaryReader
Dim bW As New IO.BinaryWriter(M)
Swap(H1, L2)
Swap(H2, L1)
bW.Write(H1)
bW.Write(H2)
bW.Write(L1)
bW.Write(L2)
M.Seek(0, SeekOrigin.Begin)
bR = New IO.BinaryReader(M)
Convert = bR.ReadInt32()
End Function
End Structure
It looks like you want a C chunk of code that uncompresses some zlib compressed data.
In that case is it possible for you to actually use zlib and just feed the zlib data to it. The zlib homepage: http://www.zlib.net/.
If I got it wrong, could you be specific what is the language that should be used for uncompressing the data and why zlib would not be a choice?
//int length;
byte[] bigEndianBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(length))
Conversely:
//byte[] bigEndianBytes;
int length = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(bigEndianBytes))
It wasn't clear to me from your question whether you want to prepend the length in VB so that it is suitable for direct use by qUncompress or whether you wanted to use the VB produced data as it is now and prepend the length in C++ before calling qUncompress.
Mike G has posted a VB solution. If you want to do it in C++ then you have two choices, either add the length at the start of the QByteArray or call zlib's uncompress directly. In both cases the Qt source for qCompress and qUncompress (corelib/tools/qbytearray.cpp) are a good reference.
This is how qCompress adds the length (nbytes) to bazip, the compressed data:
bazip[0] = (nbytes & 0xff000000) >> 24;
bazip[1] = (nbytes & 0x00ff0000) >> 16;
bazip[2] = (nbytes & 0x0000ff00) >> 8;
bazip[3] = (nbytes & 0x000000ff);
where bazip is the result QByteArray
Alternatively if you want to call uncompress directly, instead of using the qUncompress wrapper the call it uses is
baunzip.resize(len);
res = ::uncompress((uchar*)baunzip.data(), &len,
(uchar*)data+4, nbytes-4);
where baunzip is a QByteArray. In your case you would drop the +4 and -4 since your data does not have the length prepended to it.
Thank you for all your help, it was useful.
The I got the code working with:
char slideStr[currentCompressedLen];
int slideByteRead = in.readRawData(slideStr, currentCompressedLen);
QByteArray aInCompBytes = QByteArray(slideStr, slideByteRead);
aInCompBytesPlusLen = aInCompBytes;
aInCompBytesPlusLen.prepend(QByteArray::number(currentUnCompressedLen));
aInUnCompBytes.resize(currentUnCompressedLen);
aInUnCompBytes = qUncompress(aInCompBytesPlusLen);