/* * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; namespace Tizen.Applications { /// /// Represents the control message to exchange between applications. /// /// /// /// public class AppControlExample : UIApplication /// { /// /// ... /// protected override void OnAppControlReceived(AppControlReceivedEventArgs e) /// { /// AppControl appControl = new AppControl(); /// appControl.ApplicationId = "org.tizen.calculator"; /// AppControl.SendLaunchRequest(appControl, (launchRequest, replyRequest, result) => { /// // ... /// }); /// } /// } /// /// public class AppControl { private const string LogTag = "Tizen.Applications"; private static Dictionary s_replyNativeCallbackMaps = new Dictionary(); private static int s_replyNativeCallbackId = 0; private readonly SafeAppControlHandle _handle; private string _operation = null; private string _mime = null; private string _uri = null; private string _category = null; private string _applicationId = null; private ExtraDataCollection _extraData = null; /// /// Initializes the instance of the AppControl class. /// /// Thrown when failed to create AppControl handle. public AppControl() { Interop.AppControl.ErrorCode err = Interop.AppControl.Create(out _handle); if (err != Interop.AppControl.ErrorCode.None) { throw new InvalidOperationException("Failed to create the appcontrol handle. Err = " + err); } } /// /// Initializes the instance of the AppControl class with parameter. /// /// The flag value to receive an additional launch result event on launch request. /// Thrown when failed to create AppControl handle. public AppControl(bool enableAppStartedResultEvent) { Interop.AppControl.ErrorCode err = Interop.AppControl.Create(out _handle); if (err != Interop.AppControl.ErrorCode.None) { throw new InvalidOperationException("Failed to create the appcontrol handle. Err = " + err); } if (enableAppStartedResultEvent) { err = Interop.AppControl.EnableAppStartedResultEvent(_handle); if (err != Interop.AppControl.ErrorCode.None) { throw new InvalidOperationException("Failed to set EnableAppStartedResultEvent"); } } } /// /// Initializes the instance of the AppControl class with the SafeAppControlHandle. /// /// public AppControl(SafeAppControlHandle handle) { if (handle == null) { throw new ArgumentNullException("handle"); } Interop.AppControl.ErrorCode err = Interop.AppControl.DangerousClone(out _handle, handle.DangerousGetHandle()); if (err != Interop.AppControl.ErrorCode.None) { throw new InvalidOperationException("Failed to clone the appcontrol handle. Err = " + err); } } private AppControl(IntPtr handle) { Interop.AppControl.ErrorCode err = Interop.AppControl.DangerousClone(out _handle, handle); if (err != Interop.AppControl.ErrorCode.None) { throw new InvalidOperationException("Failed to clone the appcontrol handle. Err = " + err); } } #region Public Properties /// /// Gets the SafeAppControlHandle instance. /// public SafeAppControlHandle SafeAppControlHandle { get { return _handle; } } /// /// Gets and sets the operation to be performed. /// /// /// The operation is the mandatory information for the launch request. If the operation is not specified, /// AppControlOperations.Default is used for the launch request. If the operation is AppControlOperations.Default, /// the package information is mandatory to explicitly launch the application. /// (if the operation is null for setter, it clears the previous value.) /// /// /// /// AppControl appControl = new AppControl(); /// appControl.Operation = AppControlOperations.Default; /// Log.Debug(LogTag, "Operation: " + appControl.Operation); /// /// public string Operation { get { if (String.IsNullOrEmpty(_operation)) { Interop.AppControl.ErrorCode err = Interop.AppControl.GetOperation(_handle, out _operation); if (err != Interop.AppControl.ErrorCode.None) { Log.Warn(LogTag, "Failed to get the operation from the appcontrol. Err = " + err); } } return _operation; } set { Interop.AppControl.ErrorCode err = Interop.AppControl.SetOperation(_handle, value); if (err == Interop.AppControl.ErrorCode.None) { _operation = value; } else { Log.Warn(LogTag, "Failed to set the operation to the appcontrol. Err = " + err); } } } /// /// Gets and sets the explicit MIME type of the data. /// /// /// (if the mime is null for setter, it clears the previous value.) /// /// /// /// AppControl appControl = new AppControl(); /// appControl.Mime = "image/jpg"; /// Log.Debug(LogTag, "Mime: " + appControl.Mime); /// /// public string Mime { get { if (String.IsNullOrEmpty(_mime)) { Interop.AppControl.ErrorCode err = Interop.AppControl.GetMime(_handle, out _mime); if (err != Interop.AppControl.ErrorCode.None) { Log.Warn(LogTag, "Failed to get the mime from the appcontrol. Err = " + err); } } return _mime; } set { Interop.AppControl.ErrorCode err = Interop.AppControl.SetMime(_handle, value); if (err == Interop.AppControl.ErrorCode.None) { _mime = value; } else { Log.Warn(LogTag, "Failed to set the mime to the appcontrol. Err = " + err); } } } /// /// Gets and sets the URI of the data. /// /// /// Since Tizen 2.4, if the parameter 'uri' is started with 'file://' and /// it is a regular file in this application's data path which can be obtained /// by property DataPath in ApplicationInfo class, /// it will be shared to the callee application. /// Framework will grant a temporary permission to the callee application for this file and /// revoke it when the callee application is terminated. /// The callee application can just read it. /// (if the uri is null for setter, it clears the previous value.) /// /// /// /// public class AppControlExample : UIApplication /// { /// ... /// protected override void OnAppControlReceived(AppControlReceivedEventArgs e) /// { /// ... /// AppControl appControl = new AppControl(); /// appContrl.Uri = this.ApplicationInfo.DataPath + "image.jpg"; /// Log.Debug(LogTag, "Set Uri: " + appControl.Uri); /// } /// } /// /// public string Uri { get { if (String.IsNullOrEmpty(_uri)) { Interop.AppControl.ErrorCode err = Interop.AppControl.GetUri(_handle, out _uri); if (err != Interop.AppControl.ErrorCode.None) { Log.Warn(LogTag, "Failed to get the uri from the appcontrol. Err = " + err); } } return _uri; } set { Interop.AppControl.ErrorCode err = Interop.AppControl.SetUri(_handle, value); if (err == Interop.AppControl.ErrorCode.None) { _uri = value; } else { Log.Warn(LogTag, "Failed to set the uri to the appcontrol. Err = " + err); } } } /// /// Gets and sets the explicit category. /// /// /// (if the category is null for setter, it clears the previous value.) /// public string Category { get { if (String.IsNullOrEmpty(_category)) { Interop.AppControl.ErrorCode err = Interop.AppControl.GetCategory(_handle, out _category); if (err != Interop.AppControl.ErrorCode.None) { Log.Warn(LogTag, "Failed to get the category from the appcontrol. Err = " + err); } } return _category; } set { Interop.AppControl.ErrorCode err = Interop.AppControl.SetCategory(_handle, value); if (err == Interop.AppControl.ErrorCode.None) { _category = value; } else { Log.Warn(LogTag, "Failed to set the category to the appcontrol. Err = " + err); } } } /// /// Gets and sets the application id to explicitly launch. /// /// /// (if the application id is null for setter, it clears the previous value.) /// /// /// /// AppControl appControl = new AppControl(); /// appControl.ApplicationId = "org.tizen.calculator"; /// Log.Debug(LogTag, "ApplicationId: " + appControl.ApplicationId); /// /// public string ApplicationId { get { if (String.IsNullOrEmpty(_applicationId)) { Interop.AppControl.ErrorCode err = Interop.AppControl.GetAppId(_handle, out _applicationId); if (err != Interop.AppControl.ErrorCode.None) { Log.Warn(LogTag, "Failed to get the application id from the AppControl. Err = " + err); } } return _applicationId; } set { Interop.AppControl.ErrorCode err = Interop.AppControl.SetAppId(_handle, value); if (err == Interop.AppControl.ErrorCode.None) { _applicationId = value; } else { Log.Warn(LogTag, "Failed to set the application id to the AppControl. Err = " + err); } } } /// /// Gets and sets the launch mode of the application. /// /// /// Although LaunchMode were set as AppControlLaunchMode.Group, /// callee application would be launched as single mode /// if the manifest file of callee application defined the launch mode as "single". /// This property can just set the preference of caller application to launch an application. /// Sub-applications which were launched as group mode always have own process. /// Since Tizen 3.0, if launch mode not set in the caller app control, /// this property returns AppControlLaunchMode.Single launch mode. /// /// /// /// AppControl appControl = new AppControl(); /// appControl.LaunchMode = AppControlLaunchMode.Group; /// /// public AppControlLaunchMode LaunchMode { get { int value = 0; Interop.AppControl.ErrorCode err = Interop.AppControl.GetLaunchMode(_handle, out value); if (err != Interop.AppControl.ErrorCode.None) { Log.Warn(LogTag, "Failed to get the LaunchMode from the AppControl. Err = " + err); } return (AppControlLaunchMode)value; } set { Interop.AppControl.ErrorCode err = Interop.AppControl.SetLaunchMode(_handle, (int)value); if (err != Interop.AppControl.ErrorCode.None) { Log.Warn(LogTag, "Failed to set the LaunchMode to the AppControl. Err = " + err); } } } /// /// Gets the collection of the extra data. /// /// /// Extra data for communication between AppControls. /// /// /// /// AppControl appControl = new AppControl(); /// appControl.ExtraData.Add("key", "value"); /// ... /// /// public ExtraDataCollection ExtraData { get { if (_extraData == null) _extraData = new ExtraDataCollection(_handle); return _extraData; } } #endregion // Public Properties /// /// Retrieves all applications that can be launched to handle the given app_control request. /// /// The AppControl /// ApplicationIds /// Thrown when failed because of invalid parameter /// /// /// IEnumerable applicationIds = AppControl.GetMatchedApplicationIds(control); /// if (applicationIds != null) /// { /// foreach (string id in applicationIds) /// { /// // ... /// } /// } /// /// public static IEnumerable GetMatchedApplicationIds(AppControl control) { if (control == null) { throw new ArgumentNullException("control"); } List ids = new List(); Interop.AppControl.AppMatchedCallback callback = (handle, applicationId, userData) => { if (applicationId == null) { return false; } ids.Add(applicationId); return true; }; Interop.AppControl.ErrorCode err = Interop.AppControl.ForeachAppMatched(control._handle, callback, IntPtr.Zero); if (err != Interop.AppControl.ErrorCode.None) { throw new InvalidOperationException("Failed to get matched application ids. err = " + err); } return ids; } /// /// Sends the launch request. /// /// /// The operation is mandatory information for the launch request. /// If the operation is not specified, AppControlOperations.Default is used by default. /// If the operation is AppControlOperations.Default, the application ID is mandatory to explicitly launch the application. \n /// Since Tizen 2.4, the launch request of the service application over out of packages is restricted by the platform. /// Also, implicit launch requests are NOT delivered to service applications since 2.4. /// To launch a service application, an explicit launch request with application ID given by property ApplicationId MUST be sent. /// /// The AppControl /// Thrown when failed because of a null arguament /// Thrown when failed because of invalid operation /// Thrown when failed because of timeout /// http://tizen.org/privilege/appmanager.launch /// /// /// AppControl appControl = new AppControl(); /// appControl.ApplicationId = "org.tizen.calculator"; /// AppControl.SendLaunchRequest(appControl); /// /// public static void SendLaunchRequest(AppControl launchRequest) { SendLaunchRequest(launchRequest, null); } /// /// Sends the launch request. /// /// /// The operation is mandatory information for the launch request. /// If the operation is not specified, AppControlOperations.Default is used by default. /// If the operation is AppControlOperations.Default, the application ID is mandatory to explicitly launch the application. \n /// Since Tizen 2.4, the launch request of the service application over out of packages is restricted by the platform. /// Also, implicit launch requests are NOT delivered to service applications since 2.4. /// To launch a service application, an explicit launch request with application ID given by property ApplicationId MUST be sent. /// /// The AppControl /// The callback function to be called when the reply is delivered /// Thrown when failed because of arguament is invalid /// Thrown when failed because of invalid operation /// Thrown when failed because of timeout /// http://tizen.org/privilege/appmanager.launch /// /// /// AppControl appControl = new AppControl(); /// appControl.ApplicationId = "org.tizen.calculator"; /// AppControl.SendLaunchRequest(appControl, (launchRequest, replyRequest, result) => { /// // ... /// }); /// /// public static void SendLaunchRequest(AppControl launchRequest, AppControlReplyCallback replyAfterLaunching) { if (launchRequest == null) { throw new ArgumentNullException("launchRequest"); } Interop.AppControl.ErrorCode err; if (replyAfterLaunching != null) { int id = 0; lock (s_replyNativeCallbackMaps) { id = s_replyNativeCallbackId++; s_replyNativeCallbackMaps[id] = (launchRequestHandle, replyRequestHandle, result, userData) => { if (replyAfterLaunching != null) { Log.Debug(LogTag, "Reply Callback is launched"); replyAfterLaunching(new AppControl(launchRequestHandle), new AppControl(replyRequestHandle), (AppControlReplyResult)result); if (result != Interop.AppControl.AppStartedStatus) { lock (s_replyNativeCallbackMaps) { s_replyNativeCallbackMaps.Remove(id); } } } }; } err = Interop.AppControl.SendLaunchRequest(launchRequest._handle, s_replyNativeCallbackMaps[id], IntPtr.Zero); } else { err = Interop.AppControl.SendLaunchRequest(launchRequest._handle, null, IntPtr.Zero); } if (err != Interop.AppControl.ErrorCode.None) { switch (err) { case Interop.AppControl.ErrorCode.InvalidParameter: throw new ArgumentException("Invalid Arguments"); case Interop.AppControl.ErrorCode.TimedOut: throw new TimeoutException("Timed out"); default: throw new InvalidOperationException("Error = " + err); } } } /// /// Sends the terminate request to the application that is launched by AppControl. /// /// /// You are not allowed to terminate other general applications using this API. /// This API can be used to terminate sub-applications which were launched as group mode by caller application. /// Once callee application is being terminated by this API, /// other applications which were launched by callee application as group mode will be terminated as well /// /// The AppControl /// Thrown when failed because of arguament is invalid /// Thrown when failed because of invalid operation /// Thrown when failed because of timeout /// /// /// AppControl terminateRequest = new AppControl(); /// terminateRequest.ApplicationId = "org.tizen.calculator"; /// AppControl.SendTerminateRequest(terminateRequest); /// /// public static void SendTerminateRequest(AppControl terminateRequest) { if (terminateRequest == null) { throw new ArgumentNullException("terminateRequest"); } Interop.AppControl.ErrorCode err; err = Interop.AppControl.SendTerminateRequest(terminateRequest._handle); if (err != Interop.AppControl.ErrorCode.None) { switch (err) { case Interop.AppControl.ErrorCode.InvalidParameter: throw new ArgumentException("Invalid Arguments"); case Interop.AppControl.ErrorCode.TimedOut: throw new TimeoutException("Timed out"); default: throw new InvalidOperationException("Error = " + err); } } } /// /// Class for Extra Data /// public class ExtraDataCollection { private readonly SafeAppControlHandle _handle; internal ExtraDataCollection(SafeAppControlHandle handle) { _handle = handle; } /// /// Adds extra data. /// /// /// The function replaces any existing value for the given key. /// /// The name of the extra data /// The value associated with the given key /// Thrown when key or value is a zero-length string /// Thrown when the application tries to use the same key with system-defined key /// /// /// AppControl appControl = new AppControl(); /// appControl.ExtraData.Add("myKey", "myValue"); /// /// public void Add(string key, string value) { if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException("key"); } if (string.IsNullOrEmpty(value)) { throw new ArgumentNullException("value"); } Interop.AppControl.ErrorCode err = Interop.AppControl.AddExtraData(_handle, key, value); if (err != Interop.AppControl.ErrorCode.None) { switch (err) { case Interop.AppControl.ErrorCode.InvalidParameter: throw new ArgumentException("Invalid parameter: key or value is a zero-length string"); case Interop.AppControl.ErrorCode.KeyRejected: throw new ArgumentException("Key is rejected: the key is system-defined key."); default: throw new InvalidOperationException("Error = " + err); } } } /// /// Adds extra data. /// /// /// The function replaces any existing value for the given key. /// /// The name of the extra data /// The value associated with the given key /// Thrown when key or value is a zero-length string /// Thrown when the application tries to use the same key with system-defined key /// /// /// AppControl appControl = new AppControl(); /// string[] myValues = new string[] { "first", "second", "third" }; /// appControl.ExtraData.Add("myKey", myValues); /// /// public void Add(string key, IEnumerable value) { if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException("key"); } if (value == null) { throw new ArgumentNullException("value"); } string[] valueArray = value.ToArray(); Interop.AppControl.ErrorCode err = Interop.AppControl.AddExtraDataArray(_handle, key, valueArray, valueArray.Length); if (err != Interop.AppControl.ErrorCode.None) { switch (err) { case Interop.AppControl.ErrorCode.InvalidParameter: throw new ArgumentException("Invalid parameter: key or value is a zero-length string"); case Interop.AppControl.ErrorCode.KeyRejected: throw new ArgumentException("Key is rejected: the key is system-defined key."); default: throw new InvalidOperationException("Error = " + err); } } } /// /// Gets the extra data. /// /// Only string and IEnumerable<string> /// The name of extra data /// The value associated with the given key /// Thrown when the key is invalid parameter /// Thrown when the key is not found /// Thrown when the key is rejected /// /// /// AppControl appControl = new AppControl(); /// string myValue = appControl.ExtraData.Get("myKey"); /// /// public T Get(string key) { object ret = Get(key); return (T)ret; } /// /// Gets the extra data. /// /// The name of extra data /// The value associated with the given key /// Thrown when the key is invalid parameter /// Thrown when the key is not found /// Thrown when the key is rejected /// /// /// AppControl appControl = new AppControl(); /// string myValue = appControl.ExtraData.Get("myKey") as string; /// if (myValue != null) /// { /// // ... /// } /// /// public object Get(string key) { if (IsCollection(key)) { return GetDataCollection(key); } else { return GetData(key); } } /// /// Gets all keys in extra data. /// /// The keys in the AppControl /// Thrown when invalid parameter /// /// /// AppControl appControl = new AppControl(); /// IEnumerable keys = appControl.GetKeys(); /// if (keys != null) /// { /// foreach (string key in keys) /// { /// // ... /// } /// } /// /// public IEnumerable GetKeys() { List keys = new List(); Interop.AppControl.ExtraDataCallback callback = (handle, key, userData) => { if (key == null) { return false; } keys.Add(key); return true; }; Interop.AppControl.ErrorCode err = Interop.AppControl.ForeachExtraData(_handle, callback, IntPtr.Zero); if (err != Interop.AppControl.ErrorCode.None) { throw new InvalidOperationException("Failed to get keys. err = " + err); } return keys; } /// /// Tries getting the extra data. /// /// The name of extra data /// The value associated with the given key /// The result whether getting the value is done /// Thrown when the key is invalid parameter /// Thrown when the key is not found /// Thrown when the key is rejected /// /// /// AppControl appControl = new AppControl(); /// string myValue = string.Empty; /// bool result = appControl.ExtraData.TryGet("myKey", out myValue); /// if (result != null) /// { /// // ... /// } /// /// public bool TryGet(string key, out string value) { if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException("key"); } Interop.AppControl.GetExtraData(_handle, key, out value); if (value != null) { return true; } else { value = default(string); return false; } } /// /// Tries getting the extra data. /// /// The name of extra data /// The value associated with the given key /// The result whether getting the value is done /// Thrown when the key is invalid parameter /// Thrown when the key is not found /// Thrown when the key is rejected /// /// /// AppControl appControl = new AppControl(); /// IEnumerable myValue = null; /// bool result = appControl.ExtraData.TryGet("myKey", out myValue); /// if (result) /// { /// foreach (string value in myValue) /// { /// // ... /// } /// } /// /// public bool TryGet(string key, out IEnumerable value) { if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException("key"); } IntPtr valuePtr = IntPtr.Zero; int len = -1; Interop.AppControl.ErrorCode err = Interop.AppControl.GetExtraDataArray(_handle, key, out valuePtr, out len); if (err == Interop.AppControl.ErrorCode.None && valuePtr != IntPtr.Zero) { List stringList = new List(); for (int i = 0; i < len; ++i) { IntPtr charArr = Marshal.ReadIntPtr(valuePtr, IntPtr.Size * i); stringList.Add(Marshal.PtrToStringAnsi(charArr)); Interop.Libc.Free(charArr); } Interop.Libc.Free(valuePtr); value = stringList; return true; } else { value = default(IEnumerable); return false; } } /// /// Removes the extra data. /// /// The name of the extra data /// Thrown when the key is a zero-length string /// Thrown when the key is not found /// Thrown when the key is rejected /// /// /// AppControl appControl = new AppControl(); /// appControl.ExtraData.Remove("myKey"); /// /// public void Remove(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException("key"); } Interop.AppControl.ErrorCode err = Interop.AppControl.RemoveExtraData(_handle, key); if (err != Interop.AppControl.ErrorCode.None) { switch (err) { case Interop.AppControl.ErrorCode.InvalidParameter: throw new ArgumentException("Invalid parameter: key is a zero-length string"); case Interop.AppControl.ErrorCode.KeyNotFound: throw new KeyNotFoundException("Key is not found"); ; case Interop.AppControl.ErrorCode.KeyRejected: throw new ArgumentException("Key is rejected: the key is system-defined key."); default: throw new InvalidOperationException("Error = " + err); } } } /// /// Counts keys in the extra data. /// /// The number of counting keys /// Thrown when invalid parameter /// /// /// AppControl appControl = new AppControl(); /// int numberOfKeys = appControl.ExtraData.Count(); /// /// public int Count() { return GetKeys().Count(); } /// /// Checks whether the extra data associated with the given key is of collection data type. /// /// The name of the extra data /// If true the extra data is of array data type, otherwise false /// Thrown when the key is a zero-length string /// Thrown when failed to check the key /// /// /// AppControl appControl = new AppControl(); /// bool result = appControl.ExtraData.IsCollection("myKey"); /// /// public bool IsCollection(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException("key"); } bool isArray = false; Interop.AppControl.ErrorCode err = Interop.AppControl.IsExtraDataArray(_handle, key, out isArray); if (err != Interop.AppControl.ErrorCode.None) { throw new InvalidOperationException("Error = " + err); } return isArray; } private string GetData(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException("key"); } string value = string.Empty; Interop.AppControl.ErrorCode err = Interop.AppControl.GetExtraData(_handle, key, out value); if (err != Interop.AppControl.ErrorCode.None) { switch (err) { case Interop.AppControl.ErrorCode.InvalidParameter: throw new ArgumentException("Invalid parameter: key is a zero-length string"); case Interop.AppControl.ErrorCode.KeyNotFound: throw new KeyNotFoundException("Key is not found"); ; case Interop.AppControl.ErrorCode.InvalidDataType: throw new ArgumentException("Invalid data type: value is data collection type"); case Interop.AppControl.ErrorCode.KeyRejected: throw new ArgumentException("Key is rejected: the key is system-defined key."); default: throw new InvalidOperationException("Error = " + err); } } return value; } private IEnumerable GetDataCollection(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException("key"); } IntPtr valuePtr = IntPtr.Zero; int len = -1; Interop.AppControl.ErrorCode err = Interop.AppControl.GetExtraDataArray(_handle, key, out valuePtr, out len); if (err != Interop.AppControl.ErrorCode.None) { switch (err) { case Interop.AppControl.ErrorCode.InvalidParameter: throw new ArgumentException("Invalid parameter: key is a zero-length string"); case Interop.AppControl.ErrorCode.KeyNotFound: throw new KeyNotFoundException("Key is not found"); ; case Interop.AppControl.ErrorCode.InvalidDataType: throw new ArgumentException("Invalid data type: value is data collection type"); case Interop.AppControl.ErrorCode.KeyRejected: throw new ArgumentException("Key is rejected: the key is system-defined key."); default: throw new InvalidOperationException("Error = " + err); } } List valueArray = new List(); if (valuePtr != IntPtr.Zero) { for (int i = 0; i < len; ++i) { IntPtr charArr = Marshal.ReadIntPtr(valuePtr, IntPtr.Size * i); valueArray.Add(Marshal.PtrToStringAnsi(charArr)); Interop.Libc.Free(charArr); } Interop.Libc.Free(valuePtr); } return valueArray; } } } }