private static void ToPtr(IntPtr pos, NotifyOptions options)
        {
            PRINTER_NOTIFY_OPTIONS st;

            st.Flags   = options.Flags;
            st.Count   = (uint)options.Types.Count;
            st.Version = 2;
            st.pTypes  = pos + Marshal.SizeOf <PRINTER_NOTIFY_OPTIONS>();

            Marshal.StructureToPtr(st, pos, false);
            pos += Marshal.SizeOf <PRINTER_NOTIFY_OPTIONS>();

            var fieldsPos = pos + (options.Types.Count * Marshal.SizeOf <PRINTER_NOTIFY_OPTIONS_TYPE>());

            foreach (var optionsType in options.Types)
            {
                PRINTER_NOTIFY_OPTIONS_TYPE str = default;
                str.Type    = (UInt16)optionsType.Type;
                str.Count   = (UInt32)optionsType.Fields.Count;
                str.pFields = fieldsPos;

                Marshal.StructureToPtr(str, pos, false);
                pos += Marshal.SizeOf <PRINTER_NOTIFY_OPTIONS_TYPE>();

                foreach (var field in optionsType.Fields)
                {
                    Marshal.WriteInt32(fieldsPos, field);
                    fieldsPos += Marshal.SizeOf(field.GetType());
                }
            }
        }
        private static int SizeOfNotifyOptions(NotifyOptions options)
        {
            var size = Marshal.SizeOf <PRINTER_NOTIFY_OPTIONS>();

            foreach (var printerNotifyOptionsType in options.Types)
            {
                size += Marshal.SizeOf <PRINTER_NOTIFY_OPTIONS_TYPE>();
                size += printerNotifyOptionsType.Fields.Count * Marshal.SizeOf <UInt32>();
            }

            return(size);
        }
        public static IChangeNotification Create(PRINTER_CHANGE changes,
                                                 string printerName = null,
                                                 PRINTER_NOTIFY_CATEGORY category = PRINTER_NOTIFY_CATEGORY.PRINTER_NOTIFY_CATEGORY_ALL,
                                                 NotifyOptions options            = null)
        {
            var notification = new ChangeNotification
            {
                _printerHandle = OpenPrinter(printerName)
            };

            var ptrNotifyOptions = IntPtr.Zero;

            try
            {
                if (options != null)
                {
                    var size = SizeOfNotifyOptions(options);
                    ptrNotifyOptions = Marshal.AllocHGlobal(size);
                    ToPtr(ptrNotifyOptions, options);
                }

                notification._changeHandle = NativeMethods.FindFirstPrinterChangeNotification(
                    notification._printerHandle,
                    (UInt32)changes,
                    (UInt32)category,
                    ptrNotifyOptions);
                if (notification._changeHandle == INVALID_HANDLE_VALUE)
                {
                    throw new Win32Exception();
                }

                // Don't let SafeWaitHandle own the handle as it can't close it
                notification.SafeWaitHandle = new SafeWaitHandle(notification._changeHandle, false);
            }
            catch
            {
                NativeMethods.ClosePrinter(notification._printerHandle);
                throw;
            }
            finally
            {
                Marshal.FreeHGlobal(ptrNotifyOptions);
            }

            return(notification);
        }