How to use SetupIterateCabinet with C#

5

I am attempting to write code to extract the contents of a CAB file, however I am having trouble using the SetupIterateCabinet routine.

Please see doc here http://msdn.microsoft.com/en-us/library/aa377404(v=vs.85).aspx

I can import it properly like this

    private const uint SPFILENOTIFY_CABINETINFO = 0x00000010;
    private const uint SPFILENOTIFY_FILEINCABINET = 0x00000011;
    private const uint SPFILENOTIFY_NEEDNEWCABINET = 0x00000012;
    private const uint SPFILENOTIFY_FILEEXTRACTED = 0x00000013;
    private const uint SPFILENOTIFY_FILEOPDELAYED = 0x00000014;
    private const uint NO_ERROR = 0;

    private const uint FILEOP_ABORT = 0;
    private const uint FILEOP_DOIT=                     1;
    private const uint FILEOP_SKIP=                     2;
    private const uint FILEOP_NEWPATH=                  4;

    static void Main(string[] args)
    {
        SetupIterateCabinet("c:\\SomeCab.cab", 0, new PSP_FILE_CALLBACK(CallBack), 0);


        Console.ReadKey();
    }

    [DllImport("SetupApi.dll", CharSet = CharSet.Auto)]
    public static extern bool SetupIterateCabinet(string cabinetFile,
                        uint reserved, PSP_FILE_CALLBACK callBack, uint context);

    public delegate uint PSP_FILE_CALLBACK(uint context, uint notification,
                                           IntPtr param1, IntPtr param2);

    private static uint CallBack(uint context, uint notification, IntPtr param1,
                         IntPtr param2)
    {
        uint rtnValue = NO_ERROR;
        switch (notification)
        {
            case SPFILENOTIFY_FILEINCABINET:
                rtnValue = OnFileFound(context, notification, param1, param2);
                break;
            case SPFILENOTIFY_FILEEXTRACTED:
                rtnValue = OnFileExtractComplete(param1);
                break;
            case SPFILENOTIFY_NEEDNEWCABINET:
                rtnValue = NO_ERROR;
                break;
        }
        return rtnValue;
    }

    private static uint OnFileExtractComplete(IntPtr param1)
    {
        Console.WriteLine("Complete");
        return FILEOP_DOIT;
    }


    [StructLayout(LayoutKind.Sequential)]
    struct _FILE_IN_CABINET_INFO {
        IntPtr NameInCabinet;
        int FileSize;
        int Win32Error;
        int  DosDate;
        int  DosTime;
        int  DosAttribs;
        StringBuilder FullTargetName;
    };

    static private uint OnFileFound(uint context, uint notification, IntPtr param1, IntPtr param2)
    {
        _FILE_IN_CABINET_INFO fc = new _FILE_IN_CABINET_INFO() ;
        Marshal.PtrToStructure(param1, fc);

        return 1;
    }

However the problem comes when attempting to process the SPFILENOTIFY_FILEINCABINET event in the callback. According to the documentation this is a struct, that I need to put the name of where I want to have the file extracted to in. I am having trouble figuring out what the struct should look like and maybe how to convert the param to a struct.

c#
asked on Stack Overflow May 23, 2011 by Matthew Sanford

1 Answer

0

I think you have a problem with the return values of your callback function. On SPFILENOTIFY_FILECABINET, you should be returning FILEOP_DOIT. Before returning you should be setting up the filename in the FILE_IN_CABINTE_INFO. Please check the codeproject post http://www.codeproject.com/Articles/7165/Iterate-and-Extract-Cabinet-File I might add some code sample later. GTG now

EDIT:

Code sample below. I haven't tried it, but I believe it should work. I have tried to keep the structure similar to your code. This should show you how to define the FILE_IN_CABINET_INFO class and the correct values to be set and returned in the callback

    public delegate uint PSP_FILE_CALLBACK(uint context, uint notification, IntPtr param1, IntPtr param2);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public class FILE_IN_CABINET_INFO {
        public String NameInCabinet;
        public uint FileSize;
        public uint Win32Error;
        public ushort DosDate;
        public ushort DosTime;
        public ushort DosAttribs;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public System.String FullTargetName;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public class FILEPATHS {
        public String Target;
        public String Source;
        public uint Win32Error;
        public uint Flags;
    } 


    public const uint SPFILENOTIFY_FILEINCABINET  = 0x00000011; // The file has been extracted from the cabinet.
    public const uint SPFILENOTIFY_NEEDNEWCABINET = 0x00000012; // file is encountered in the cabinet.
    public const uint SPFILENOTIFY_FILEEXTRACTED  = 0x00000013; // The current file is continued in the next cabinet.

    public const uint NO_ERROR = 0;

    public const uint FILEOP_ABORT = 0;   // Abort cabinet processing.
    public const uint FILEOP_DOIT  = 1;   // Extract the current file.
    public const uint FILEOP_SKIP  = 2;   // Skip the current file.

    [DllImport("SetupApi.dll", CharSet = CharSet.Auto)]
    public static extern bool SetupIterateCabinet(string cabinetFile, uint reserved, PSP_FILE_CALLBACK callBack, uint context);

    [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    public static extern uint GetLastError();

    static void Main(string[] args) {
        IterateCabinet(@"c:\SomeCab.cab");
    }

    public static void IterateCabinet(string filePath) {
        PSP_FILE_CALLBACK callback = new PSP_FILE_CALLBACK(CallBack);

        if (!SetupIterateCabinet(filePath, 0, callback, 0))
            throw new Win32Exception((int)GetLastError());
    }

    static uint CallBack(uint context, uint notification, IntPtr param1, IntPtr param2) {
        if (notification == SPFILENOTIFY_FILEINCABINET)
            return OnFileFound(context, notification, param1, param2);
        else if (notification == SPFILENOTIFY_FILEEXTRACTED)
            return OnFileExtractComplete(param1);
        else if (notification == SPFILENOTIFY_NEEDNEWCABINET)
            return NO_ERROR;
        return NO_ERROR;
    }

    static uint OnFileFound(uint context, uint notification, IntPtr param1, IntPtr param2) {
        FILE_IN_CABINET_INFO fileInCabinetInfo = (FILE_IN_CABINET_INFO)Marshal.PtrToStructure(param1, typeof(FILE_IN_CABINET_INFO));
        fileInCabinetInfo.FullTargetName = fileInCabinetInfo.NameInCabinet; // extract to current directory
        return FILEOP_DOIT;
    }

    static uint OnFileExtractComplete(IntPtr param1) {
        FILEPATHS filePaths = (FILEPATHS)Marshal.PtrToStructure(param1, typeof(FILEPATHS));

        if (filePaths.Win32Error == NO_ERROR)
            Console.WriteLine("File {0} extracted to {1} " + filePaths.Source, filePaths.Target);
        else
            Console.WriteLine("Errors occurred while extracting cab File {0} to {1} ", filePaths.Source, filePaths.Target);

        return filePaths.Win32Error;
    }
answered on Stack Overflow Dec 15, 2015 by Vikhram • edited Dec 18, 2015 by Vikhram

User contributions licensed under CC BY-SA 3.0