thrown exception in Marshal.PtrToStructure

  • Thread starter Thread starter Stephanie Doherty
  • Start date Start date
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
 
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
 
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
 
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())
 
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())
 
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.
 
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.
 
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
 
You have the structure declared wrong. Note it should be LPTStr, not LPStr
on each of the four fields. See my first reply again.
 
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.
 
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
 
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
 
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
 
Back
Top