thrown exception in Marshal.PtrToStructure

S

Stephanie Doherty

Hello World,

I am trying to read an intptr() returned from a call to EnumPrinters into a
structure and keep getting an exception. The relevant code looks like:

Public Structure pInfo
<MarshalAs(UnmanagedType.LPStr)> Dim flags As String
<MarshalAs(UnmanagedType.LPStr)> Dim pDescription As String
<MarshalAs(UnmanagedType.LPStr)> Dim pName As String
<MarshalAs(UnmanagedType.LPStr)> Dim pComment As String
End Structure

<DllImport("winspool.drv", EntryPoint:="EnumPrinters", _
SetLastError:=True, CharSet:=CharSet.Unicode)> Public Shared Function _
EnumPrinters(ByVal flags As Int32, ByVal pName As String, ByVal Level _
As Int32, ByVal pPrinterEnum As IntPtr, ByVal cbBuf As Int32, ByRef _
pcbNeeded As Int32, ByRef pcReturned As Int32) As Int32
End Function


selectedItem = "\\" & CmB_Servers.SelectedItem
server = selectedItem.ToString()
EnumPrinters(flag, server, PRINTER_LEVEL_1, outC, 0, pcbNeeded,
pcReturned)
outC = Marshal.AllocHGlobal(pcbNeeded + 1)
If EnumPrinters(flag, server, PRINTER_LEVEL_1, outC, pcbNeeded,
pcbNeeded, pcReturned) > 0 Then

Dim manyPr As pInfo
Dim currentP As IntPtr = outC
Dim i As Integer

For i = 1 To pcReturned

manyPr = Marshal.PtrToStructure(currentP, manyPr.GetType())
CmB_Printers.Items.Add(manyPr.pName)
currentP = IntPtr.op_Explicit(currentP.ToInt32 +
Marshal.SizeOf(manyPr.GetType))
Next
Else
MsgBox("unable to get printers")
End If
Marshal.FreeHGlobal(outC)

----
The exception thrown is: Object reference not set to an instance of an object
I assume that is referring to the manyPr variable, but am not sure what I
should be doing about it.

Help!

Thanks,
Stephanie
 
B

Bill McCarthy

First, turn on Option Strict, then try this :

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
Public Structure pInfo
<MarshalAs(UnmanagedType.LPTStr)> Dim flags As String
<MarshalAs(UnmanagedType.LPTStr)> Dim pDescription As String
<MarshalAs(UnmanagedType.LPTStr)> Dim pName As String
<MarshalAs(UnmanagedType.LPTStr)> Dim pComment As String
End Structure




<DllImport("winspool.drv", EntryPoint:="EnumPrintersW", _
SetLastError:=True, CharSet:=CharSet.Unicode)> Public Function _
EnumPrinters(ByVal flags As Int32, ByVal pName As String, ByVal Level _
As Int32, ByVal pPrinterEnum As IntPtr, ByVal cbBuf As Int32, ByRef _
pcbNeeded As Int32, ByRef pcReturned As Int32) As Int32
End Function


For i = 1 To pcReturned

manyPr = CType(Marshal.PtrToStructure(currentP, GetType(pInfo)),
pInfo)
CmB_Printers.Items.Add(manyPr.pName)
currentP = IntPtr.op_Explicit(currentP.ToInt32 +
Marshal.SizeOf(GetType(pInfo)))
Next
 
S

Stephanie Doherty

Thank you for your suggestion,Bill. Unfortunately this throws an exception
(Error: PInvoke item (field,method) must be Static.) in the first call to
EnumPrinter.

The definitions for the variables in that call are:
Public Const PRINTER_ENUM_SHARED As Int32 = 20
Public Const PRINTER_ENUM_NAME As Int32 = 8
Public Const PRINTER_LEVEL_1 As Int32 = 1
Dim pcbNeeded As Int32 = 0
Dim pcReturned As Int32 = 0
Dim flag As Int32 = PRINTER_ENUM_NAME Or PRINTER_ENUM_SHARED

What did I miss?
Stephanie
 
S

Stephanie Doherty

Ok. Setting the EnumPrinters function as Shared fixed that exception.
However, once past that, I am still getting the original exception (Object
reference not set to an instance of an object) for the line:

manyPr = Marshal.PtrToStructure(currentP, manyPr.GetType())
 
B

Bill McCarthy

go back and read the code in my first reply

Stephanie Doherty said:
Ok. Setting the EnumPrinters function as Shared fixed that exception.
However, once past that, I am still getting the original exception (Object
reference not set to an instance of an object) for the line:

manyPr = Marshal.PtrToStructure(currentP, manyPr.GetType())
 
S

Stephanie Doherty

I believe I already did. Unless I missed something, my changes should have
consisted of adding the line:
StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _

changing the declaration for EnumPrinters to:
<DllImport("winspool.drv", EntryPoint:="EnumPrintersW", _

and the code for the line that causes the exception to:
manyPr = CType(Marshal.PtrToStructure(currentP, GetType(pInfo)), pInfo)

plus the substitution a couple of lines later to:
currentP = IntPtr.op_Explicit(currentP.ToInt32 +
Marshal.SizeOf((GetType(pInfo))))


but I'm still getting the exception.
 
B

Bill McCarthy

and change the strings to UnmanagedType.LPTStr


Stephanie Doherty said:
I believe I already did. Unless I missed something, my changes should have
consisted of adding the line:
StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _

changing the declaration for EnumPrinters to:
<DllImport("winspool.drv", EntryPoint:="EnumPrintersW", _

and the code for the line that causes the exception to:
manyPr = CType(Marshal.PtrToStructure(currentP, GetType(pInfo)), pInfo)

plus the substitution a couple of lines later to:
currentP = IntPtr.op_Explicit(currentP.ToInt32 +
Marshal.SizeOf((GetType(pInfo))))


but I'm still getting the exception.
 
S

Stephanie Doherty

Here goes:

Option Strict On
Imports System
Imports System.IO
Imports System.Text
Imports System.Runtime.InteropServices
Imports System.ComponentModel
Imports System.Management

Public Class Form1
Inherits System.Windows.Forms.Form
Public Const PRINTER_ENUM_SHARED As Int32 = 20
Public Const PRINTER_ENUM_NAME As Int32 = 8
Public Const PRINTER_LEVEL_1 As Int32 = 1
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
Public Structure pInfo
<MarshalAs(UnmanagedType.LPStr)> Dim flags As String
<MarshalAs(UnmanagedType.LPStr)> Dim pDescription As String
<MarshalAs(UnmanagedType.LPStr)> Dim pName As String

<DllImport("winspool.drv", EntryPoint:="EnumPrintersW", _
SetLastError:=True, CharSet:=CharSet.Unicode)> Public Shared Function _
EnumPrinters(ByVal flags As Int32, ByVal pName As String, ByVal Level _
As Int32, ByVal pPrinterEnum As IntPtr, ByVal cbBuf As Int32, ByRef _
pcbNeeded As Int32, ByRef pcReturned As Int32) As Int32
End Function
Public Sub getServerPrinters()
Dim pcbNeeded As Int32 = 0
Dim pcReturned As Int32 = 0
Dim outC As IntPtr = IntPtr.Zero
Dim flag As Int32 = PRINTER_ENUM_NAME Or PRINTER_ENUM_SHARED
Dim selectedItem As Object
Dim server As String
Dim status As Int32


selectedItem = "\\" + CmB_Servers.SelectedItem.ToString
server = selectedItem.ToString()
EnumPrinters(flag, server, PRINTER_LEVEL_1, outC, 0, pcbNeeded,
pcReturned)
outC = Marshal.AllocHGlobal(pcbNeeded + 1)
If EnumPrinters(flag, server, PRINTER_LEVEL_1, outC, pcbNeeded,
pcbNeeded, pcReturned) > 0 Then

Dim manyPr As pInfo
Dim currentP As IntPtr = outC
Dim i As Integer

For i = 1 To pcReturned

manyPr = CType(Marshal.PtrToStructure(currentP,
GetType(pInfo)), pInfo)
CmB_Printers.Items.Add(manyPr.pName)
currentP = IntPtr.op_Explicit(currentP.ToInt32 +
Marshal.SizeOf((GetType(pInfo))))
Next
Else
MsgBox("unable to get printers")
End If
Marshal.FreeHGlobal(outC)

End Sub
....
End Class
 
B

Bill McCarthy

You have the structure declared wrong. Note it should be LPTStr, not LPStr
on each of the four fields. See my first reply again.
 
S

Stephanie Doherty

I'm afraid that doesn't do it either.

Bill McCarthy said:
You have the structure declared wrong. Note it should be LPTStr, not LPStr
on each of the four fields. See my first reply again.
 
B

Bill McCarthy

try this :

Option Strict On
Imports System
Imports System.IO
Imports System.Text
Imports System.Runtime.InteropServices
Imports System.ComponentModel
Imports System.Management

Public Class Form1
Inherits System.Windows.Forms.Form

Public Const PRINTER_ENUM_SHARED As Int32 = 20
Public Const PRINTER_ENUM_NAME As Int32 = 8
Public Const PRINTER_LEVEL_1 As Int32 = 1


<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
Public Structure pInfo
Dim flags As Int32
<MarshalAs(UnmanagedType.LPTStr)> Dim pDescription As String
<MarshalAs(UnmanagedType.LPTStr)> Dim pName As String
<MarshalAs(UnmanagedType.LPTStr)> Dim pComment As String
End Structure

<DllImport("winspool.drv", EntryPoint:="EnumPrintersW", _
SetLastError:=True, CharSet:=CharSet.Unicode)> Public Shared Function _
EnumPrinters(ByVal flags As Int32, ByVal pName As String, ByVal Level _
As Int32, ByVal pPrinterEnum As IntPtr, ByVal cbBuf As Int32, ByRef _
pcbNeeded As Int32, ByRef pcReturned As Int32) As Int32
End Function


Public Sub getServerPrinters()
Dim pcbNeeded As Int32 = 0
Dim pcReturned As Int32 = 0
Dim outC As IntPtr = IntPtr.Zero
Dim flag As Int32 = PRINTER_ENUM_NAME Or PRINTER_ENUM_SHARED
Dim server As String


server = "\\" & My.Computer.Name ' add code here for server name
EnumPrinters(flag, server, PRINTER_LEVEL_1, outC, 0, pcbNeeded,
pcReturned)

outC = Marshal.AllocHGlobal(pcbNeeded)

If EnumPrinters(flag, server, PRINTER_LEVEL_1, outC, pcbNeeded,
pcbNeeded, pcReturned) > 0 Then

Dim manyPr As pInfo
Dim currentP As IntPtr = outC
Dim i As Integer

For i = 1 To pcReturned

manyPr = CType(Marshal.PtrToStructure(currentP, GetType(pInfo)),
pInfo)
Debug.Print(manyPr.pName)
currentP = IntPtr.op_Explicit(currentP.ToInt32 +
Marshal.SizeOf((GetType(pInfo))))
Next
Else
MsgBox("unable to get printers")
End If
Marshal.FreeHGlobal(outC)

End Sub
 
S

Stephanie Doherty

That worked perfectly! I was then able to make the substitutions for my
system and it's all good. Thank you so much, Bill.

Stpehanie
 
B

Bill McCarthy

Glad I could help :)

Stephanie Doherty said:
That worked perfectly! I was then able to make the substitutions for my
system and it's all good. Thank you so much, Bill.

Stpehanie
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top