1

Im trying to make getJob() working from printerspooler api in 64bit.

I use the following definition (as other people are using on SO)

[DllImport("winspool.drv", EntryPoint = "GetJob", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]
public static extern bool GetJob([In] IntPtr hPrinter, [In] Int32 dwJobId, [In] Int32 Level, [Out] IntPtr lpJob, [In] Int32 cbBuf, ref IntPtr lpbSizeNeeded);

But this only works when im not debugging and i'm not in 64bit.

In 64 bit i get the windows Error: The parameter is incorrect.

How to i fix this?

I tried changing Int32 to IntPtr (4=>8 bytes in 64bit) and the reverse IntPtr => Int32. to no avail..

The docs are here: https://learn.microsoft.com/en-us/windows/desktop/printdocs/getjob:

BOOL GetJob(
  _In_  HANDLE  hPrinter,
  _In_  DWORD   JobId,
  _In_  DWORD   Level,
  _Out_ LPBYTE  pJob,
  _In_  DWORD   cbBuf,
  _Out_ LPDWORD pcbNeeded
);

I also tried changing ref to out and putting ref/out to lpJob parameter but even this seems not to work.

What could I try next?

Edit

what seems to work is:

[DllImport("winspool.drv", EntryPoint = "GetJob", SetLastError = true, CharSet = CharSet.Auto, ExactSpelling = false, CallingConvention = CallingConvention.StdCall)]
public static extern bool GetJob([In] IntPtr hPrinter, [In] Int32 dwJobId, [In] Int32 Level, [Out] byte[] lpJob, [In] Int32 cbBuf, ref Int32 lpbSizeNeeded);

and using it as follows:

public JOB_INFO_1(IntPtr hPrinter, Int32 dwJobId)
{
  var BytesWritten = new Int32();
  var ptBuf = new byte[0];


  if (PrinterMonitorComponent.ComponentTraceSwitch.TraceVerbose)
    Console.WriteLine("JOB_INFO_1 new(" + hPrinter.ToString() + "," + dwJobId.ToString() + ")");


  // \\ Get the required buffer size
  if (!UnsafeNativeMethods.GetJob(hPrinter, dwJobId, 1, ptBuf, 0, ref BytesWritten))
  {
    if (BytesWritten == 0)
    {
      var ex = new Win32Exception();
      if (PrinterMonitorComponent.ComponentTraceSwitch.TraceError)
        Console.WriteLine("{0} GetJob for JOB_INFO_1 failed on handle: {1} for job: {2} - {3}", this.GetType().ToString(), hPrinter, dwJobId, ex.Message);
      throw ex;
    }
  }

  // \\ Allocate a buffer the right size
  if (BytesWritten > 0)
    ptBuf = new byte[BytesWritten]; // Marshal.AllocHGlobal(BytesWritten);

  //Console.WriteLine($"Buffer {BytesWritten} x"); // uncommenting this code somehow breaks it again -.-
  // \\ Populate the JOB_INFO_1 structure
  if (!UnsafeNativeMethods.GetJob(hPrinter, dwJobId, 1, ptBuf, BytesWritten, ref BytesWritten))
  {
    if (PrinterMonitorComponent.ComponentTraceSwitch.TraceError)
      Console.WriteLine("GetJob for JOB_INFO_1 failed on handle: " + hPrinter.ToString() + " for job: " + dwJobId, this.GetType().ToString());
    throw new Win32Exception();
  }
  else
  {
    GCHandle handle = GCHandle.Alloc(ptBuf, GCHandleType.Pinned);
    Marshal.PtrToStructure(handle.AddrOfPinnedObject(), this);
    handle.Free();
    //Marshal.PtrToStructure(ptBuf, this);

  }

  // \\ Free the allocated memory
  //Marshal.FreeHGlobal(ptBuf);
}

edit2

seems to not work, sometimes it seemd to work but unit test didn't notice changes in csproj file so i was still testing against 32 bit in the end.

adding following line to csproj makes it works (it runs in 32bit, putting it in 64bit, it fails)

<PlatformTarget>x86</PlatformTarget>
Community
  • 1
  • 1
Joel Harkes
  • 10,975
  • 3
  • 46
  • 65
  • All `DWORD`s should be either `Int32` or `UInt32` (which one usually doesn't matter). The final, `LPDWORD`, should be `ref Int32` or `ref UInt32`. The `pJob` parameter I think should be able to be `[Out] IntPtr` like you have now, or alternatively `[Out] byte[]` (also try them without the `[Out]` attribute) – Visual Vincent Nov 02 '18 at 10:28
  • @VisualVincent changing pcbNeeded to ref int32 doesnt work. i fear the documentation might not be correct, had same type of issue with getting printer notifications (docs being incorrect). – Joel Harkes Nov 02 '18 at 10:36
  • Well I doubt the last parameter was he issue, but `ref (U)Int32` is the correct declaration for it. Try my `pJob` suggestions as well, both with and without `[Out]`. -- By the way I found what seems to be a managed version of this API: https://learn.microsoft.com/en-us/dotnet/api/system.printing.printqueue.getjob?view=netframework-4.7.2 – Visual Vincent Nov 02 '18 at 10:37
  • What is the exact (full) error message you're getting? Can you share it in your question along with a verifiable code example? – Visual Vincent Nov 02 '18 at 10:45
  • tried setting up Mvce but is difficult, first i print a raw document than using FindNextPrinterChangeNotification i get a printjob added notification and then i try to get the job. also changing to byte[], now it works... sometimes... Although i still dont get the document name (probably some data alignment issue). Ill add some more code. – Joel Harkes Nov 02 '18 at 10:51
  • 1
    The clumsy LPBYTE argument is necessary in the C language because this function can return different structs. You don't have this limitation at all in C#, it support method overloads. So declare as many as you like, now using say `ref JobInfo1` where JobInfo1 is your declaration for the JOB_INFO_1 structure. Etcetera. – Hans Passant Nov 02 '18 at 11:03
  • But intptr seems to be fine in 32bit changing it to byte seems to work for 64bit but seems to break it for 32 bit? why would that be? – Joel Harkes Nov 02 '18 at 11:08
  • @VisualVincent still getting parameter is incorrect in 64bit. using `GetJob([In] IntPtr hPrinter, [In] Int32 dwJobId, [In] Int32 Level, [Out] byte[] lpJob, [In] Int32 cbBuf, ref Int32 lpbSizeNeeded);` – Joel Harkes Nov 02 '18 at 13:56
  • Is the job ID (`dwJobId`) valid? Where do you get it from? If you pass an invalid ID it will result in the "incorrect parameter" error (`ERROR_INVALID_PARAMETER`). – Visual Vincent Nov 02 '18 at 14:48
  • @VisualVincent jobid is 79 in this case. just going back to 32bit it works fine, switching to 64bit this error pops up. – Joel Harkes Nov 02 '18 at 15:05
  • Cdde is based on https://github.com/MerrionComputing/PrintQueueWatch – Joel Harkes Nov 02 '18 at 15:07

1 Answers1

2

It seems to have been mostly an timing issue...

I was testing with a null printer, somehow in 32bits it mostly worked, but than going to 64bits something must have gone slower thats why it would fail, same for debugging.

The job would already ahve been spooled and thus requesting the job info is no longer available.

To fix this you can set: Keep printed documents.

enter image description here

Joel Harkes
  • 10,975
  • 3
  • 46
  • 65