[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

microsoft.public.dotnet.framework.interop

GetPrinter Call in x64 caused a FatalExecutionEngineError expectio

SteveYau

1/31/2007 8:05:00 AM

Hi There,

I tried a simple VB.Net console program that uses win32 api call on
WinSpool.drv to query a local or remote printer's sharing status. The API
calls I used are OpenPrinter, GetPrinter and ClosePrinter. The program runs
perfectly on x86 32-bit platform.

However, when I compile the same program on a x64 Windows Server 2003 R2
Ent. and run it (with only slight modification on printer name), it caused a
FatalExecutionEngineError. On VS2005 debug windows, it showed:

---
The error code is 0xC0000005. This error may be a bug in the CLR or in the
unsafe or non-verifiable portions of user code. Common sources of this bug
include user marshaling errors for COM-interop or PInvoke, which may corrupt
the stack.
---

And in Application eventlog, a ".NET RunTime" event 1023 ".NET Runtime
version 2.0.50727.63 - Fatal Execution Engine Error (000006427F880608)
(80131506)" was logged.

Here I attach this small program. Line 116, the second invocation of
GetPrinter, is the place where .Net engine failed.

Grateful if any experts here can give me some hints.

Thanks

Steve

----- Attached sample code ---
Imports System.Runtime.InteropServices

Module Module1

Const PRINTER_ATTRIBUTE_SHARED As Integer = &H8
Const ERROR_INSUFFICIENT_BUFFER As Integer = &H7A

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
Structure PRINTER_INFO_2W
<MarshalAs(UnmanagedType.LPWStr)> Public pServerName As String
<MarshalAs(UnmanagedType.LPWStr)> Public pPrinterName As String
<MarshalAs(UnmanagedType.LPWStr)> Public pShareName As String
<MarshalAs(UnmanagedType.LPWStr)> Public pPortName As String
<MarshalAs(UnmanagedType.LPWStr)> Public pDriverName As String
<MarshalAs(UnmanagedType.LPWStr)> Public pComment As String
<MarshalAs(UnmanagedType.LPWStr)> Public pLocation As String
Public pDevMode As IntPtr
<MarshalAs(UnmanagedType.LPWStr)> Public pSepFile As String
<MarshalAs(UnmanagedType.LPWStr)> Public pPrintProcessor As String
<MarshalAs(UnmanagedType.LPWStr)> Public pDataType As String
<MarshalAs(UnmanagedType.LPWStr)> Public pParameter As String
Public pSecurityDescriptor As IntPtr
Public Attributes As Integer
Public Priority As Integer
Public DefaultPriority As Integer
Public StartTime As Integer
Public UntilTime As Integer
Public Status As Integer
Public cJobs As Integer
Public AveragePPM As Integer
End Structure

<DllImport("winspool.Drv", EntryPoint:="OpenPrinterW", _
SetLastError:=True, CharSet:=CharSet.Unicode, _
ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Function OpenPrinter(ByVal src As String, _
ByRef hPrinter As IntPtr, ByVal pd As Integer) As Boolean
End Function

<DllImport("winspool.Drv", EntryPoint:="ClosePrinter", _
SetLastError:=True, CharSet:=CharSet.Unicode, _
ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Function ClosePrinter(ByVal hPrinter As IntPtr) As Integer
End Function

<DllImport("winspool.Drv", EntryPoint:="GetPrinterW", _
SetLastError:=True, CharSet:=CharSet.Unicode, _
ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
Function GetPrinter(ByVal hPrinter As IntPtr, ByVal Level As Integer, _
ByRef pPrinter As Byte, ByVal cbBuf As Integer, ByRef pcbNeeded As
Integer _
) As Integer
End Function

Sub Main()
Dim buf() As Byte
Dim BytesWritten, ByteLength, rc, DLLErrCode As Integer
Dim bufptr As IntPtr = IntPtr.Zero
Dim bResult As Boolean
Dim pPrinter_Info As PRINTER_INFO_2W
Dim PrintHandle As IntPtr = IntPtr.Zero

Console.WriteLine("Trying to OpenPrinter [\\CCZ166\CCL12]")

' check to see if the printer can be opened.
rc = OpenPrinter("\\CCZ166\CCL12", PrintHandle, 0)
If rc = 0 Then
Console.WriteLine("Cannot OpenPrinter [\\CCZ166\CCL12] Error = [" &
Err.LastDllError & "]")
Exit Sub
End If
Console.WriteLine("Printer [CCL12] Found. Checking Shared Status... ")
ReDim buf(1)

' check to get printer information, pass no buffer and ask for return
buffer size
rc = GetPrinter(PrintHandle, 2, buf(0), 0, BytesWritten)
If rc = 0 Then
DLLErrCode = Err.LastDllError
' Expected to get ERROR_INSUFFICIENT_BUFFER return code
If DLLErrCode <> ERROR_INSUFFICIENT_BUFFER Then
Console.WriteLine("Cannot GetPrinter [CCL12] DLL Error = [" &
DLLErrCode & "]")
ClosePrinter(PrintHandle)
Exit Sub
End If
End If

Console.WriteLine("First GetPrinter Called... BytesWritten = [" &
BytesWritten.ToString & "]")
With pPrinter_Info
.pServerName = ""
.pPrinterName = ""
.pShareName = ""
.pPortName = ""
.pDriverName = ""
.pComment = ""
.pLocation = ""
.pDevMode = IntPtr.Zero
.pSepFile = ""
.pPrintProcessor = ""
.pDataType = ""
.pParameter = ""
.pSecurityDescriptor = IntPtr.Zero
.Attributes = 0
.Priority = 0
.DefaultPriority = 0
.StartTime = 0
.UntilTime = 0
.Status = 0
.cJobs = 0
.AveragePPM = 0
End With

ByteLength = BytesWritten
ReDim buf(ByteLength - 1)

' Get Printer Info and store it in buf
Console.WriteLine("Second GetPrinter Calling... ")

rc = GetPrinter(PrintHandle, 2, buf(0), ByteLength, BytesWritten)
Console.WriteLine("Second GetPrinter Called... ")
If rc = 0 Then
' Unexpected GetPrinter Error
Console.WriteLine("Cannot GetPrinter [CCL12] DLL Error = " & _
Err.LastDllError)
ClosePrinter(PrintHandle)
Exit Sub
End If

' Allocate memory and parse the buffer
Try
bufptr = Marshal.AllocCoTaskMem(ByteLength)
Marshal.Copy(buf, 0, bufptr, ByteLength)
pPrinter_Info = CType(Marshal.PtrToStructure(bufptr,
GetType(PRINTER_INFO_2W)), _
PRINTER_INFO_2W)
Marshal.FreeCoTaskMem(bufptr)
Catch ex As Exception
Console.WriteLine("Unable to marshal PRINTER_INFO_2. ")
ClosePrinter(PrintHandle)
Exit Sub
End Try

' Check whether the printer is shared
bResult = (pPrinter_Info.Attributes And PRINTER_ATTRIBUTE_SHARED)
ClosePrinter(PrintHandle)

If bResult Then
Console.WriteLine("Printer [CCL12] found and shared. ")
Else
Console.WriteLine("Printer [CCL12] found but not shared.")
End If
End Sub

End Module


3 Answers

(Mattias Sjögren)

1/31/2007 8:00:00 PM

0


>However, when I compile the same program on a x64 Windows Server 2003 R2
>Ent. and run it (with only slight modification on printer name), it caused a
>FatalExecutionEngineError.

Great. That helps expose the errors in the code that may go unnoticed
on Win32.


> <DllImport("winspool.Drv", EntryPoint:="OpenPrinterW", _
> SetLastError:=True, CharSet:=CharSet.Unicode, _
> ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
> Function OpenPrinter(ByVal src As String, _
> ByRef hPrinter As IntPtr, ByVal pd As Integer) As Boolean

The last parameters is a pointer, so you have to use IntPtr rather
than Integer.


> <DllImport("winspool.Drv", EntryPoint:="GetPrinterW", _
> SetLastError:=True, CharSet:=CharSet.Unicode, _
> ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
> Function GetPrinter(ByVal hPrinter As IntPtr, ByVal Level As Integer, _
> ByRef pPrinter As Byte, ByVal cbBuf As Integer, ByRef pcbNeeded As
>Integer _
> ) As Integer

pPrinter should be a pointer to pointer to byte (i.e. a ByRef IntPtr
in managed code).


Mattias

--
Mattias Sjögren [C# MVP] mattias @ mvps.org
http://www.msjogren.n... | http://www.dotneti...
Please reply only to the newsgroup.

SteveYau

2/1/2007 6:44:00 AM

0

> > <DllImport("winspool.Drv", EntryPoint:="GetPrinterW", _
> > SetLastError:=True, CharSet:=CharSet.Unicode, _
> > ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _
> > Function GetPrinter(ByVal hPrinter As IntPtr, ByVal Level As Integer, _
> > ByRef pPrinter As Byte, ByVal cbBuf As Integer, ByRef pcbNeeded As
> >Integer _
> > ) As Integer
>
> pPrinter should be a pointer to pointer to byte (i.e. a ByRef IntPtr
> in managed code).

Can you elaborate this point a bit more? Or can you demonstrate me the way
to modify my code? I just get confused here. Byref Byte should already passed
the starting position of a byte array and the length of byte array should be
determined by cbBuf.

Also, I noticed that the first call to GetPrinter returned a large volume of
pcbNeeded (4 times that in 32-bit environment).

Steve

(Mattias Sjögren)

2/2/2007 8:03:00 PM

0

Steve,

>> pPrinter should be a pointer to pointer to byte (i.e. a ByRef IntPtr
>> in managed code).
>
>Can you elaborate this point a bit more?

First I should correct myself. The parameter shouldn't be a pointer to
a pointer to byte. That's what my local Platform SDK docs says
(LPBYTE*) but it turns out the docs were wrong. The headers and MSDN
online library has it correct - it's just a single pointer to a byte
array (LPBYTE).

So you had the indirection level right but your declaration is still
incorrect...


>Byref Byte should already passed
>the starting position of a byte array and the length of byte array should be
>determined by cbBuf.

Passing the first byte in an array ByRef where a byte array is
expected was something you got away with on Win32 and the 32-bit CLR.
But no more on Win64. If the function wants a byte array, you should
pass it the entire array (ByVal since arrays are reference types). You
can read more here

http://blogs.msdn.com/joshwil/archive/2005/08/10/4...


>Also, I noticed that the first call to GetPrinter returned a large volume of
>pcbNeeded (4 times that in 32-bit environment).

I wouldn't worry about that if you get it working. Sure 4 times sounds
a bit much, but the double pointer size and stricter alignment rules
can certainly at least double the buffer needed.


Mattias

--
Mattias Sjögren [C# MVP] mattias @ mvps.org
http://www.msjogren.n... | http://www.dotneti...
Please reply only to the newsgroup.