1

我有一个可用的 safari 扩展程序,我可以通过在 safari 网络浏览器上拖动它来手动安装它。我想知道如何以编程方式安装它。我已经为 firefox、chrome 和 IE 做了这个。在 Firefox 中,只需将您的 .xpi 文件复制到 Windows 7 中的此文件夹(“C:\Users\admin\AppData\Roaming\Mozilla\Firefox\Profiles\xxx.default\extensions”),您的扩展程序就会被安装。

在 chrome 中你必须编写这些注册表项

Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Google\Chrome\Extensions\dlilbimladfdhkfbbcbjjnbleakbogef]
"version"="3.6"
"path"="C:\\extension.crx"

但是在 safari 中,当我将 .safariextz 文件复制到此文件夹“C:\Users\admin\AppData\Local\Apple Computer\Safari\Extensions”时,未安装扩展名。任何人都可以指导我如何做到这一点。

4

2 回答 2

1

在文件夹中:

~\Users\\AppData\Local\Apple Computer\Safari\Extensions

有一个名为Extensions.plist的文件,您还需要在此文件中为您的扩展添加一个条目。

于 2012-07-31T11:04:25.090 回答
1

“~\Users\AppData\Local\Apple Computer\Safari\Extensions”文件夹中的 Extension.plist 是一个二进制文件。为了阅读和添加一个条目,我们可以使用这个类。

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace PlistCS
{
public static class Plist
{
    private static List<int> offsetTable = new List<int>();
    private static List<byte> objectTable = new List<byte>();
    private static int refCount;
    private static int objRefSize;
    private static int offsetByteSize;
    private static long offsetTableOffset;

    #region Public Functions

    public static object readPlist(string path)
    {
        using (FileStream f = new FileStream(path, FileMode.Open, FileAccess.Read))
        {
            return readPlist(f);
        }
    }

    public static object readPlistSource(string source)
    {
        return readPlist(System.Text.Encoding.UTF8.GetBytes(source));
    }

    public static object readPlist(byte[] data)
    {
        return readPlist(new MemoryStream(data));
    }

    public static plistType getPlistType(Stream stream)
    {
        byte[] magicHeader = new byte[8];
        stream.Read(magicHeader, 0, 8);

        if (BitConverter.ToInt64(magicHeader, 0) == 3472403351741427810)
        {
            return plistType.Binary;
        }
        else
        {
            return plistType.Xml;
        }
    }

    public static object readPlist(Stream stream, plistType type = plistType.Auto)
    {
        if (type == plistType.Auto)
        {
            type = getPlistType(stream);
            stream.Seek(0, SeekOrigin.Begin);
        }

        if (type == plistType.Binary)
        {
            using (BinaryReader reader = new BinaryReader(stream))
            {
                byte[] data = reader.ReadBytes((int)reader.BaseStream.Length);
                return readBinary(data);
            }
        }
        else
        {
            using (BinaryReader reader = new BinaryReader(stream))
            {
                byte[] data = reader.ReadBytes((int)reader.BaseStream.Length);
                return readBinary(data);
            }
        }
    }

    public static void writeBinary(object value, string path)
    {
        using (BinaryWriter writer = new BinaryWriter(new FileStream(path, FileMode.Create)))
        {
            writer.Write(writeBinary(value));
        }
    }

    public static void writeBinary(object value, Stream stream)
    {
        using (BinaryWriter writer = new BinaryWriter(stream))
        {
            writer.Write(writeBinary(value));
        }
    }

    public static byte[] writeBinary(object value)
    {
        offsetTable.Clear();
        objectTable.Clear();
        refCount = 0;
        objRefSize = 0;
        offsetByteSize = 0;
        offsetTableOffset = 0;

        //Do not count the root node, subtract by 1
        int totalRefs = countObject(value) - 1;

        refCount = totalRefs;

        objRefSize = RegulateNullBytes(BitConverter.GetBytes(refCount)).Length;

        composeBinary(value);

        writeBinaryString("bplist00", false);

        offsetTableOffset = (long)objectTable.Count;

        offsetTable.Add(objectTable.Count - 8);

        offsetByteSize = RegulateNullBytes(BitConverter.GetBytes(offsetTable[offsetTable.Count - 1])).Length;

        List<byte> offsetBytes = new List<byte>();

        offsetTable.Reverse();

        for (int i = 0; i < offsetTable.Count; i++)
        {
            offsetTable[i] = objectTable.Count - offsetTable[i];
            byte[] buffer = RegulateNullBytes(BitConverter.GetBytes(offsetTable[i]), offsetByteSize);
            Array.Reverse(buffer);
            offsetBytes.AddRange(buffer);
        }

        objectTable.AddRange(offsetBytes);

        objectTable.AddRange(new byte[6]);
        objectTable.Add(Convert.ToByte(offsetByteSize));
        objectTable.Add(Convert.ToByte(objRefSize));

        var a = BitConverter.GetBytes((long)totalRefs + 1);
        Array.Reverse(a);
        objectTable.AddRange(a);

        objectTable.AddRange(BitConverter.GetBytes((long)0));
        a = BitConverter.GetBytes(offsetTableOffset);
        Array.Reverse(a);
        objectTable.AddRange(a);

        return objectTable.ToArray();
    }

    #endregion

    #region Private Functions

    private static object readBinary(byte[] data)
    {
        offsetTable.Clear();
        List<byte> offsetTableBytes = new List<byte>();
        objectTable.Clear();
        refCount = 0;
        objRefSize = 0;
        offsetByteSize = 0;
        offsetTableOffset = 0;

        List<byte> bList = new List<byte>(data);

        List<byte> trailer = bList.GetRange(bList.Count - 32, 32);

        parseTrailer(trailer);

        objectTable = bList.GetRange(0, (int)offsetTableOffset);

        offsetTableBytes = bList.GetRange((int)offsetTableOffset, bList.Count - (int)offsetTableOffset - 32);

        parseOffsetTable(offsetTableBytes);

        return parseBinary(0);
    }


    private static int countObject(object value)
    {
        int count = 0;
        switch (value.GetType().ToString())
        {
            case "System.Collections.Generic.Dictionary`2[System.String,System.Object]":
                Dictionary<string, object> dict = (Dictionary<string, object>)value;
                foreach (string key in dict.Keys)
                {
                    count += countObject(dict[key]);
                }
                count += dict.Keys.Count;
                count++;
                break;
            case "System.Collections.Generic.List`1[System.Object]":
                List<object> list = (List<object>)value;
                foreach (object obj in list)
                {
                    count += countObject(obj);
                }
                count++;
                break;
            default:
                count++;
                break;
        }
        return count;
    }

    private static byte[] writeBinaryDictionary(Dictionary<string, object> dictionary)
    {
        List<byte> buffer = new List<byte>();
        List<byte> header = new List<byte>();
        List<int> refs = new List<int>();
        for (int i = dictionary.Count - 1; i >= 0; i--)
        {
            var o = new object[dictionary.Count];
            dictionary.Values.CopyTo(o, 0);
            composeBinary(o[i]);
            offsetTable.Add(objectTable.Count);
            refs.Add(refCount);
            refCount--;
        }
        for (int i = dictionary.Count - 1; i >= 0; i--)
        {
            var o = new string[dictionary.Count];
            dictionary.Keys.CopyTo(o, 0);
            composeBinary(o[i]);//);
            offsetTable.Add(objectTable.Count);
            refs.Add(refCount);
            refCount--;
        }

        if (dictionary.Count < 15)
        {
            header.Add(Convert.ToByte(0xD0 | Convert.ToByte(dictionary.Count)));
        }
        else
        {
            header.Add(0xD0 | 0xf);
            header.AddRange(writeBinaryInteger(dictionary.Count, false));
        }


        foreach (int val in refs)
        {
            byte[] refBuffer = RegulateNullBytes(BitConverter.GetBytes(val), objRefSize);
            Array.Reverse(refBuffer);
            buffer.InsertRange(0, refBuffer);
        }

        buffer.InsertRange(0, header);


        objectTable.InsertRange(0, buffer);

        return buffer.ToArray();
    }

    private static byte[] composeBinaryArray(List<object> objects)
    {
        List<byte> buffer = new List<byte>();
        List<byte> header = new List<byte>();
        List<int> refs = new List<int>();

        for (int i = objects.Count - 1; i >= 0; i--)
        {
            composeBinary(objects[i]);
            offsetTable.Add(objectTable.Count);
            refs.Add(refCount);
            refCount--;
        }

        if (objects.Count < 15)
        {
            header.Add(Convert.ToByte(0xA0 | Convert.ToByte(objects.Count)));
        }
        else
        {
            header.Add(0xA0 | 0xf);
            header.AddRange(writeBinaryInteger(objects.Count, false));
        }

        foreach (int val in refs)
        {
            byte[] refBuffer = RegulateNullBytes(BitConverter.GetBytes(val), objRefSize);
            Array.Reverse(refBuffer);
            buffer.InsertRange(0, refBuffer);
        }

        buffer.InsertRange(0, header);

        objectTable.InsertRange(0, buffer);

        return buffer.ToArray();
    }

    private static byte[] composeBinary(object obj)
    {
        byte[] value;
        switch (obj.GetType().ToString())
        {
            case "System.Collections.Generic.Dictionary`2[System.String,System.Object]":
                value = writeBinaryDictionary((Dictionary<string, object>)obj);
                return value;

            case "System.Collections.Generic.List`1[System.Object]":
                value = composeBinaryArray((List<object>)obj);
                return value;

            case "System.Byte[]":
                value = writeBinaryByteArray((byte[])obj);
                return value;

            case "System.Double":
                value = writeBinaryDouble((double)obj);
                return value;

            case "System.Int32":
                value = writeBinaryInteger((int)obj, true);
                return value;

            case "System.String":
                value = writeBinaryString((string)obj, true);
                return value;

            case "System.DateTime":
                value = writeBinaryDate((DateTime)obj);
                return value;

            case "System.Boolean":
                value = writeBinaryBool((bool)obj);
                return value;

            default:
                return new byte[0];
        }
    }

    public static byte[] writeBinaryDate(DateTime obj)
    {
        List<byte> buffer = new List<byte>(RegulateNullBytes(BitConverter.GetBytes(PlistDateConverter.ConvertToAppleTimeStamp(obj)), 8));
        buffer.Reverse();
        buffer.Insert(0, 0x33);
        objectTable.InsertRange(0, buffer);
        return buffer.ToArray();
    }

    public static byte[] writeBinaryBool(bool obj)
    {
        List<byte> buffer = new List<byte>(new byte[1] { (bool)obj ? (byte)9 : (byte)8 });
        objectTable.InsertRange(0, buffer);
        return buffer.ToArray();
    }

    private static byte[] writeBinaryInteger(int value, bool write)
    {
        List<byte> buffer = new List<byte>(BitConverter.GetBytes((long)value));
        buffer = new List<byte>(RegulateNullBytes(buffer.ToArray()));
        while (buffer.Count != Math.Pow(2, Math.Log(buffer.Count) / Math.Log(2)))
            buffer.Add(0);
        int header = 0x10 | (int)(Math.Log(buffer.Count) / Math.Log(2));

        buffer.Reverse();

        buffer.Insert(0, Convert.ToByte(header));

        if (write)
            objectTable.InsertRange(0, buffer);

        return buffer.ToArray();
    }

    private static byte[] writeBinaryDouble(double value)
    {
        List<byte> buffer = new List<byte>(RegulateNullBytes(BitConverter.GetBytes(value), 4));
        while (buffer.Count != Math.Pow(2, Math.Log(buffer.Count) / Math.Log(2)))
            buffer.Add(0);
        int header = 0x20 | (int)(Math.Log(buffer.Count) / Math.Log(2));

        buffer.Reverse();

        buffer.Insert(0, Convert.ToByte(header));

        objectTable.InsertRange(0, buffer);

        return buffer.ToArray();
    }

    private static byte[] writeBinaryByteArray(byte[] value)
    {
        List<byte> buffer = new List<byte>(value);
        List<byte> header = new List<byte>();
        if (value.Length < 15)
        {
            header.Add(Convert.ToByte(0x40 | Convert.ToByte(value.Length)));
        }
        else
        {
            header.Add(0x40 | 0xf);
            header.AddRange(writeBinaryInteger(buffer.Count, false));
        }

        buffer.InsertRange(0, header);

        objectTable.InsertRange(0, buffer);

        return buffer.ToArray();
    }

    private static byte[] writeBinaryString(string value, bool head)
    {
        List<byte> buffer = new List<byte>();
        List<byte> header = new List<byte>();
        foreach (char chr in value.ToCharArray())
            buffer.Add(Convert.ToByte(chr));

        if (head)
        {
            if (value.Length < 15)
            {
                header.Add(Convert.ToByte(0x50 | Convert.ToByte(value.Length)));
            }
            else
            {
                header.Add(0x50 | 0xf);
                header.AddRange(writeBinaryInteger(buffer.Count, false));
            }
        }

        buffer.InsertRange(0, header);

        objectTable.InsertRange(0, buffer);

        return buffer.ToArray();
    }

    private static byte[] RegulateNullBytes(byte[] value)
    {
        return RegulateNullBytes(value, 1);
    }

    private static byte[] RegulateNullBytes(byte[] value, int minBytes)
    {
        Array.Reverse(value);
        List<byte> bytes = new List<byte>(value);
        for (int i = 0; i < bytes.Count; i++)
        {
            if (bytes[i] == 0 && bytes.Count > minBytes)
            {
                bytes.Remove(bytes[i]);
                i--;
            }
            else
                break;
        }

        if (bytes.Count < minBytes)
        {
            int dist = minBytes - bytes.Count;
            for (int i = 0; i < dist; i++)
                bytes.Insert(0, 0);
        }

        value = bytes.ToArray();
        Array.Reverse(value);
        return value;
    }

    private static void parseTrailer(List<byte> trailer)
    {
        offsetByteSize = BitConverter.ToInt32(RegulateNullBytes(trailer.GetRange(6, 1).ToArray(), 4), 0);
        objRefSize = BitConverter.ToInt32(RegulateNullBytes(trailer.GetRange(7, 1).ToArray(), 4), 0);
        byte[] refCountBytes = trailer.GetRange(12, 4).ToArray();
        Array.Reverse(refCountBytes);
        refCount = BitConverter.ToInt32(refCountBytes, 0);
        byte[] offsetTableOffsetBytes = trailer.GetRange(24, 8).ToArray();
        Array.Reverse(offsetTableOffsetBytes);
        offsetTableOffset = BitConverter.ToInt64(offsetTableOffsetBytes, 0);
    }

    private static void parseOffsetTable(List<byte> offsetTableBytes)
    {
        for (int i = 0; i < offsetTableBytes.Count; i += offsetByteSize)
        {
            byte[] buffer = offsetTableBytes.GetRange(i, offsetByteSize).ToArray();
            Array.Reverse(buffer);
            offsetTable.Add(BitConverter.ToInt32(RegulateNullBytes(buffer, 4), 0));
        }
    }

    private static object parseBinaryDictionary(int objRef)
    {
        Dictionary<string, object> buffer = new Dictionary<string, object>();
        List<int> refs = new List<int>();
        int refCount = 0;

        byte dictByte = objectTable[offsetTable[objRef]];

        int refStartPosition;
        refCount = getCount(offsetTable[objRef], out refStartPosition);


        if (refCount < 15)
            refStartPosition = offsetTable[objRef] + 1;
        else
            refStartPosition = offsetTable[objRef] + 2 + RegulateNullBytes(BitConverter.GetBytes(refCount), 1).Length;

        for (int i = refStartPosition; i < refStartPosition + refCount * 2 * objRefSize; i += objRefSize)
        {
            byte[] refBuffer = objectTable.GetRange(i, objRefSize).ToArray();
            Array.Reverse(refBuffer);
            refs.Add(BitConverter.ToInt32(RegulateNullBytes(refBuffer, 4), 0));
        }

        for (int i = 0; i < refCount; i++)
        {
            buffer.Add((string)parseBinary(refs[i]), parseBinary(refs[i + refCount]));
        }

        return buffer;
    }

    private static object parseBinaryArray(int objRef)
    {
        List<object> buffer = new List<object>();
        List<int> refs = new List<int>();
        int refCount = 0;

        byte arrayByte = objectTable[offsetTable[objRef]];

        int refStartPosition;
        refCount = getCount(offsetTable[objRef], out refStartPosition);


        if (refCount < 15)
            refStartPosition = offsetTable[objRef] + 1;
        else
            //The following integer has a header aswell so we increase the refStartPosition by two to account for that.
            refStartPosition = offsetTable[objRef] + 2 + RegulateNullBytes(BitConverter.GetBytes(refCount), 1).Length;

        for (int i = refStartPosition; i < refStartPosition + refCount * objRefSize; i += objRefSize)
        {
            byte[] refBuffer = objectTable.GetRange(i, objRefSize).ToArray();
            Array.Reverse(refBuffer);
            refs.Add(BitConverter.ToInt32(RegulateNullBytes(refBuffer, 4), 0));
        }

        for (int i = 0; i < refCount; i++)
        {
            buffer.Add(parseBinary(refs[i]));
        }

        return buffer;
    }

    private static int getCount(int bytePosition, out int newBytePosition)
    {
        byte headerByte = objectTable[bytePosition];
        byte headerByteTrail = Convert.ToByte(headerByte & 0xf);
        int count;
        if (headerByteTrail < 15)
        {
            count = headerByteTrail;
            newBytePosition = bytePosition + 1;
        }
        else
            count = (int)parseBinaryInt(bytePosition + 1, out newBytePosition);
        return count;
    }

    private static object parseBinary(int objRef)
    {
        byte header = objectTable[offsetTable[objRef]];
        switch (header & 0xF0)
        {
            case 0:
                {
                    //If the byte is
                    //0 return null
                    //9 return true
                    //8 return false
                    return (objectTable[offsetTable[objRef]] == 0) ? (object)null : ((objectTable[offsetTable[objRef]] == 9) ? true : false);
                }
            case 0x10:
                {
                    return parseBinaryInt(offsetTable[objRef]);
                }
            case 0x20:
                {
                    return parseBinaryReal(offsetTable[objRef]);
                }
            case 0x30:
                {
                    return parseBinaryDate(offsetTable[objRef]);
                }
            case 0x40:
                {
                    return parseBinaryByteArray(offsetTable[objRef]);
                }
            case 0x50://String ASCII
                {
                    return parseBinaryAsciiString(offsetTable[objRef]);
                }
            case 0x60://String Unicode
                {
                    return parseBinaryUnicodeString(offsetTable[objRef]);
                }
            case 0xD0:
                {
                    return parseBinaryDictionary(objRef);
                }
            case 0xA0:
                {
                    return parseBinaryArray(objRef);
                }
        }
        throw new Exception("This type is not supported");
    }

    public static object parseBinaryDate(int headerPosition)
    {
        byte[] buffer = objectTable.GetRange(headerPosition + 1, 8).ToArray();
        Array.Reverse(buffer);
        double appleTime = BitConverter.ToDouble(buffer, 0);
        DateTime result = PlistDateConverter.ConvertFromAppleTimeStamp(appleTime);
        return result;
    }

    private static object parseBinaryInt(int headerPosition)
    {
        int output;
        return parseBinaryInt(headerPosition, out output);
    }

    private static object parseBinaryInt(int headerPosition, out int newHeaderPosition)
    {
        byte header = objectTable[headerPosition];
        int byteCount = (int)Math.Pow(2, header & 0xf);
        byte[] buffer = objectTable.GetRange(headerPosition + 1, byteCount).ToArray();
        Array.Reverse(buffer);
        //Add one to account for the header byte
        newHeaderPosition = headerPosition + byteCount + 1;
        return BitConverter.ToInt32(RegulateNullBytes(buffer, 4), 0);
    }

    private static object parseBinaryReal(int headerPosition)
    {
        byte header = objectTable[headerPosition];
        int byteCount = (int)Math.Pow(2, header & 0xf);
        byte[] buffer = objectTable.GetRange(headerPosition + 1, byteCount).ToArray();
        Array.Reverse(buffer);

        return BitConverter.ToDouble(RegulateNullBytes(buffer, 8), 0);
    }

    private static object parseBinaryAsciiString(int headerPosition)
    {
        int charStartPosition;
        int charCount = getCount(headerPosition, out charStartPosition);

        var buffer = objectTable.GetRange(charStartPosition, charCount);
        return buffer.Count > 0 ? Encoding.ASCII.GetString(buffer.ToArray()) : string.Empty;
    }

    private static object parseBinaryUnicodeString(int headerPosition)
    {
        int charStartPosition;
        int charCount = getCount(headerPosition, out charStartPosition);
        charCount = charCount * 2;

        byte[] buffer = new byte[charCount];
        byte one, two;

        for (int i = 0; i < charCount; i += 2)
        {
            one = objectTable.GetRange(charStartPosition + i, 1)[0];
            two = objectTable.GetRange(charStartPosition + i + 1, 1)[0];

            if (BitConverter.IsLittleEndian)
            {
                buffer[i] = two;
                buffer[i + 1] = one;
            }
            else
            {
                buffer[i] = one;
                buffer[i + 1] = two;
            }
        }

        return Encoding.Unicode.GetString(buffer);
    }

    private static object parseBinaryByteArray(int headerPosition)
    {
        int byteStartPosition;
        int byteCount = getCount(headerPosition, out byteStartPosition);
        return objectTable.GetRange(byteStartPosition, byteCount).ToArray();
    }

    #endregion
}

public enum plistType
{
    Auto, Binary, Xml
}

public static class PlistDateConverter
{
    public static long timeDifference = 978307200;

    public static long GetAppleTime(long unixTime)
    {
        return unixTime - timeDifference;
    }

    public static long GetUnixTime(long appleTime)
    {
        return appleTime + timeDifference;
    }

    public static DateTime ConvertFromAppleTimeStamp(double timestamp)
    {
        DateTime origin = new DateTime(2001, 1, 1, 0, 0, 0, 0);
        return origin.AddSeconds(timestamp);
    }

    public static double ConvertToAppleTimeStamp(DateTime date)
    {
        DateTime begin = new DateTime(2001, 1, 1, 0, 0, 0, 0);
        TimeSpan diff = date - begin;
        return Math.Floor(diff.TotalSeconds);
    }
}

}

并在 Installer.cs 类的提交操作中使用此方法添加扩展 Extension.plist 的条目

public void InstallSafariExt()
    {
        string safariExtPlist = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Apple Computer\\Safari\\Extensions\\Extensions.plist";
        string safariSetupPlist = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + "\YourComp\\YourSoft\\Extensions.plist";

        string ExtDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Apple Computer\\Safari\\Extensions";
        if (!Directory.Exists(ExtDir))
        {
            Directory.CreateDirectory(ExtDir);
            if (!File.Exists(safariExtPlist))
            {
                File.Copy(safariSetupPlist, safariExtPlist);
            }
        }
        else
        {
            if (!File.Exists(safariExtPlist))
            {
                File.Copy(safariSetupPlist, safariExtPlist);
            }
        }

        object obj = Plist.readPlist(safariExtPlist);
        Dictionary<string, object> dict = (Dictionary<string, object>)obj;
        Dictionary<string, object> NewExt = new Dictionary<string, object>();
        NewExt.Add("Hidden Bars", new List<object>());
        NewExt.Add("Added Non-Default Toolbar Items", new List<object>());
        NewExt.Add("Enabled", true);
        NewExt.Add("Archive File Name", "YourExtName.safariextz");
        NewExt.Add("Removed Default Toolbar Items", new List<object>());
        NewExt.Add("Bundle Directory Name", "YourExtName.safariextension");
        List<object> listExt = (List<object>)dict["Installed Extensions"];
        listExt.Add(NewExt);
        Plist.writeBinary(obj, safariExtPlist);

        string safariExtFile = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + "\YourComp\\YourSoft\\YourExtName.safariextz";
        string safariInstallfolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Apple Computer\\Safari\\Extensions\\YourExtName.safariextz";

        string[] safExtFiles = Directory.GetFiles(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Apple Computer\\Safari\\Extensions\\", "YourExtName*.safariextz");
        for (int i = 0; i < safExtFiles.Length; i++)
        {
            if (File.Exists(safExtFiles[i]))
                File.Delete(safExtFiles[i]);
        }

        File.Copy(safariExtFile, safariInstallfolder);
    }
于 2012-08-01T04:58:10.473 回答