News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Filling in a VB Structure from a DLL

Started by Robert Collins, November 11, 2005, 10:14:49 PM

Previous topic - Next topic

Robert Collins

I am having a big problem understanding what is going on in the following example code snippits.

I have a VB program that calls two functions in a 'C' written DLL. The function that puzzles me is the second call statement in the VB program to the DLL function 'FillIconResourceStructure'. 

Here is what I get from debugging both the DLL and the VB program......

In the DLL after executing the 7 statements I get the following results:

lpIRS->IconImages[1].Width = 32
lpIRS->IconImages[1].Height = 32
lpIRS->IconImages[1].Colors = 4
lpIRS->IconImages[1].lpBits = 20513888
lpIRS->IconImages[1].dwNumBytes = 744
lpIRS->IconImages[1].lpXOR = 20513992
lpIRS->IconImages[1].lpAND = 20514504

I am assuming that these results are correct but it wouldn't matter anyway since the results are not the topic of my problem.

After returning to the VB program from the DLL I see that the structure contains the following data:

MyIconResource.IconImages(1).Width  = 32
MyIconResource.IconImages(1).Height = 32
MyIconResource.IconImages(1).Colors = 4
MyIconResource.IconImages(1).lpBits = 70387632     '<------?????
MyIconResource.IconImages(1).dwNumBytes = 744
MyIconResource.IconImages(1).lpXOR = 70387736    '<------?????
MyIconResource.IconImages(1).lpAND = 70388248    '<------?????

As you can see there are 3 values that didn't get returned correctly.

I have to believe that I have made the VB structures correctly and since I do get 4 values back the same as they were computed in the DLL I cannot imagine why I do not get back the same values for the 3 that were returned incorrectly.

Here is the DLL snippit code:

  '
  '
typedef struct
{
  UINT          Width;      // Width
  UINT          Height;     // Height
  UINT          Colors;     // bpp
  LPBYTE        lpBits;     // ptr to DIB bits
  DWORD         dwNumBytes; // how many bytes?
  LPBYTE        lpXOR;      // ptr to XOR image bits
  LPBYTE        lpAND;      // ptr to AND image bits
  LPBITMAPINFO  lpbi;       // ptr to header
} ICONIMAGE, *LPICONIMAGE;

typedef struct
{
  BOOL          bHasChanged;                     // Has image changed?
  TCHAR         szOriginalICOFileName[MAX_PATH]; // Original name
  TCHAR         szOriginalDLLFileName[MAX_PATH]; // Original name
  UINT          nNumImages;                      // How many images?
  ICONIMAGE     IconImages[1];                   // Image entries
} ICONRESOURCE, *LPICONRESOURCE;

'
'
'
LONG _stdcall FillIconResourceStructure(LONG lpID,
                                        LPCTSTR szFileName,
                                        LPEXEDLLICONINFO lpLibraryResource,
                                        LPICONRESOURCE lpIRS)
{
    '
    '
    '
    '  Fill in the icon structure from the VB program
    '
  lpIRS->IconImages[1].Width = lpImage->lpbi->bmiHeader.biWidth;
  lpIRS->IconImages[1].Height = lpImage->lpbi->bmiHeader.biHeight)/2;
  lpIRS->IconImages[1].Colors = lpImage->lpbi->bmiHeader.biPlanes * lpImage->lpbi->bmiHeader.biBitCount;
  lpIRS->IconImages[1].lpBits = malloc(lpIRS->IconImages.dwNumBytes);
  lpIRS->IconImages[1].dwNumBytes = SizeofResource(lpLibraryResource, hRsrc);
  lpIRS->IconImages[1].lpXOR = VBFindDIBBits((LPSTR)lpImage->lpbi);
  lpIRS->IconImages[1].lpAND = lpImage->lpXOR + (lpImage->Height*VBBytesPerLine((LPBITMAPINFOHEADER)(lpImage->lpbi)));
    '
    '
    '
  return 0;
}


Here is the VB snippit code:

Type ICONIMAGE
  Width As Long                              ' Width
  Height As Long                             ' Height
  Colors As Long                             ' Colors
  lpBits As Long                             ' ptr to DIB bits
  dwNumBytes As Long                         ' how many bytes?
  lpXOR As Long                              ' ptr to XOR image bits
  lpAND As Long                              ' ptr to AND image bits
  lpbi As BITMAPINFO                         ' ptr to header
End Type

Type ICONRESOURCE
  bHasChanged As Boolean                     ' Has image changed?
  a As Byte                                  ' filler for VB Boolean (16 bits) to be same length as C BOOL (32 bits)
  b As Byte                                  '   "     "      "        "      "       "      "     "      "
  szOriginalICOFileName As String * MAX_PATH ' Original name
  szOriginalDLLFileName As String * MAX_PATH ' Original name
  nNumImages As Long                         ' How many images?
  IconImages(1) As ICONIMAGE                 ' Image entries
End Type

Declare Function FillIconResourceStructure Lib "Icon.dll" (ByVal iconID As Long, ByVal szFilePath As String, ByVal lpLibraryResource As Long, ByRef lpICONRESOURCE As typeICONRESOURCE) As Long
Declare Function GetLibraryResource Lib "Icon.dll" (ByVal szFilePath As String) As Long

Public Function FillIconResourceStructure()
  Dim FilePath As String
  Dim retVal As Long

  Dim hLibrary As Long
  Dim MyIconResource As typeICONRESOURCE

  FilePath = "C:\Windows\System\SHELL32.DLL"

  '
  ' Get a handle to the Library Resource
  '
  hLibrary = GetLibraryResource(ByVal FilePath)

  '
  ' Fill in the structure with the icon data
  '
  retVal = FillIconResourceStructure(ByVal 1, ByVal FilePath, hLibrary, MyIconResource)
End Function

sluggy

I am rusty on this as it has been several years since i did this sort of stuff. The problem appears to be that you are passing the structure (MyIconResource) incorrectly - you need to change your declare. Try passing it ByVal and As Any, otherwise VB tries to do tricky stuff for you. What appears to be happening is that you end up passing a pointer to a pointer, or that VB thinks the return is a pointer to a pointer. Like i said, i am rusty. In any case, arrays in VB are actually SAFEARRAYs (a COM type), and ordinary C cannot treat them as a normal array without destroying them

One approach that i know *will* work is to actually pass a fixed length byte buffer ByVal and As Any - to the C dll this still looks like a struct, so it fills it normally. Then when you recieve it back, you "deserialise" the byte buffer back into a proper struct - this is the method that is used when dealing with arrays of structs. I have made various posts on this in the past, see if you can search them out. I may also still have around a personal email from Matt Curland describing how to deal with structs and arrays, i will see if i can find it, but no promises as that was about 4 or 5 years ago :)



AeroASM

Note that the returned values are always 49873744 bigger than they should be.

Quick fix: in your VB code, subtract this number from the incorrect results.

Long-term solution: maybe this has something to do with signed/unsigned confusion?

Robert Collins

Quote from: sluggy on November 14, 2005, 10:26:57 AM
.....Try passing it ByVal and As Any.....

If I do that I will get a VB error as Type Mismatch on the call statement


  '
  '
Declare Function FillIconResourceStructure Lib "Icon.dll" (ByVal iconID As Long, ByVal szFilePath As String, ByVal lpLibraryResource As Long, ByVal lpICONRESOURCE As Any) As Long

  '
  '
  '
  '
Public Function FillIconResourceStructure()
  '
  '
  '
  retVal = FillIconResourceStructure(ByVal 1, ByVal FilePath, hLibrary, MyIconResource)
 
End Function

Robert Collins

Well, this is all very strange and I am not going to dwell on it any further but as it turns out it I will get the same results if I run the VB program as an .exe. The results I get while running VB in the debug mode appear to be false results but the results I get while running VB in the exe mode yields the same results as calculated by the DLL. Now maybe both results are correct depending on the run mode. I guess the best way to prove it is to now take this application one step further and use those returned results and see if I can produce the actual icon.

zooba

I'm wondering about your use of "IconImages[1]" in the C code. Both C and VB use 0-based arrays, so I would have expected "IconImages[0]" here. I'm not 100% on C types, but you might want to try replacing LPBYTE with a simple 32-bit integer, as that is what VB is expecting. It may be dereferencing somewhere along the line because it is a pointer type.

'ByRef' is the correct way to pass the structure, and 'As ICONRESOURCE' enforces strong typing whereas 'As Any' will let you pass anything.

sluggy

Quote from: zooba on November 16, 2005, 07:15:29 AM
'ByRef' is the correct way to pass the structure, and 'As ICONRESOURCE' enforces strong typing whereas 'As Any' will let you pass anything.
This is correct, and it works for *simple* structs. However, once you start passing arrays from VB to C, even if they are embedded in a struct, the rules change and you have to use a different method.


Robert Collins

Quote from: zooba on November 16, 2005, 07:15:29 AM
I'm wondering about your use of "IconImages[1]" in the C code. Both C and VB use 0-based arrays, so I would have expected "IconImages[0]" here. I'm not 100% on C types, but you might want to try replacing LPBYTE with a simple 32-bit integer, as that is what VB is expecting. It may be dereferencing somewhere along the line because it is a pointer type.

'ByRef' is the correct way to pass the structure, and 'As ICONRESOURCE' enforces strong typing whereas 'As Any' will let you pass anything.

Actually, I didn't write the 'C' side of the project. The 'C' code was written by another programmer and it was originally written as a .EXE application. I just took his code and made a DLL out of it because my boss wanted the .EXE as a VB coded application. The orignal 'C' code program used "IconImage[1]" so I didn't change the structure at all. However, just out of curriosity I changed both the 'C' and the 'VB' structure to a zero-based array but it didn't make any difference in the end results. I don't think that 'C' or 'VB' care wheather you use '0' base or '1' base arrays.

As far as the LPBYTE data type it is already a 32-bit integer. However, it is strange that it was the values of the LPBYTE's that were different and the other values were returned exactly the same as computed in the DLL. Maybe it has to do with the fact that the values that were returned the same were actual values and the ones that returned different were pointers. Perhaps, during Debug, VB moves the pointers up to a higher location. Just a thought.

Anyway, as I stated in my previous post, even though the values were different during the VB debug mode they came out the same in the run mode. In both cases, Debug and Run the end results were correct. I finished the VB application and the icon was prooduced correctly.