Visual Basic Development Bookmark and Share   
 Home > Visual Basic Language > An unexpected problem using Lockbits and an ASM DLL
 

An unexpected problem using Lockbits and an ASM DLL

In duplicated code using Lockbits in VB to obtain the address of Bitmap.Scan0, the first
is OK, in all applications, but the second NEVER is. What is the explanation please? Surely
both StartAddrA and StartAddrB should be valid pointers to Bitmap.Scan0 for calls to valid,
functional ASM code in the functional DLL. The error is, it seems, in the VB Lockbits
process. Using VS2010 Beta 2 and XP.

Rgeards,
Ron

In the VB test-DLL program

Public Class Form1
Public Declare Function ProcSingleImage Lib "c:\CODE TEST\TestVBAsmCppDll-01\debug\_
TestCppAtlDllAsm.dll" (ByVal ProcID As Int32, ByVal StartAddrA As IntPtr, ByVal Nbytes As Int32, _
ByVal modData As Byte) As Boolean
Public Declare Function ProcDoubleImage Lib "c:\CODE TEST\TestVBAsmCppDll-01\debug\_
TestCppAtlDllAsm.dll" (ByVal ProcID As Int32, ByVal StartAddrA As IntPtr, ByVal StartAddrB As IntPtr, _
ByVal Nbytes As Int32, ByVal modData As Byte) As Boolean

Public ProcID As Int32

''One image
Public Const ProcIDNull As Integer = 0
Public Const ProcIDBiasR As Integer = 1
Public Const ProcIDScaleR As Integer = 2
Public Const ProcIDBiasG As Integer = 3
Public Const ProcIDScaleG As Integer = 4
Public Const ProcIDBiasB As Integer = 5
Public Const ProcIDScaleB As Integer = 6
Public Const ProcIDBiasW As Integer = 7
Public Const ProcIDScaleW As Integer = 8
Public Const ProcIDInv As Integer = 9

''Two images
Public Const ProcDoubleSub As Integer = 10

Public StartAddrA As IntPtr
Public StartAddrB As IntPtr
Public nBytes As Int32
Public nPix As Int32

Public bmp1 As Bitmap ''GLOBAL therefore safe from GC!!?
Public bmp2 As Bitmap ''GLOBAL therefore safe from GC!!?

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim ofd As New OpenFileDialog
ofd.ShowDialog(Me)
If ofd.FileName <> "" Then
''ORIGINAL APPROACH - bmp1.scan0 OK; bmp2.scan0 error
''PictureBox1.Load(ofd.FileName)
''bmp1 = PictureBox1.Image
''bmp2 = Bitmap.FromHbitmap(bmp1.GetHbitmap)

''SECOND APPROACH -bmp1.scan0 OK; bmp2 scan0 error
bmp1 = Bitmap.FromFile(ofd.FileName)
bmp2 = Bitmap.FromFile(ofd.FileName)
PictureBox1.Image = bmp1

nBytes = bmp1.Width * bmp1.Height * 3
Dim bmpData As System.Drawing.Imaging.BitmapData
Dim rect As New Rectangle(0, 0, bmp1.Width, bmp1.Height)

bmpData = bmp1.LockBits(rect, Drawing.Imaging.ImageLockMode.ReadWrite, bmp1.PixelFormat)
StartAddrA = bmpData.Scan0 ''This address OK for all of numerous operations
bmp1.UnlockBits(bmpData)

Dim bmpData2 As System.Drawing.Imaging.BitmapData
bmpData2 = bmp2.LockBits(rect, Drawing.Imaging.ImageLockMode.ReadWrite, bmp2.PixelFormat)
StartAddrB = bmpData2.Scan0 ''This address fails (protected memory error)
bmp2.UnlockBits(bmpData2)

TextBox1.Text = StartAddrA.ToString
TextBox2.Text = StartAddrB.ToString

End If
End Sub

Private Sub PictureBox1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles_
PictureBox1.Click
''To compare the bitmaps during processing
Static img As Byte
If img = 0 Then img = 2
If img = 1 Then
img = 2
PictureBox1.Image = bmp2
PictureBox1.Refresh()
Else
img = 1
PictureBox1.Image = bmp1
PictureBox1.Refresh()
End If
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles_
Button1.Click
''BLUE BIAS correct for StartAddrA but not StartAddrB
Dim res As Boolean = False
res = ProcSingleImage(ProcIDBiasB, StartAddrA, nBytes, 100)
If res = True Then
TextBox2.Text = "BLUE BIAS"
Else
TextBox2.Text = ""
End If
''PictureBox1.Image = bmp1''not needed
PictureBox1.Refresh() ''REQUIRED
End Sub

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles_
Button2.Click
''INVERT
''OK but ONLY if StartAddrA is used and NOT StartAddrB
Dim res As Boolean = False
res = ProcSingleImage(ProcIDInv, StartAddrA, nBytes, 100)
If res = True Then
TextBox2.Text = "INVERT"
Else
TextBox2.Text = ""
End If
''PictureBox1.Image = bmp1''not needed
PictureBox1.Refresh() ''REQUIRED
End Sub

Private Sub Button6_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles_
Button6.Click
''Subtracts BMP2 data from BMP1
''OK but ONLY if BMP1 is used (ie called with StartAddrA in place of StartAddrB
Dim res As Boolean = False
res = ProcDoubleImage(ProcDoubleSub, StartAddrA, StartAddrB, nBytes, 100)
If res = True Then
TextBox2.Text = "DOUBLE SUB"
Else
TextBox2.Text = ""
End If
PictureBox1.Refresh() ''REQUIRED

End Sub
End Class

In the ATL DLL:

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

extern "C" BOOL __stdcall ProcSingleImage(int ProcID, char * StartAddr, int Nbytes, char modData )
/* Simple Single Image Processing*/
/* Both OK but ONLY if StartAddrA is used - NOT if StartAddrB used */
{

switch (ProcID)
{
case ProcIDNull:;
/*Null entry warning*/
return false;
break;

case ProcIDBiasB:;
/*Add bias to BLUE*/
_asm
{
push eax;preserve register
push ebx;preserve register
mov ebx, nPix;enter loop size
mov eax, StartAddr;enter loop start offset 0 = BLUE
loopSingle1:
mov cl,modData;
adc [eax],cl;process the byte (pixel colour)
jnc nocSingle1;
mov [eax],255;
nocSingle1:
inc eax;move to pixel = GREEN
inc eax;move to pixel = RED
inc eax;move to next pixel = BLUE
dec ebx;count down pixel
jnz loopSingle1;if count is not zero repeat
pop ebx;restore register
pop eax;restore register
}
return true;
break;

case ProcIDInv:;
/*INVERT*/
_asm
{
push eax;preserve register
push ebx;preserve register
mov ebx, Nbytes;enter loop size
mov eax, StartAddr;enter loop start offset 0 = WHITE
loopInv:
not [eax];invert the byte (pixel colour)
inc eax;move to next byte
dec ebx;count down byte
jnz loopInv;if count is not zero repeat
pop ebx;restore register
pop eax;restore register
}
return true;
break;

default:;
return false;
}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
extern "C" BOOL __stdcall ProcDoubleImage(int ProcID, char * StartAddrA, char * StartAddrB, int nBytes, char modData )
/* Simple Double Image Processing (eg darks, dark subtraction etc) */
/* If StartAddrA is used in both eax and ebx the image is 'delted' as it
should be. However if StartAddrB is used there is a 'read protected
memory' errror.
{

switch (ProcID)
{
case ProcIDNull:;
/*Null entry warning*/
return false;
break;

case ProcDoubleSub:;
/*Sub two images = A - B */
_asm
{
push eax;preserve register
push ebx;preserve register
push ecx;preserve register
push edx;preserve register

mov eax, StartAddrA;enter loop A start
mov ebx, StartAddrB;enter loop B start
mov ecx, nBytes;enter loop size
loopDouble1:
mov dl,[eax];get image A byte
sub dl,[ebx];sub image B byte
jns nosDouble1;
mov dl,0;
nosDouble1:
mov [eax],dl;set result
inc eax;move to next image A BYTE
inc ebx;move to next image B BYTE
dec ecx;count bytes
jnz loopDouble1;if count is not zero repeat

pop edx;restore register
pop ecx;restore register
pop ebx;restore register
pop eax;restore register
}
return true;
break;

default:;
return false;
}
}

  • Edited byRegnwald Sunday, November 29, 2009 9:51 PMextra tag
  •  
Regnwald  Sunday, November 29, 2009 1:27 PM
You shouldn't call UnlockBits until you're done with your processing. Or in other words, you should consider BitmapData.Scan0 to be invalid after UnlockBits is called.

Mattias, C# MVP
Mattias Sjögren  Monday, November 30, 2009 9:30 AM

G'day, Mattial,

Thanks for the reply. I felt sure the problem was not my ASM but an oddity of managed/unmanaged code. But a couple of good scotches to drown the blues and a good nights sleep (and a late rise) I looked again.

1) Commenting out the code in te loops and both StartAddrA and StartAddrB are correctly handled. putting 0 in [StartAddrA] or [StartAddrB] clears the images.
2) The error seems to be simplistic: xor edx,edx; then mov dl, [StartAddrB]; instead of mov dx [StartAddrB]; then mov [StartAddrB],dl; instead of mov [StartAddrB],dx; !!!
3)The ''protected memory warning focussed me on the looping through memory instead of the use of registers

Regarding Lockbits, bitmap.scan0 is the only address we have for variables (?) rather than methods. I am assuming that global and static vaiables are NOT affected by GC(!?) in managedcode,and am exploring these issues. Indeed, I am exploing the use of Lockbits to store arrays accesible to ASM, and to blazes with the MS reluctance to open up pointer use to VB/C#. I know, native C++ for pointers and ASM. But I havelost two projects to the failure of the form designer in C++ but NEVER (yet) in VB.

ASM useage is not compatible with managed code, in any languagr (?). But to use ASM efficiently one needs pointers to stable addresses. This seems one way to do it. A look-up table is just an array and a bitmap is just an array. MS could give us pointers to read in global or static memory any time they damn well wanted to but it seems they just damn well don't want to. Yet it would be a very easy way to start to woo back the many (of us) disgruntled VB5/VB6 warriors of old.

Sorry for all the pontification. I thought it was 30 years ago I last needed ASM but on checking the dates of old ASM libraries it was 38 years ago. I was using Fortran and Algol in those days before I got lazy! It was string manipulation that started me in Basic and VB1.0!

Regards,
This (and its siblings) a very valuable forum. A good number of contributors with good (often interesting, sometimes surprising) advice.

Ron

So, for the time being, this is the path I'm trying

Regnwald  Tuesday, December 01, 2009 3:29 AM

You can use google to search for other answers

Custom Search

More Threads

• Trap Mouse Cursor
• Updating version of MS Access database
• How to click Post Reply Button on Gaia Forums Then Type Message?
• Build a object that has a property type arraylist
• How does the user print what they type in a text box?
• Connection to lotus notes
• about Type class
• Get ISP Details
• Same problem different words. How do I do a 'look up' for a non-keyed field
• Get Mapped Drive Label?