ProtoNoob
ProtoNoob

Reputation: 301

Problematic Conversion of Pointers From C# code to VB.net

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?

Upvotes: 3

Views: 2206

Answers (2)

Hans Passant
Hans Passant

Reputation: 942128

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.

Upvotes: 5

Reed Copsey
Reed Copsey

Reputation: 564771

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.

Upvotes: 1

Related Questions