using System; using System.Runtime.InteropServices; namespace Vanara.PInvoke { /// Items from the cabinet.dll public static partial class Cabinet { /// /// The FNCLOSE macro provides the declaration for the application-defined callback function to close a file in an FDI context. /// /// The file handle. /// Returns 0 if the file was successfully closed. A return value of –1 indicates an error. /// The function accepts parameters similar to _close. // https://docs.microsoft.com/en-us/windows/desktop/api/fdi/nf-fdi-fnclose [PInvokeData("fdi.h", MSDNShortId = "89db9c2a-42ab-410d-a427-60d282385c2b")] public delegate int PFNCLOSE(HFILE hf); /// /// The FNFDINOTIFY macro provides the declaration for the application-defined callback notification function to update the /// application on the status of the decoder. /// /// the FDIDECRYPT structure. /// Success returns 1; Failure returns 0; -1 if FDICopy() is aborted. /// /// If this function is passed on the FDICopy() call, then FDI calls it at various times to update the decryption state and to /// decrypt FCDATA blocks. /// // https://docs.microsoft.com/en-us/windows/desktop/api/fdi/nf-fdi-fnfdinotify [PInvokeData("fdi.h", MSDNShortId = "7655ddb2-7cd4-4012-913c-9909fcea639a")] public delegate int PFNFDINOTIFY(ref FDIDECRYPT pfdid); /// /// The FNOPEN macro provides the declaration for the application-defined callback function to open a file in an FDI context. /// /// File name. /// The kind of operations allowed. /// Permission mode. /// Return value is file handle of open file to read, or INVALID_FILE_HANDLE on failure. /// The function accepts parameters similar to _open. // https://docs.microsoft.com/en-us/windows/desktop/api/fdi/nf-fdi-fnopen [PInvokeData("fdi.h", MSDNShortId = "45bd2d23-1f6d-42a6-8afb-86227da6118f")] public delegate HFILE PFNOPEN(string pszFile, int oflag, int pmode); /// /// The FNREAD macro provides the declaration for the application-defined callback function to read data from a file in an FDI context. /// /// File descriptor referring to the open file. /// Storage location for data. /// Maximum number of bytes to read. /// returns the number of bytes read /// The function accepts parameters similar to _read. // https://docs.microsoft.com/en-us/windows/desktop/api/fdi/nf-fdi-fnread [PInvokeData("fdi.h", MSDNShortId = "0a8c6c9f-051c-43a0-b43b-1fd8b4fef10c")] public delegate uint PFNREAD(HFILE hf, IntPtr memory, uint cb); /// /// The FNSEEK macro provides the declaration for the application-defined callback function to move a file pointer to the /// specified location in an FDI context. /// /// File descriptor referring to an open file. /// Number of bytes from origin. /// Initial position.. /// returns the offset, in bytes, of the new position from the beginning of the file. /// The function accepts parameters similar to _lseek. // https://docs.microsoft.com/en-us/windows/desktop/api/fdi/nf-fdi-fnseek [PInvokeData("fdi.h", MSDNShortId = "e49b5086-6b89-40ce-b6fa-905d21593dec")] public delegate int PFNSEEK(HFILE hf, int dist, int seektype); /// /// The FNWRITE macro provides the declaration for the application-defined callback function to write data to a file in an FDI context. /// /// File descriptor of file into which data is written. /// Data to be written. /// Number of bytes. /// returns the number of bytes actually written. /// The function accepts parameters similar to _write. // https://docs.microsoft.com/en-us/windows/desktop/api/fdi/nf-fdi-fnwrite [PInvokeData("fdi.h", MSDNShortId = "e15d4293-2955-48cd-b8c9-77669a1e6436")] public delegate uint PFNWRITE(HFILE hf, IntPtr memory, uint cb); public enum FDIDECRYPTTYPE { fdidtNEW_CABINET, // New cabinet fdidtNEW_FOLDER, // New folder fdidtDECRYPT, // Decrypt a data block } [PInvokeData("fdi.h")] public enum FDIERROR { /// /// Description: No error /// Cause: Function was successfull. /// Response: Keep going! /// FDIERROR_NONE, /// /// Description: Cabinet not found /// Cause: Bad file name or path passed to FDICopy(), or returned to fdintNEXT_CABINET. /// Response: To prevent this error, validate the existence of the the cabinet *before* passing the path to FDI. /// FDIERROR_CABINET_NOT_FOUND, /// /// Description: Cabinet file does not have the correct format /// Cause: File passed to to FDICopy(), or returned to fdintNEXT_CABINET, is too small to be a cabinet file, or does not /// have the cabinet signature in its first four bytes. /// Response: To prevent this error, call FDIIsCabinet() to check a cabinet before calling FDICopy() or returning the cabinet /// path to fdintNEXT_CABINET. /// FDIERROR_NOT_A_CABINET, /// /// Description: Cabinet file has an unknown version number. /// Cause: File passed to to FDICopy(), or returned to fdintNEXT_CABINET, has what looks like a cabinet file header, but /// the version of the cabinet file format is not one understood by this version of FDI. The erf.erfType field is filled in with /// the version number found in the cabinet file. /// Response: To prevent this error, call FDIIsCabinet() to check a cabinet before calling FDICopy() or returning the cabinet /// path to fdintNEXT_CABINET. /// FDIERROR_UNKNOWN_CABINET_VERSION, /// /// Description: Cabinet file is corrupt /// Cause: FDI returns this error any time it finds a problem with the logical format of a cabinet file, and any time one /// of the passed-in file I/O calls fails when operating on a cabinet (PFNOPEN, PFNSEEK, PFNREAD, or PFNCLOSE). The client can /// distinguish these two cases based upon whether the last file I/O call failed or not. /// Response: Assuming this is not a real corruption problem in a cabinet file, the file I/O functions could attempt to do /// retries on failure (for example, if there is a temporary network connection problem). If this does not work, and the file I/O /// call has to fail, then the FDI client will have to clean up and call the FDICopy() function again. /// FDIERROR_CORRUPT_CABINET, /// /// Description: Could not allocate enough memory /// Cause: FDI tried to allocate memory with the PFNALLOC function, but it failed. /// Response: If possible, PFNALLOC should take whatever steps are possible to allocate the memory requested. If memory is not /// immediately available, it might post a dialog asking the user to free memory, for example. Note that the bulk of FDI's memory /// allocations are made at FDICreate() time and when the first cabinet file is opened during FDICopy(). /// FDIERROR_ALLOC_FAIL, /// /// Description: Unknown compression type in a cabinet folder /// Cause: [Should never happen.] A folder in a cabinet has an unknown compression type. This is probably caused by a /// mismatch between the version of Diamond used to create the cabinet and the FDI. LIB used to read the cabinet. /// Response: Abort. /// FDIERROR_BAD_COMPR_TYPE, /// /// Description: Failure decompressing data from a cabinet file /// Cause: The decompressor found an error in the data coming from the file cabinet. The cabinet file was corrupted. /// [11-Apr-1994 bens When checksuming is turned on, this error should never occur.] /// Response: Probably should abort; only other choice is to cleanup and call FDICopy() again, and hope there was some /// intermittent data error that will not reoccur. /// FDIERROR_MDI_FAIL, /// /// Description: Failure writing to target file /// Cause: FDI returns this error any time it gets an error back from one of the passed-in file I/O calls fails when /// writing to a file being extracted from a cabinet. /// Response: To avoid or minimize this error, the file I/O functions could attempt to avoid failing. A common cause might be /// disk full -- in this case, the PFNWRITE function could have a check for free space, and put up a dialog asking the user to /// free some disk space. /// FDIERROR_TARGET_FILE, /// /// Description: Cabinets in a set do not have the same RESERVE sizes /// Cause: [Should never happen]. FDI requires that the sizes of the per-cabinet, per-folder, and per-data block RESERVE /// sections be consistent across all the cabinet in a set. Diamond will only generate cabinet sets with these properties. /// Response: Abort. /// FDIERROR_RESERVE_MISMATCH, /// /// Description: Cabinet returned on fdintNEXT_CABINET is incorrect /// Cause: NOTE: THIS ERROR IS NEVER RETURNED BY FDICopy()! Rather, FDICopy() keeps calling the fdintNEXT_CABINET callback /// until either the correct cabinet is specified, or you return ABORT. When FDICopy() is extracting a file that crosses a /// cabinet boundary, it calls fdintNEXT_CABINET to ask for the path to the next cabinet. Not being very trusting, FDI then /// checks to make sure that the correct continuation cabinet was supplied! It does this by checking the "setID" and "iCabinet" /// fields in the cabinet. When DIAMOND.EXE creates a set of cabinets, it constructs the "setID" using the sum of the bytes of /// all the destination file names in the cabinet set. FDI makes sure that the 16-bit setID of the continuation cabinet matches /// the cabinet file just processed. FDI then checks that the cabinet number (iCabinet) is one more than the cabinet number for /// the cabinet just processed. /// Response: You need code in your fdintNEXT_CABINET (see below) handler to do retries if you get recalled with this error. /// See the sample code (EXTRACT.C) to see how this should be handled. /// FDIERROR_WRONG_CABINET, /// /// Description: FDI aborted. /// Cause: An FDI callback returned -1 (usually). /// Response: Up to client. /// FDIERROR_USER_ABORT, /// /// Description: Unexpected end of file. /// Cause: This error may be returned instead of FDIERROR_CORRUPT_CABINET if PFNREAD returned 0. /// Response: See FDIERROR_CORRUPT_CABINET above. /// FDIERROR_EOF, } /// The FDICopy function extracts files from cabinets. /// A valid FDI context handle returned by the FDICreate function. /// /// The name of the cabinet file, excluding any path information, from which to extract files. If a file is split over multiple /// cabinets, FDICopy allows for subsequent cabinets to be opened. /// /// /// The pathname of the cabinet file, but not including the name of the file itself. For example, "C:\MyCabs". /// The contents of pszCabinet are appended to pszCabPath to create the full pathname of the cabinet. /// /// No flags are currently defined and this parameter should be set to zero. /// /// Pointer to an application-defined callback notification function to update the application on the status of the decoder. The /// function should be declared using the FNFDINOTIFY macro. /// /// Not currently used by FDI. This parameter should be set to NULL. /// Pointer to an application-specified value to pass to the notification function. /// /// If the function succeeds, it returns TRUE; otherwise, FALSE. /// Extended error information is provided in the ERF structure used to create the FDI context. /// // https://docs.microsoft.com/en-us/windows/desktop/api/fdi/nf-fdi-fdicopy BOOL DIAMONDAPI FDICopy( HFDI hfdi, LPSTR pszCabinet, // LPSTR pszCabPath, int flags, PFNFDINOTIFY pfnfdin, PFNFDIDECRYPT pfnfdid, void *pvUser ); [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Ansi)] [PInvokeData("fdi.h", MSDNShortId = "6ec2b10b-f70a-4a22-beff-df6b6a4c4cfd")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool FDICopy(HFDI hfdi, string pszCabinet, string pszCabPath, int flags, PFNFDINOTIFY pfnfdin, IntPtr pfnfdid, IntPtr pvUser); /// The FDICreate function creates an FDI context. /// /// Pointer to an application-defined callback function to allocate memory. The function should be declared using the FNALLOC macro. /// /// /// Pointer to an application-defined callback function to free previously allocated memory. The function should be declared using /// the FNFREE macro. /// /// /// Pointer to an application-defined callback function to open a file. The function should be declared using the FNOPEN macro. /// /// /// Pointer to an application-defined callback function to read data from a file. The function should be declared using the FNREAD macro. /// /// /// Pointer to an application-defined callback function to write data to a file. The function should be declared using the FNWRITE macro. /// /// /// Pointer to an application-defined callback function to close a file. The function should be declared using the FNCLOSE macro. /// /// /// Pointer to an application-defined callback function to move a file pointer to the specified location. The function should be /// declared using the FNSEEK macro. /// /// /// In the 16-bit version of FDI, specifies the CPU type and can be any of the following values. /// Note Expressing the cpuUNKNOWN value is recommended. /// /// /// Value /// Meaning /// /// /// cpuUNKNOWN -1 /// FDI should determine the CPU type. /// /// /// cpu80286 0 /// Only 80286 instructions can be used. /// /// /// cpu80386 1 /// 80386 instructions can be used. /// /// /// /// Pointer to an ERF structure that receives the error information. /// /// If the function succeeds, it returns a non- NULL HFDI context pointer; otherwise, it returns NULL. /// Extended error information is provided in the ERF structure. /// // https://docs.microsoft.com/en-us/windows/desktop/api/fdi/nf-fdi-fdicreate HFDI DIAMONDAPI FDICreate( PFNALLOC pfnalloc, PFNFREE // pfnfree, PFNOPEN pfnopen, PFNREAD pfnread, PFNWRITE pfnwrite, PFNCLOSE pfnclose, PFNSEEK pfnseek, int cpuType, PERF perf ); [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Ansi)] [PInvokeData("fdi.h", MSDNShortId = "90634725-b7a8-4369-8a91-684debee9548")] public static extern SafeHFDI FDICreate(PFNALLOC pfnalloc, PFNFREE pfnfree, PFNOPEN pfnopen, PFNREAD pfnread, PFNWRITE pfnwrite, PFNCLOSE pfnclose, PFNSEEK pfnseek, int cpuType, out ERF perf); /// The FDIDestroy function deletes an open FDI context. /// A valid FDI context handle returned by the FDICreate function. /// /// If the function succeeds, it returns TRUE; otherwise, FALSE. /// Extended error information is provided in the ERF structure used to create the FDI context. /// // https://docs.microsoft.com/en-us/windows/desktop/api/fdi/nf-fdi-fdidestroy BOOL DIAMONDAPI FDIDestroy( HFDI hfdi ); [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Ansi)] [PInvokeData("fdi.h", MSDNShortId = "fe3b8045-a476-4a21-b732-0d4799798faf")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool FDIDestroy(HFDI hfdi); /// /// The FDIIsCabinet function determines whether a file is a cabinet and, if it is, returns information about it. /// /// A valid FDI context handle returned by FDICreate. /// /// An application-defined value to keep track of the opened file. This value must be of the same type as values used by the File I/O /// functions passed to FDICreate. /// /// /// Pointer to an FDICABINETINFO structure that receives the cabinet details, in the event the file is actually a cabinet. /// /// /// If the file is a cabinet, the function returns TRUE ; otherwise, FALSE. /// Extended error information is provided in the ERF structure used to create the FDI context. /// // https://docs.microsoft.com/en-us/windows/desktop/api/fdi/nf-fdi-fdiiscabinet BOOL DIAMONDAPI FDIIsCabinet( HFDI hfdi, INT_PTR hf, // PFDICABINETINFO pfdici ); [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Ansi)] [PInvokeData("fdi.h", MSDNShortId = "01d223ca-56c6-49fa-b9e6-e5eeda88936a")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool FDIIsCabinet(HFDI hfdi, IntPtr hf, out FDICABINETINFO pfdici); /// The FDITruncateCabinet function truncates a cabinet file starting at the specified folder number. /// A valid FDI context handle returned by the FDICreate function. /// The full cabinet filename. /// The index of the first folder to delete. /// /// If the function succeeds, it returns TRUE; otherwise, FALSE. /// Extended error information is provided in the ERF structure used to create the FDI context. /// // https://docs.microsoft.com/en-us/windows/desktop/api/fdi/nf-fdi-fditruncatecabinet BOOL DIAMONDAPI FDITruncateCabinet( HFDI hfdi, // LPSTR pszCabinetName, USHORT iFolderToDelete ); [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Ansi)] [PInvokeData("fdi.h", MSDNShortId = "c923b0a5-1a8d-42aa-bd05-0d318199756d")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool FDITruncateCabinet(HFDI hfdi, string pszCabinetName, ushort iFolderToDelete); /// The FDICABINETINFO structure contains details about a particular cabinet file. // https://docs.microsoft.com/en-us/windows/desktop/api/fdi/ns-fdi-__unnamed_struct_0 typedef struct { long cbCabinet; USHORT // cFolders; USHORT cFiles; USHORT setID; USHORT iCabinet; BOOL fReserve; BOOL hasprev; BOOL hasnext; } FDICABINETINFO; [PInvokeData("fdi.h", MSDNShortId = "fde1a2ca-60cd-4a4d-9872-681e2f8f4fb1")] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct FDICABINETINFO { /// The total length of the cabinet file. public long cbCabinet; /// The count of the folders in the cabinet. public ushort cFolders; /// The count of the files in the cabinet. public ushort cFiles; /// The identifier of the cabinet set. public ushort setID; /// The cabinet number in set. This index is zero based. public ushort iCabinet; /// If this value is set to TRUE, a reserved area is present in the cabinet. [MarshalAs(UnmanagedType.Bool)] public bool fReserve; /// /// If this value is set to TRUE, the cabinet is linked to a previous cabinet. This is accomplished by having a file /// continued from the previous cabinet into the current one. /// [MarshalAs(UnmanagedType.Bool)] public bool hasprev; /// /// If this value is set to TRUE, the current cabinet is linked to the next cabinet by having a file continued from the /// current cabinet into the next one. /// [MarshalAs(UnmanagedType.Bool)] public bool hasnext; } [PInvokeData("fdi.h")] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct FDIDECRYPT { ///Command type (selects union below) public FDIDECRYPTTYPE fdidt; ///Decryption context public IntPtr pvUser; public Union union; [StructLayout(LayoutKind.Explicit)] public struct Union { [FieldOffset(0)] public NEW_CABINET cabinet; [FieldOffset(0)] public NEW_FOLDER folder; [FieldOffset(0)] public DECRYPT decrypt; } [StructLayout(LayoutKind.Sequential)] public struct NEW_CABINET { ///RESERVE section from CFHEADER public IntPtr pHeaderReserve; ///Size of pHeaderReserve public ushort cbHeaderReserve; ///Cabinet set ID public ushort setID; ///Cabinet number in set (0 based) public int iCabinet; } [StructLayout(LayoutKind.Sequential)] public struct NEW_FOLDER { ///RESERVE section from CFFOLDER public IntPtr pFolderReserve; ///Size of pFolderReserve public ushort cbFolderReserve; ///Folder number in cabinet (0 based) public ushort iFolder; } [StructLayout(LayoutKind.Sequential)] public struct DECRYPT { ///RESERVE section from CFDATA public IntPtr pDataReserve; ///Size of pDataReserve public ushort cbDataReserve; ///Data buffer public IntPtr pbData; ///Size of data buffer public ushort cbData; ///TRUE if this is a split data block public bool fSplit; ///0 if this is not a split block, or the first piece of a split block; Greater than 0 if this is the second piece of a split block. public ushort cbPartial; } } /// The FDINOTIFICATION structure to provide information to FNFDINOTIFY. // https://docs.microsoft.com/en-us/windows/desktop/api/fdi/ns-fdi-fdinotification typedef struct { long cb; char *psz1; char *psz2; // char *psz3; void *pv; INT_PTR hf; USHORT date; USHORT time; USHORT attribs; USHORT setID; USHORT iCabinet; USHORT iFolder; // FDIERROR fdie; } FDINOTIFICATION, *PFDINOTIFICATION; [PInvokeData("fdi.h", MSDNShortId = "8b92226e-b19a-4624-925e-4a98d037637d")] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct FDINOTIFICATION { /// The size, in bytes, of a cabinet element. public long cb; /// A null-terminated string. public Vanara.InteropServices.StrPtrAnsi psz1; /// A null-terminated string. public Vanara.InteropServices.StrPtrAnsi psz2; /// A null-terminated string. public Vanara.InteropServices.StrPtrAnsi psz3; /// Pointer to an application-defined value. public IntPtr pv; /// Application-defined value used to identify the opened file. public HFILE hf; /// /// The MS-DOS date. /// /// /// Bits /// Description /// /// /// 0-4 /// Day of the month (1-31) /// /// /// 5-8 /// Month (1 = January, 2 = February, etc.) /// /// /// 9-15 /// Year offset from 1980 (add 1980 /// /// /// public ushort date; /// /// The MS-DOS time. /// /// /// Bits /// Description /// /// /// 0-4 /// Second divided by 2 /// /// /// 5-10 /// Minute (0-59) /// /// /// 11-15 /// Hour (0-23 on a 24-hour clock) /// /// /// public ushort time; /// The file attributes. For possible values and their descriptions, see File Attributes. public ushort attribs; /// The identifier for a cabinet set. public ushort setID; /// The number of the cabinets within a set. public ushort iCabinet; /// The number of folders within a cabinet. public ushort iFolder; /// /// An FDI error code. Possible values include: /// /// /// Value /// Meaning /// /// /// FDIERROR_NONE 0x00 /// No error. /// /// /// FDIERROR_CABINET_NOT_FOUND 0x01 /// The cabinet file was not found. /// /// /// FDIERROR_NOT_A_CABINET 0x02 /// The cabinet file does not have the correct format. /// /// /// FDIERROR_UNKNOWN_CABINET_VERSION 0x03 /// The cabinet file has an unknown version number. /// /// /// FDIERROR_CORRUPT_CABINET 0x04 /// The cabinet file is corrupt. /// /// /// FDIERROR_ALLOC_FAIL 0x05 /// Insufficient memory. /// /// /// FDIERROR_BAD_COMPR_TYPE 0x06 /// Unknown compression type used in the cabinet folder. /// /// /// FDIERROR_MDI_FAIL 0x07 /// Failure decompressing data from the cabinet file. /// /// /// FDIERROR_TARGET_FILE 0x08 /// Failure writing to the target file. /// /// /// FDIERROR_RESERVE_MISMATCH 0x09 /// The cabinets within a set do not have the same RESERVE sizes. /// /// /// FDIERROR_WRONG_CABINET 0x0A /// The cabinet returned by fdintNEXT_CABINET is incorrect. /// /// /// FDIERROR_USER_ABORT 0x0B /// FDI aborted. /// /// /// public FDIERROR fdie; } /// Provides a handle to a file decompression interface. [StructLayout(LayoutKind.Sequential)] public struct HFDI : IHandle { private IntPtr handle; /// Initializes a new instance of the struct. /// An object that represents the pre-existing handle to use. public HFDI(IntPtr preexistingHandle) => handle = preexistingHandle; /// Returns an invalid handle by instantiating a object with . public static HFDI NULL => new HFDI(IntPtr.Zero); /// Gets a value indicating whether this instance is a null handle. public bool IsNull => handle == IntPtr.Zero; /// Performs an explicit conversion from to . /// The handle. /// The result of the conversion. public static explicit operator IntPtr(HFDI h) => h.handle; /// Performs an implicit conversion from to . /// The pointer to a handle. /// The result of the conversion. public static implicit operator HFDI(IntPtr h) => new HFDI(h); /// Implements the operator !=. /// The first handle. /// The second handle. /// The result of the operator. public static bool operator !=(HFDI h1, HFDI h2) => !(h1 == h2); /// Implements the operator ==. /// The first handle. /// The second handle. /// The result of the operator. public static bool operator ==(HFDI h1, HFDI h2) => h1.Equals(h2); /// public override bool Equals(object obj) => obj is HFDI h ? handle == h.handle : false; /// public override int GetHashCode() => handle.GetHashCode(); /// public IntPtr DangerousGetHandle() => handle; } /// Provides a for that is disposed using . public class SafeHFDI : SafeHANDLE { /// Initializes a new instance of the class and assigns an existing handle. /// An object that represents the pre-existing handle to use. /// /// to reliably release the handle during the finalization phase; otherwise, (not recommended). /// public SafeHFDI(IntPtr preexistingHandle, bool ownsHandle = true) : base(preexistingHandle, ownsHandle) { } /// Initializes a new instance of the class. private SafeHFDI() : base() { } /// Performs an implicit conversion from to . /// The safe handle instance. /// The result of the conversion. public static implicit operator HFDI(SafeHFDI h) => h.handle; /// protected override bool InternalReleaseHandle() => FDIDestroy(this); } } }