diff --git a/PInvoke/Cabinet/Fci.cs b/PInvoke/Cabinet/Fci.cs index 3667ccec..526e53cd 100644 --- a/PInvoke/Cabinet/Fci.cs +++ b/PInvoke/Cabinet/Fci.cs @@ -47,7 +47,7 @@ namespace Vanara.PInvoke /// // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcialloc [PInvokeData("fci.h", MSDNShortId = "339ac9d2-60bc-4a90-8a46-6fbb073be9d1")] - [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate IntPtr PFNALLOC(uint cb); /// @@ -60,7 +60,7 @@ namespace Vanara.PInvoke /// The function accepts parameters similar to _close, with the addition of err and pv. // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfciclose [PInvokeData("fci.h", MSDNShortId = "c4edf6ca-0b16-4e30-933b-934f8930c6d6")] - [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate int PFNFCICLOSE(IntPtr hf, out int err, IntPtr pv); /// @@ -73,7 +73,7 @@ namespace Vanara.PInvoke /// The function accepts parameters similar to remove, with the addition of err and pv. // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcidelete [PInvokeData("fci.h", MSDNShortId = "5c85ad86-2794-4f7c-8c10-18fea3519b11")] - [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate int PFNFCIDELETE(string pszFile, out int err, IntPtr pv); /// @@ -88,7 +88,7 @@ namespace Vanara.PInvoke /// Returns 0 if the file was successfully replaced. A return value of –1 indicates an error. // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcifileplaced [PInvokeData("fci.h", MSDNShortId = "f8a1bcfc-8a13-49cf-a3e7-caec6c6421b0")] - [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate int PFNFCIFILEPLACED(ref CCAB pccab, string pszFile, int cbFile, [MarshalAs(UnmanagedType.Bool)] bool fContinuation, IntPtr pv); /// @@ -110,7 +110,7 @@ namespace Vanara.PInvoke /// // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcigetnextcabinet [PInvokeData("fci.h", MSDNShortId = "d56fb63e-91bf-4991-a954-176211697a2e")] - [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [return: MarshalAs(UnmanagedType.Bool)] public delegate bool PFNFCIGETNEXTCABINET(ref CCAB pccab, uint cbPrevCab, IntPtr pv); @@ -128,7 +128,7 @@ namespace Vanara.PInvoke /// The function should open the file using the file open function compatible with those passed into FCICreate. // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcigetopeninfo [PInvokeData("fci.h", MSDNShortId = "5baccb69-7872-4d67-ad74-70cdd7459f8d")] - [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate IntPtr PFNFCIGETOPENINFO(string pszName, ref ushort pdate, ref ushort ptime, ref ushort pattribs, out int err, IntPtr pv); /// @@ -145,7 +145,7 @@ namespace Vanara.PInvoke /// // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcigettempfile [PInvokeData("fci.h", MSDNShortId = "8978f688-d8f1-437a-b298-eed1e7dac012")] - [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [return: MarshalAs(UnmanagedType.Bool)] public delegate bool PFNFCIGETTEMPFILE(StringBuilder pszTempName, int cbTempName, IntPtr pv); @@ -161,7 +161,7 @@ namespace Vanara.PInvoke /// The function accepts parameters similar to _open. // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfciopen [PInvokeData("fci.h", MSDNShortId = "72cf50cb-c895-4953-9c4d-f8ddaa294f2a")] - [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate IntPtr PFNFCIOPEN(string pszFile, int oflag, int pmode, out int err, IntPtr pv); /// @@ -179,7 +179,7 @@ namespace Vanara.PInvoke /// // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfciread [PInvokeData("fci.h", MSDNShortId = "dd4e97ff-efbc-462b-b954-bc3260fa1513")] - [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate uint PFNFCIREAD(IntPtr hf, IntPtr memory, uint cb, out int err, IntPtr pv); /// @@ -197,7 +197,7 @@ namespace Vanara.PInvoke /// // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfciseek [PInvokeData("fci.h", MSDNShortId = "e5a14c98-4de6-452e-8993-afb7964aeee7")] - [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate int PFNFCISEEK(IntPtr hf, int dist, int seektype, out int err, IntPtr pv); /// @@ -218,7 +218,7 @@ namespace Vanara.PInvoke /// // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcistatus [PInvokeData("fci.h", MSDNShortId = "529fd3c8-9783-4dbe-9268-a9137935cf9b")] - [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate int PFNFCISTATUS(CabinetFileStatus typeStatus, uint cb1, uint cb2, IntPtr pv); /// @@ -234,7 +234,7 @@ namespace Vanara.PInvoke /// The function accepts parameters similar to _write. // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfciwrite [PInvokeData("fci.h", MSDNShortId = "ca4c3b5b-1ed5-4f12-8317-c1e1dac5f816")] - [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate uint PFNFCIWRITE(IntPtr hf, IntPtr memory, uint cb, out int err, IntPtr pv); /// @@ -248,7 +248,7 @@ namespace Vanara.PInvoke /// // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fnfcifree [PInvokeData("fci.h", MSDNShortId = "48f052e2-7786-430a-b3dc-afcfdffae387")] - [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Ansi)] + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate void PFNFREE(IntPtr memory); /// Used by . @@ -400,7 +400,7 @@ namespace Vanara.PInvoke // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fciaddfile BOOL DIAMONDAPI FCIAddFile( HFCI hfci, LPSTR // pszSourceFile, LPSTR pszFileName, BOOL fExecute, PFNFCIGETNEXTCABINET pfnfcignc, PFNFCISTATUS pfnfcis, PFNFCIGETOPENINFO // pfnfcigoi, TCOMP typeCompress ); - [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Ansi)] + [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [PInvokeData("fci.h", MSDNShortId = "f99e8718-853b-4d35-98ae-61a8333dbaba")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool FCIAddFile([In] HFCI hfci, string pszSourceFile, string pszFileName, [MarshalAs(UnmanagedType.Bool)] bool fExecute, @@ -459,7 +459,7 @@ namespace Vanara.PInvoke // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fcicreate HFCI DIAMONDAPI FCICreate( PERF perf, PFNFCIFILEPLACED // pfnfcifp, PFNFCIALLOC pfna, PFNFCIFREE pfnf, PFNFCIOPEN pfnopen, PFNFCIREAD pfnread, PFNFCIWRITE pfnwrite, PFNFCICLOSE pfnclose, // PFNFCISEEK pfnseek, PFNFCIDELETE pfndelete, PFNFCIGETTEMPFILE pfnfcigtf, PCCAB pccab, void *pv ); - [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Ansi)] + [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [PInvokeData("fci.h", MSDNShortId = "bfcea06d-2f09-405c-955c-0f56149148f2")] public static extern SafeHFCI FCICreate(ref ERF perf, [In] PFNFCIFILEPLACED pfnfcifp, [In] PFNALLOC pfna, [In] PFNFREE pfnf, [In] PFNFCIOPEN pfnopen, [In] PFNFCIREAD pfnread, [In] PFNFCIWRITE pfnwrite, [In] PFNFCICLOSE pfnclose, [In] PFNFCISEEK pfnseek, @@ -474,7 +474,7 @@ namespace Vanara.PInvoke /// Extended error information is provided in the ERF structure used to create the FCI context. /// // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fcidestroy BOOL DIAMONDAPI FCIDestroy( HFCI hfci ); - [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Ansi)] + [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [PInvokeData("fci.h", MSDNShortId = "bb1a6294-664f-450f-b8ec-d6f8957d920e")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool FCIDestroy([In] HFCI hfci); @@ -510,7 +510,7 @@ namespace Vanara.PInvoke /// // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fciflushcabinet BOOL DIAMONDAPI FCIFlushCabinet( HFCI hfci, BOOL // fGetNextCab, PFNFCIGETNEXTCABINET pfnfcignc, PFNFCISTATUS pfnfcis ); - [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Ansi)] + [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [PInvokeData("fci.h", MSDNShortId = "dc586260-180e-4a6b-accf-2ddd62ac1335")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool FCIFlushCabinet([In] HFCI hfci, [MarshalAs(UnmanagedType.Bool)] bool fGetNextCab, [In] PFNFCIGETNEXTCABINET pfnfcignc, [In] PFNFCISTATUS pfnfcis); @@ -540,7 +540,7 @@ namespace Vanara.PInvoke /// // https://docs.microsoft.com/en-us/windows/desktop/api/fci/nf-fci-fciflushfolder BOOL DIAMONDAPI FCIFlushFolder( HFCI hfci, // PFNFCIGETNEXTCABINET pfnfcignc, PFNFCISTATUS pfnfcis ); - [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CharSet = CharSet.Ansi)] + [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [PInvokeData("fci.h", MSDNShortId = "dc9c226e-e309-48c3-9edb-3f0a040c0c18")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool FCIFlushFolder([In] HFCI hfci, [In] PFNFCIGETNEXTCABINET pfnfcignc, [In] PFNFCISTATUS pfnfcis); @@ -664,7 +664,7 @@ namespace Vanara.PInvoke public static implicit operator HFCI(SafeHFCI h) => h.handle; /// - protected override bool InternalReleaseHandle() => FCIDestroy(this); + protected override bool InternalReleaseHandle() => FCIDestroy(handle); } } } \ No newline at end of file diff --git a/PInvoke/Cabinet/Fdi.cs b/PInvoke/Cabinet/Fdi.cs index 5b08a607..46af6288 100644 --- a/PInvoke/Cabinet/Fdi.cs +++ b/PInvoke/Cabinet/Fdi.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Runtime.InteropServices; namespace Vanara.PInvoke @@ -20,6 +21,7 @@ namespace Vanara.PInvoke /// The FDIDECRYPT structure. /// Undocumented [PInvokeData("fdi.h")] + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate int PFNFDIDECRYPT(ref FDIDECRYPT pfdid); /// @@ -35,6 +37,7 @@ namespace Vanara.PInvoke /// // https://docs.microsoft.com/en-us/windows/desktop/api/fdi/nf-fdi-fnfdinotify [PInvokeData("fdi.h", MSDNShortId = "7655ddb2-7cd4-4012-913c-9909fcea639a")] + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] public delegate IntPtr PFNFDINOTIFY(FDINOTIFICATIONTYPE fdint, ref FDINOTIFICATION pfdin); /// @@ -47,7 +50,8 @@ namespace Vanara.PInvoke /// 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 IntPtr PFNOPEN(string pszFile, int oflag, int pmode); + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + public delegate IntPtr PFNOPEN(string pszFile, RunTimeLib.FileOpConstant oflag, RunTimeLib.FilePermissionConstant pmode); /// /// The FNREAD macro provides the declaration for the application-defined callback function to read data from a file in an @@ -60,7 +64,8 @@ namespace Vanara.PInvoke /// 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(IntPtr hf, IntPtr memory, uint cb); + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + public delegate uint PFNREAD(IntPtr hf, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] memory, uint cb); /// /// The FNSEEK macro provides the declaration for the application-defined callback function to move a file pointer to the @@ -73,7 +78,8 @@ namespace Vanara.PInvoke /// 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(IntPtr hf, int dist, int seektype); + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + public delegate int PFNSEEK(IntPtr hf, int dist, SeekOrigin seektype); /// /// The FNWRITE macro provides the declaration for the application-defined callback function to write data to a file in an @@ -86,7 +92,22 @@ namespace Vanara.PInvoke /// 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(IntPtr hf, IntPtr memory, uint cb); + [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + public delegate uint PFNWRITE(IntPtr hf, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] memory, uint cb); + + /// Specifies the CPU type. + [PInvokeData("fdi.h")] + public enum FDICPU + { + /// FDI should determine the CPU type. + cpuUNKNOWN = -1, + + /// Only 80286 instructions can be used. + cpu80286 = 0, + + /// 80386 instructions can be used. + cpu80386 = 1 + } /// [PInvokeData("fdi.h")] @@ -229,55 +250,117 @@ namespace Vanara.PInvoke public enum FDINOTIFICATIONTYPE { /// - /// General information about the cabinet. When this value is set, the FDINOTIFICATION structure is populated with the following - /// information: The application should return 0 to indicate success, or -1 to indicate failure, which will abort FDICopy. An + /// General information about the cabinet. + /// When this value is set, the FDINOTIFICATION structure is populated with the following information: + /// + /// psz1 will point to the name of the next cabinet (excluding path information) + /// psz2 will point to the name of the next disk + /// psz3 will point to the cabinet path name + /// setID will equal the set ID of the current cabinet + /// + /// iCabinet will equal the cabinet number within the cabinet set (0 for the first cabinet, 1 for the second cabinet, etc.) + /// + /// + /// + /// The application should return 0 to indicate success, or -1 to indicate failure, which will abort FDICopy. An /// fdintCABINET_INFO notification will be provided once for each cabinet opened by FDICopy; this includes continuation cabinets /// opened due to files spanning cabinet boundaries. + /// /// fdintCABINET_INFO, /// - /// First file in the cabinet is a continuation of a file from previous cabinet. When this value is set, the FDINOTIFICATION - /// structure is populated with the following information: The fdintPARTIAL_FILE notification is called for files at the - /// beginning of a cabinet that have continued from a previous cabinet. This notification will occur only when FDICopy is - /// started on the second or subsequent cabinet in a series, which has files continued from a previous cabinet. The application - /// should return 0 for success, or -1 to indicate failure. + /// First file in the cabinet is a continuation of a file from previous cabinet. + /// When this value is set, the FDINOTIFICATION structure is populated with the following information: + /// + /// psz1 will point to the name of the file continued from a previous cabinet + /// psz2 will point to the name of the cabinet on which the first segment of the file exists + /// psz3 will point to the name of the disk on which the first segment of the file exists + /// + /// + /// The fdintPARTIAL_FILE notification is called for files at the beginning of a cabinet that have continued from a previous + /// cabinet. This notification will occur only when FDICopy is started on the second or subsequent cabinet in a series, which + /// has files continued from a previous cabinet. The application should return 0 for success, or -1 to indicate failure. + /// /// fdintPARTIAL_FILE, /// - /// Information identifying the file to be copied. When this value is set, the FDINOTIFICATION structure is populated with the - /// following information: The application should return one of three values; 0 to skip (i.e. not copy) the file; -1 (negative + /// Information identifying the file to be copied. + /// When this value is set, the FDINOTIFICATION structure is populated with the following information: + /// + /// psz1 will point to the name of a file in the cabinet + /// cb will equal the uncompressed size of the file + /// date will equal the file's 16-bit MS-DOS date + /// time will equal the file's 16-bit MS-DOS time + /// attribs will equal the file's 16-bit MS-DOS attributes + /// + /// + /// The application should return one of three values; 0 to skip (i.e. not copy) the file; -1 (negative /// one) to abort FDICopy; or a nonzero (and non-negative-one) file handle that indicates where to write the file. The file /// handle must be compatible with the PFNCLOSE function supplied to FDICreate. The fdintCOPY_FILE notification is called for /// each file that starts within the current cabinet, providing the opportunity for the application to request that the file be /// copied or skipped. + /// /// fdintCOPY_FILE, /// - /// Close the file, set relevant information. When this value is set, the FDINOTIFICATION structure is populated with the - /// following information: It is the responsibility of the application to execute the file if cb equals 1. The - /// fdintCLOSE_FILE_INFO notification is called after all of the data has been written to a target file. The application must - /// close the file (using the provided hf handle), and set the file date, time, and attributes. The application should return - /// TRUE for success, and FALSE or -1 to abort FDICopy. FDI assumes that the target file was closed, even if this callback - /// returns failure; FDI will not attempt to use PFNCLOSE to close the file. + /// Close the file, set relevant information. + /// When this value is set, the FDINOTIFICATION structure is populated with the following information: + /// + /// psz1 will point to the name of a file in the cabinet + /// hf will be a file handle (which originated from fdintCOPY_FILE) + /// date date will equal the file's 16-bit MS-DOS date + /// time time will equal the file's 16-bit MS-DOS time + /// attribs attributes will equal the file's 16-bit MS-DOS attributes (minus the _A_EXEC bit) + /// cb will equal either 0 or 1, indicating whether the file should be executed after extract (1), or not (0) + /// + /// + /// It is the responsibility of the application to execute the file if cb equals 1. The fdintCLOSE_FILE_INFO notification is + /// called after all of the data has been written to a target file. The application must close the file (using the provided hf + /// handle), and set the file date, time, and attributes. The application should return TRUE for success, and FALSE or -1 to + /// abort FDICopy. FDI assumes that the target file was closed, even if this callback returns failure; FDI will not attempt to + /// use PFNCLOSE to close the file. + /// /// fdintCLOSE_FILE_INFO, /// - /// File continued to next cabinet. When this value is set, the FDINOTIFICATION structure is populated with the following - /// information: This notification is called only if fdintCOPY_FILE is instructed to copy a file, which is continued from a - /// subsequent cabinet, to the current cabinet . Since it is possible for the application to modify the cabinet name, it is - /// important that the cabinet path name, indicated by psz3, be validated before it is returned. Additionally, the application - /// should ensure that the cabinet exists and is readable before returning; if necessary, the application should issue a disk - /// change prompt to confirm. When this function returns to FDI, FDI will verify that the setID and iCabinet fields of the - /// supplied cabinet match the expected values for that cabinet. If not, FDI will continue to send fdintNEXT_CABINET - /// notification messages with the fdie field set to FDIERROR_WRONG_CABINET, until the correct cabinet file is specified, or - /// until this function returns -1 and aborts the FDICopy call. If, after returning from this function, the cabinet file is not - /// present, readable, or has been damaged, then the fdie field will equal one of the following values: If there was no error, - /// fdie will equal FDIERROR_NONE. The application should return 0 to indicate success, or -1 to indicate failure, which will - /// abort FDICopy. + /// File continued to next cabinet. + /// When this value is set, the FDINOTIFICATION structure is populated with the following information: + /// + /// psz1 will point to the name of the next cabinet on which the current file is continued + /// psz2 will be a file handle (which originated from fdintCOPY_FILE) + /// psz3 will point to the cabinet path information + /// fdie will equal a success or error value + /// + /// + /// This notification is called only if fdintCOPY_FILE is instructed to copy a file, which is continued from a subsequent + /// cabinet, to the current cabinet . Since it is possible for the application to modify the cabinet name, it is important that + /// the cabinet path name, indicated by psz3, be validated before it is returned. Additionally, the application should ensure + /// that the cabinet exists and is readable before returning; if necessary, the application should issue a disk change prompt to confirm. + /// + /// + /// When this function returns to FDI, FDI will verify that the setID and iCabinet fields of the supplied cabinet match the + /// expected values for that cabinet. If not, FDI will continue to send fdintNEXT_CABINET notification messages with the fdie + /// field set to FDIERROR_WRONG_CABINET, until the correct cabinet file is specified, or until this function returns -1 and + /// aborts the FDICopy call. If, after returning from this function, the cabinet file is not present, readable, or has been + /// damaged, then the fdie field will equal one of the following values: + /// + /// + /// FDIERROR_CABINET_NOT_FOUND + /// FDIERROR_NOT_A_CABINET + /// FDIERROR_UNKNOWN_CABINET_VERSION + /// FDIERROR_CORRUPT_CABINET + /// FDIERROR_BAD_COMPR_TYPE + /// FDIERROR_RESERVE_MISMATCH + /// FDIERROR_WRONG_CABINET + /// + /// + /// If there was no error, fdie will equal FDIERROR_NONE. The application should return 0 to indicate success, or -1 to indicate + /// failure, which will abort FDICopy. + /// /// fdintNEXT_CABINET, @@ -308,11 +391,11 @@ namespace Vanara.PInvoke /// // 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)] + [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [PInvokeData("fdi.h", MSDNShortId = "6ec2b10b-f70a-4a22-beff-df6b6a4c4cfd")] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool FDICopy([In] HFDI hfdi, string pszCabinet, string pszCabPath, int flags, [In] PFNFDINOTIFY pfnfdin, - [In] PFNFDIDECRYPT pfnfdid, [In, Optional] IntPtr pvUser); + public static extern bool FDICopy([In] HFDI hfdi, string pszCabinet, string pszCabPath, [Optional] int flags, [In] PFNFDINOTIFY pfnfdin, + [In, Optional] PFNFDIDECRYPT pfnfdid, [In, Optional] IntPtr pvUser); /// The FDICreate function creates an FDI context. /// @@ -367,10 +450,15 @@ namespace Vanara.PInvoke /// // 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)] + [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [PInvokeData("fdi.h", MSDNShortId = "90634725-b7a8-4369-8a91-684debee9548")] - public static extern SafeHFDI FDICreate([In] PFNALLOC pfnalloc, [In] PFNFREE pfnfree, [In] PFNOPEN pfnopen, [In] PFNREAD pfnread, - [In] PFNWRITE pfnwrite, [In] PFNCLOSE pfnclose, [In] PFNSEEK pfnseek, int cpuType, ref ERF perf); + public static extern SafeHFDI FDICreate([In, MarshalAs(UnmanagedType.FunctionPtr)] PFNALLOC pfnalloc, + [In, MarshalAs(UnmanagedType.FunctionPtr)] PFNFREE pfnfree, + [In, MarshalAs(UnmanagedType.FunctionPtr)] PFNOPEN pfnopen, + [In, MarshalAs(UnmanagedType.FunctionPtr)] PFNREAD pfnread, + [In, MarshalAs(UnmanagedType.FunctionPtr)] PFNWRITE pfnwrite, + [In, MarshalAs(UnmanagedType.FunctionPtr)] PFNCLOSE pfnclose, + [In, MarshalAs(UnmanagedType.FunctionPtr)] PFNSEEK pfnseek, FDICPU cpuType, ref ERF perf); /// The FDIDestroy function deletes an open FDI context. /// A valid FDI context handle returned by the FDICreate function. @@ -379,7 +467,7 @@ namespace Vanara.PInvoke /// 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)] + [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [PInvokeData("fdi.h", MSDNShortId = "fe3b8045-a476-4a21-b732-0d4799798faf")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool FDIDestroy([In] HFDI hfdi); @@ -401,7 +489,7 @@ namespace Vanara.PInvoke /// // 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)] + [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [PInvokeData("fdi.h", MSDNShortId = "01d223ca-56c6-49fa-b9e6-e5eeda88936a")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool FDIIsCabinet([In] HFDI hfdi, [In] IntPtr hf, out FDICABINETINFO pfdici); @@ -416,7 +504,7 @@ namespace Vanara.PInvoke /// // 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)] + [DllImport(Lib.Cabinet, SetLastError = false, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [PInvokeData("fdi.h", MSDNShortId = "c923b0a5-1a8d-42aa-bd05-0d318199756d")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool FDITruncateCabinet([In] HFDI hfdi, string pszCabinetName, ushort iFolderToDelete); @@ -444,19 +532,22 @@ namespace Vanara.PInvoke public ushort iCabinet; /// If this value is set to TRUE, a reserved area is present in the cabinet. - [MarshalAs(UnmanagedType.Bool)] public bool fReserve; + [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; + [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; + [MarshalAs(UnmanagedType.Bool)] + public bool hasnext; } /// @@ -538,6 +629,7 @@ namespace Vanara.PInvoke public ushort cbData; ///TRUE if this is a split data block + [MarshalAs(UnmanagedType.Bool)] 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. @@ -687,6 +779,26 @@ namespace Vanara.PInvoke /// /// public FDIERROR fdie; + + /// The file attributes. + public FileFlagsAndAttributes Attributes => (FileFlagsAndAttributes)attribs; + + /// Gets the date and time. + /// The date and time value. + public DateTime DateTime + { + get + { + return new(Extensions.BitHelper.GetBits(date, 9, 7) + 1980, GetValidBits(date, 5, 4, 1, 12, 1), GetValidBits(date, 0, 5, 1, 31, 1), + GetValidBits(time, 11, 5, 0, 23, 0), GetValidBits(time, 5, 6, 0, 59, 0), GetValidBits(time, 0, 5, 0, 29, 0) * 2, DateTimeKind.Local); + + static ushort GetValidBits(ushort bits, byte start, byte len, ushort low, ushort high, ushort def) + { + var ret = Extensions.BitHelper.GetBits(bits, start, len); + return ret < low || ret > high ? def : ret; + } + } + } } /// Provides a handle to a file decompression interface. @@ -756,7 +868,7 @@ namespace Vanara.PInvoke public static implicit operator HFDI(SafeHFDI h) => h.handle; /// - protected override bool InternalReleaseHandle() => FDIDestroy(this); + protected override bool InternalReleaseHandle() => FDIDestroy(handle); } } } \ No newline at end of file diff --git a/UnitTests/PInvoke/Cabinet/CabinetTests.cs b/UnitTests/PInvoke/Cabinet/CabinetTests.cs index 13745656..78741978 100644 --- a/UnitTests/PInvoke/Cabinet/CabinetTests.cs +++ b/UnitTests/PInvoke/Cabinet/CabinetTests.cs @@ -1,27 +1,157 @@ using NUnit.Framework; using NUnit.Framework.Internal; using System; +using System.IO; using System.Runtime.InteropServices; +using Vanara.RunTimeLib; +using Vanara.Extensions; using static Vanara.PInvoke.Cabinet; +using System.Collections.Generic; namespace Vanara.PInvoke.Tests { [TestFixture] - public class CabinetTests + public class FDITests { + const string cabfn = "test.cab"; + static readonly string cabdir = TestCaseSources.TempDirWhack; + private ERF erf = new(); + private SafeHFDI handle; + [OneTimeSetUp] public void _Setup() { + handle = FDICreate(AllocCallback, FreeCallback, OpenCallback, ReadCallback, WriteCallback, CloseCallback, SeekCallback, FDICPU.cpuUNKNOWN, ref erf); } [OneTimeTearDown] public void _TearDown() { + handle?.Dispose(); } [Test] - public void Test() + public void FDICreateTest() { + SafeHFDI h; + ERF myerf = new(); + Assert.That(h = FDICreate(AllocCallback, FreeCallback, OpenCallback, ReadCallback, WriteCallback, CloseCallback, SeekCallback, FDICPU.cpuUNKNOWN, ref myerf), ResultIs.ValidHandle); + Assert.That(() => h?.Dispose(), Throws.Nothing); + } + + [Test, MTAThread] + public void FDICopyTest() + { + List files = new(); + Assert.That(FDICopy(handle, cabfn, cabdir, 0, Notify), Is.True); + Assert.That(files.Count, Is.GreaterThan(0)); + files.WriteValues(); + + IntPtr Notify(FDINOTIFICATIONTYPE fdint, ref FDINOTIFICATION pfdin) + { + switch (fdint) + { + case FDINOTIFICATIONTYPE.fdintCOPY_FILE: + files.Add($"{pfdin.psz1} : {pfdin.cb} : {pfdin.DateTime}"); + return IntPtr.Zero; + case FDINOTIFICATIONTYPE.fdintCLOSE_FILE_INFO: + return (IntPtr)1; + default: + break; + } + return IntPtr.Zero; + } + + + } + + private IntPtr AllocCallback(uint cb) => Marshal.AllocHGlobal((int)cb); + + private FileStream StreamFromHandle(IntPtr hf) => GCHandle.FromIntPtr(hf).Target as FileStream; + + private int CloseCallback(IntPtr hf) + { + FileStream stream = StreamFromHandle(hf); + if (stream is null) + return -1; + + stream.Dispose(); + ((GCHandle)hf).Free(); + return 0; + } + + private void FreeCallback(IntPtr memory) => Marshal.FreeHGlobal(memory); + + private IntPtr OpenCallback(string pszFile, RunTimeLib.FileOpConstant oflag, RunTimeLib.FilePermissionConstant pmode) + { + FileMode mode = oflag.ToFileMode(); + FileAccess access = pmode.ToFileAccess(); + FileShare share = pmode.ToFileShare(); + + try + { + FileStream stream = new FileStream(pszFile, mode, access, share); + return stream is null ? new IntPtr(-1) : GCHandle.ToIntPtr(GCHandle.Alloc(stream)); + } + catch (IOException) + { + return new IntPtr(-1); + } + } + + private uint ReadCallback(IntPtr hf, byte[] memory, uint cb) + { + FileStream stream = StreamFromHandle(hf); + + int numCharactersRead; + try + { + numCharactersRead = stream.Read(memory, 0, (int)cb); + } + catch (ArgumentNullException) { numCharactersRead = -1; } + catch (ArgumentOutOfRangeException) { numCharactersRead = -1; } + catch (NotSupportedException) { numCharactersRead = -1; } + catch (IOException) { numCharactersRead = -1; } + catch (ArgumentException) { numCharactersRead = -1; } + catch (ObjectDisposedException) { numCharactersRead = -1; } + + return unchecked((uint)numCharactersRead); + } + + private int SeekCallback(IntPtr hf, int dist, SeekOrigin seektype) + { + FileStream stream = StreamFromHandle(hf); + long status; + try + { + status = stream.Seek(dist, seektype); + } + catch (NotSupportedException) { status = -1; } + catch (IOException) { status = -1; } + catch (ArgumentException) { status = -1; } + catch (ObjectDisposedException) { status = -1; } + + return (int)status; + } + + private uint WriteCallback(IntPtr hf, byte[] memory, uint cb) + { + FileStream stream = StreamFromHandle(hf); + + int numCharactersWritten; + try + { + stream.Write(memory, 0, (int)cb); + numCharactersWritten = (int)cb; // Write doesn't return the number of bytes written. Per MSDN, if it succeeds, it will have written count bytes. + } + catch (ArgumentNullException) { numCharactersWritten = -1; } + catch (ArgumentOutOfRangeException) { numCharactersWritten = -1; } + catch (NotSupportedException) { numCharactersWritten = -1; } + catch (IOException) { numCharactersWritten = -1; } + catch (ArgumentException) { numCharactersWritten = -1; } + catch (ObjectDisposedException) { numCharactersWritten = -1; } + + return unchecked((uint)numCharactersWritten); } } } \ No newline at end of file