3

In the attempt to learn both vb.net and C# better I am taking a project (TreeViewAdv found on SourceForge) and trying to convert its code to VB. Though I hope to get to the point where I can convert the code manually (as this is a learning project), I am currently using a C# to VB code converter (found at www.DeveloperFusion.com) to get the ball rolling and some basic understanding first. The code I have converted is mostly problem free, but there is one problem (maybe 2). See Code:

C#

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;

namespace Aga.Controls
{
public static class BitmapHelper
{
    [StructLayout(LayoutKind.Sequential)]
    private struct PixelData
    {
        public byte B;
        public byte G;
        public byte R;
        public byte A;
    }

    public static void SetAlphaChanelValue(Bitmap image, byte value)
    {
        if (image == null)
            throw new ArgumentNullException("image");
        if (image.PixelFormat != PixelFormat.Format32bppArgb)
            throw new ArgumentException("Wrong PixelFormat");

        BitmapData bitmapData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height),
                                 ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
        unsafe
        {
            PixelData* pPixel = (PixelData*)bitmapData.Scan0;
            for (int i = 0; i < bitmapData.Height; i++)
            {
                for (int j = 0; j < bitmapData.Width; j++)
                {
                    pPixel->A = value;
                    pPixel++;
                }
                pPixel += bitmapData.Stride - (bitmapData.Width * 4);
            }
        }
        image.UnlockBits(bitmapData);
    }
}
}

VB.Net (Post Conversion)

Imports System.Collections.Generic
Imports System.Text
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Drawing.Imaging

Namespace Aga.Controls

Public NotInheritable Class BitmapHelper

    Private Sub New()
    End Sub

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure PixelData
        Public B As Byte
        Public G As Byte
        Public R As Byte
        Public A As Byte
    End Structure


    Public Shared Sub SetAlphaChanelValue(ByVal image As Bitmap, ByVal value As Byte)
        If image Is Nothing Then
            Throw New ArgumentNullException("image")
        End If
        If image.PixelFormat <> PixelFormat.Format32bppArgb Then
            Throw New ArgumentException("Wrong PixelFormat")
        End If

        Dim bitmapData As BitmapData = image.LockBits(New Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
        Dim pPixel As Pointer(Of PixelData) = DirectCast(bitmapData.Scan0, Pointer(Of PixelData))
        For i As Integer = 0 To bitmapData.Height - 1
            For j As Integer = 0 To bitmapData.Width - 1
                pPixel.A = value
                pPixel += 1
            Next
            pPixel += bitmapData.Stride - (bitmapData.Width * 4)
        Next
        image.UnlockBits(bitmapData)
    End Sub

End Class

End Namespace

The lines of code in questions, I believe, are: C#

PixelData* pPixel = (PixelData*)bitmapData.Scan0;

Which converts to this in VB

Dim pPixel As Pointer(Of PixelData) = DirectCast(bitmapData.Scan0, Pointer(Of PixelData))

Intellisense tells me that there is something wrong with Pointer(Of PixelData)

My Question: What is the best way to get the VB code above to function like the C# code (in results not methodology), and why the solution works (I wish to understand the reasoning)?

Things that should be kept in mind when answering or commenting:

1) I already understand that CRL or managed programming languages do not use pointers

2) I am not trying to find a way to use pointers or unsafe code in vb.net I know this cannot be done.

3) I am not going to ditch VB nor do I wish to simply keep the C# code in a separate assembly and reference it from VB. I understand it has limitations, but this is a LEARNING project so that when I go to a job interview and they asking me, "Can you do X in VB?" I can confidently say yes.

4) Again, what is paramount here is not which road I take but the destination. I understand that there is a shorter road to the destination through C#, but I wish to go through VB.

For bonus points, I expect the statement

pPixel += 1

to not work on the VB side (as pPixel is a structure, see above), but it builds fine on the C# side. Why does this (work/not work) in the respective languages. This is NOT a question about safe/unsafe code blocks, so much as when the code ACTUALLY RUNS and does not throw an error because of inappropriate casting/type use (int 1 --> struct pPixel), WHY?

10
  • 3
    C# supports "unsafe" code, including pointers, in projects which have the "allow unsafe" option. This isn't supported in vb... Commented Oct 31, 2013 at 3:56
  • @ReedCopsey. Yeah, I already know that, as I stated in the question. What I asked was, "what is the BEST way to get this code to work and why?" Commented Oct 31, 2013 at 4:46
  • 2
    Only C# can use unsafe code. Therefore your best option is to ditch VB.Net and use only C#. Your second best option is to keep the unsafe code in a C# assembly and reference that C# assembly from a VB.Net project. Commented Oct 31, 2013 at 6:43
  • Not really relevant in the context of your question, but are you sure this "BitmapHelper" is actually needed for the core functionality of TreeViewAdv? Commented Oct 31, 2013 at 16:56
  • @Idle_Mind. 1.) I understand that only C# can use unsafe code. I am not asking, "How do I get VB to use unsafe code?" Nor am I asking, "How do I use pointers in VB?" What I am asking is "Given the limitations of VB.net, how do I get the VB code to successfully function as closely as possible to the C# code?" Accepting that it will likely not be as efficient as the C# code. The destination is key here, not the methodology. 2.) I COULD ditch VB but then when I go to a job interview and they tell me they really need a VB programmer should I just tell THEM to ditch VB? Commented Oct 31, 2013 at 17:22

2 Answers 2

5

No pointer support in VB.NET, you need to fall back to the framework support functions. Marshal.WriteByte fits the ticket:

    Dim pPixel = bitmapData.Scan0
    For y As Integer = 0 To bitmapData.Height - 1
        For x As Integer = 0 To bitmapData.Width - 1
            Marshal.WriteByte(pPixel, 4 * x + 3, value)
        Next
        pPixel += bitmapData.Stride
    Next

And of course never dismiss the excellent language interop supported by .NET. A VB.NET program can very easily use a C# class library project.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, to the point (though I have a bit of work to do before I can confirm). I do have a question, however. Why the +3 in "4 * x + 3"? Is there something in the nature of the objects that we are working with that requires such an offset or was the number just illustrative?
It is because the alpha byte is at offset 3 in a 32-bit pixel. Like your PixelData structure says: offset 0=blue, 1=green, 2=red, 3=alpha. VB.NET programmers have to understand the machine better :)
1

Unfortunately, VB.Net doesn't support direct manipulation of the data via pointers. This is one of the significant disadvantages of VB when compared to C# - no access to unsafe code when you need it.

The way around it, in this case, is actually to copy from the IntPtr to a byte array, perform your manipulations, then copy back. This can be done with the Marshal.Copy method.

The Bitmap.LockBits sample code on MSDN for VB shows the entire process, and how to manipulate a bitmap's pixel data from VB.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.