0

I'm currently trying to make deezer wrapper for native library to work. It works until it tries to play the track : I got a callback saying PLAYLIST_TRACK_NO_RIGHT.

I've tried to read this track with online deezer website and it works well. I also tried with another tracks that works on the deezer website but still doesn't work with the wrapper saying there are no rights to play it.

Does anyone knows why there is this error please ? I think there is something with the Connect method. I saw that ccappSecret property disapeared and instead we have a ProductId, ProductBuildId and anonymousblob that have popped.

So what do we have to write into this ? There is no documentation about that (the only documentation we can found says that we should send the product id into the product id. Ok, but what is this? where do we find this information ?)

Here is the wrapper code :

using System;
using System.Collections;
using System.Runtime.InteropServices;
// make this binding dependent on WPF, but easier to use
using System.Windows.Threading;

// http://www.codeproject.com/Articles/339290/PInvoke-pointer-safety-Replacing-IntPtr-with-unsaf

namespace Deezer
{

    #region Enums

    public enum CONNECT_EVENT_TYPE
    {
        UNKNOWN,                           /**< Connect event has not been set yet, not a valid value. */
        USER_OFFLINE_AVAILABLE,            /**< User logged in, and credentials from offline store are loaded. */

        USER_ACCESS_TOKEN_OK,              /**< (Not available) dz_connect_login_with_email() ok, and access_token is available */
        USER_ACCESS_TOKEN_FAILED,          /**< (Not available) dz_connect_login_with_email() failed */

        USER_LOGIN_OK,                     /**< Login with access_token ok, infos from user available. */
        USER_LOGIN_FAIL_NETWORK_ERROR,     /**< Login with access_token failed because of network condition. */
        USER_LOGIN_FAIL_BAD_CREDENTIALS,   /**< Login with access_token failed because of bad credentials. */
        USER_LOGIN_FAIL_USER_INFO,         /**< Login with access_token failed because of other problem. */
        USER_LOGIN_FAIL_OFFLINE_MODE,      /**< Login with access_token failed because we are in forced offline mode. */

        USER_NEW_OPTIONS,                  /**< User options have just changed. */

        ADVERTISEMENT_START,               /**< A new advertisement needs to be displayed. */
        ADVERTISEMENT_STOP,                /**< An advertisement needs to be stopped. */
    };

    public enum ERRORS
    {
        DZ_ERROR_NO_ERROR = 0x00000000,
        DZ_ERROR_NO_ERROR_ASYNC = 0x00000001,
        DZ_ERROR_ERROR_ARG = 0x00000002,
        DZ_ERROR_ERROR_STATE = 0x00000003,
        DZ_ERROR_NOT_IMPLEMENTED = 0x00000004,
        DZ_ERROR_ASYNC_CANCELED = 0x00000005,

        DZ_ERROR_NOT_ENOUGH_MEMORY,
        DZ_ERROR_OS_ERROR,
        DZ_ERROR_UNSUPPORTED,
        DZ_ERROR_CLASS_NOT_FOUND,
        DZ_ERROR_JSON_PARSING,
        DZ_ERROR_XML_PARSING,
        DZ_ERROR_PARSING,
        DZ_ERROR_CLASS_INSTANTIATION,
        DZ_ERROR_RUNNABLE_ALREADY_STARTED,
        DZ_ERROR_RUNNABLE_NOT_STARTED,
        DZ_ERROR_CACHE_RESOURCE_OPEN_FAILED,
        DZ_ERROR_FS_FULL,
        DZ_ERROR_FILE_EXISTS,
        DZ_ERROR_IO_ERROR,

        DZ_ERROR_CATEGORY_CONNECT = 0x00010000,
        DZ_ERROR_CONNECT_SESSION_LOGIN_FAILED,
        DZ_ERROR_USER_PROFILE_PERM_DENIED,
        DZ_ERROR_CACHE_DIRECTORY_PERM_DENIED,
        DZ_ERROR_CONNECT_SESSION_NOT_ONLINE,
        DZ_ERROR_CONNECT_SESSION_OFFLINE_MODE,
        DZ_ERROR_CONNECT_NO_OFFLINE_CACHE,

        DZ_ERROR_CATEGORY_PLAYER = 0x00020000,
        DZ_ERROR_PLAYER_PLAYLIST_NONE_SET,
        DZ_ERROR_PLAYER_PLAYLIST_BAD_INDEX,
        DZ_ERROR_PLAYER_PLAYLIST_NO_MEDIA,         /**< when trying to access non existing track/radio */
        DZ_ERROR_PLAYER_PLAYLIST_NO_RIGHTS,        /**< when trying to access track/radio with no rights */
        DZ_ERROR_PLAYER_PLAYLIST_RIGHT_TIMEOUT,    /**< when timoeout trying to get rights */
        DZ_ERROR_PLAYER_PLAYLIST_RADIO_TOO_MANY_SKIP,
        DZ_ERROR_PLAYER_PLAYLIST_NO_MORE_TRACK,
        DZ_ERROR_PLAYER_PAUSE_NOT_STARTED,
        DZ_ERROR_PLAYER_PAUSE_ALREADY_PAUSED,
        DZ_ERROR_PLAYER_UNPAUSE_NOT_STARTED,
        DZ_ERROR_PLAYER_UNPAUSE_NOT_PAUSED,
        DZ_ERROR_PLAYER_SEEK_NOT_SEEKABLE_NOT_STARTED,
        DZ_ERROR_PLAYER_SEEK_NOT_SEEKABLE_NO_DURATION,
        DZ_ERROR_PLAYER_SEEK_NOT_SEEKABLE_NOT_INDEXED,
        DZ_ERROR_PLAYER_SEEK_NOT_SEEKABLE,

        DZ_ERROR_CATEGORY_MEDIASTREAMER = 0x00030000,
        DZ_ERROR_MEDIASTREAMER_BAD_URL_SCHEME,
        DZ_ERROR_MEDIASTREAMER_BAD_URL_HOST,
        DZ_ERROR_MEDIASTREAMER_BAD_URL_TRACK,
        DZ_ERROR_MEDIASTREAMER_NOT_AVAILABLE_OFFLINE,
        DZ_ERROR_MEDIASTREAMER_NOT_READABLE,
        DZ_ERROR_MEDIASTREAMER_NO_DURATION,
        DZ_ERROR_MEDIASTREAMER_NOT_INDEXED,
        DZ_ERROR_MEDIASTREAMER_SEEK_NOT_SEEKABLE,
        DZ_ERROR_MEDIASTREAMER_NO_DATA,
        DZ_ERROR_MEDIASTREAMER_END_OF_STREAM,
        DZ_ERROR_MEDIASTREAMER_ALREADY_MAPPED,
        DZ_ERROR_MEDIASTREAMER_NOT_MAPPED,

        DZ_ERROR_CATEGORY_OFFLINE = 0x00040000,
        DZ_ERROR_OFFLINE_FS_FULL,

        DZ_ERROR_PLAYER_BAD_URL,
    };

    public enum PLAYER_COMMANDS
    {
        UNKNOWN,           /**< Player command has not been set yet, not a valid value. */
        START_TRACKLIST,   /**< A new tracklist was loaded and a track played. */
        JUMP_IN_TRACKLIST, /**< The user jump into a new song in the current tracklist. */
        NEXT,              /**< Next button. */
        PREV,              /**< Prev button. */
        DISLIKE,           /**< Dislike button. */
        NATURAL_END,       /**< Natural end. */
        RESUMED_AFTER_ADS, /**< Reload after playing an ads. */
    }

    public enum TRACKLIST_AUTOPLAY_MODE
    {
        MODE_UNKNOWN,

        MANUAL,

        MODE_ONE,

        MODE_ONE_REPEAT,

        MODE_NEXT,

        MODE_NEXT_REPEAT,

        MODE_RANDOM,

        MODE_RANDOM_REPEAT,
    };

    public enum PLAYER_EVENT_TYPE
    {
        UNKNOWN,                             /**< Player event has not been set yet, not a valid value. */

        // Data access related event.
        LIMITATION_FORCED_PAUSE,             /**< Another deezer player session was created elsewhere, the player has entered pause mode. */

        // Track selection related event.
        PLAYLIST_TRACK_NOT_AVAILABLE_OFFLINE,/**< You're offline, the track is not available. */
        PLAYLIST_TRACK_NO_RIGHT,             /**< You don't have the right to render this track. */
        PLAYLIST_TRACK_RIGHTS_AFTER_AUDIOADS,/**< You have right to play it, but you should render an ads first :
                                                  - Use dz_player_event_get_advertisement_infos_json().
                                                  - Play an ad with dz_player_play_audioads().
                                                  - Wait for #DZ_PLAYER_EVENT_RENDER_TRACK_END.
                                                  - Use dz_player_play() with previous track or DZ_PLAYER_PLAY_CMD_RESUMED_AFTER_ADS (to be done even on radios for now).
                                              */
        PLAYLIST_SKIP_NO_RIGHT,              /**< You're on a radio, and you had no right to do skip. */

        PLAYLIST_TRACK_SELECTED,             /**< A track is selected among the ones available on the server, and will be fetched and read. */

        PLAYLIST_NEED_NATURAL_NEXT,          /**< We need a new natural_next action. */

        // Data loading related event.
        MEDIASTREAM_DATA_READY,              /**< Data is ready to be introduced into audio output (first data after a play). */
        MEDIASTREAM_DATA_READY_AFTER_SEEK,   /**< Data is ready to be introduced into audio output (first data after a seek). */

        // Play (audio rendering on output) related event.
        RENDER_TRACK_START_FAILURE,       /**< Error, track is unable to play. */
        RENDER_TRACK_START,               /**< A track has started to play. */
        RENDER_TRACK_END,                 /**< A track has stopped because the stream has ended. */
        RENDER_TRACK_PAUSED,              /**< Currently on paused. */
        RENDER_TRACK_SEEKING,             /**< Waiting for new data on seek. */
        RENDER_TRACK_UNDERFLOW,           /**< Underflow happened whilst playing a track. */
        RENDER_TRACK_RESUMED,             /**< Player resumed play after a underflow or a pause. */
        RENDER_TRACK_REMOVED,             /**< Player stopped playing a track. */
    };

    #endregion

    #region Delegates

    // called with userdata Dispatcher on connect events
    public delegate void ConnectOnEventCb(Connect connect, ConnectEvent connectEvent, DispatcherObject userdata);
    public delegate void PlayerOnEventCb(Player player, PlayerEvent playerEvent, DispatcherObject userdata);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    unsafe public delegate void libcConnectOnEventCb(CONNECT* libcConnect, CONNECT_EVENT* libcConnectEvent, IntPtr userdata);
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    unsafe public delegate bool libcAppCrashDelegate();
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    unsafe public delegate void libcPlayerOnEventCb(PLAYER* libcPlayer, PLAYER_EVENT* libcPlayerEvent, IntPtr userdata);

    #endregion

    #region Structures

unsafe public struct CONNECT_EVENT { };

unsafe public struct UTF8STRING { };

unsafe public struct CONNECT { };

unsafe public struct PLAYER_EVENT { };

unsafe public struct PLAYER { };

#endregion

    #region Imports

#endregion

    // to be in sync with dz_connect_configuration
    [StructLayout(LayoutKind.Sequential)]
    public class ConnectConfig
    {
        public string ccAppId;

        public string product_id;
        public string product_build_id;
        public string anonymousblob;


        //public string ccAppSecret;

        public string ccUserProfilePath;

        public DispatcherObject ccConnectUserdata;
        public ConnectOnEventCb ccConnectEventCb;
    }

    public class ConnectEvent
    {
        internal CONNECT_EVENT_TYPE eventType;

        /* two design strategies:
     * - we could keep a reference to CONNECT_EVENT* with dz_object_retain and call method on the fly
     * - we extract all info in constructor and have pure managed object
     * 
     * here we keep the second option, because we have to have a managed object anyway, and it's 
     * a lot fewer unsafe method to expose, even though it's making a lot of calls in the constructor..
     */
        public unsafe static ConnectEvent newFromLibcEvent(CONNECT_EVENT* libcConnectEventHndl)
    {
        CONNECT_EVENT_TYPE eventType;
        unsafe
        {
            eventType = dz_connect_event_get_type(libcConnectEventHndl);
        }
        switch (eventType)
        {
            case CONNECT_EVENT_TYPE.USER_ACCESS_TOKEN_OK:
                string accessToken;
                unsafe
                {
                    IntPtr libcAccessTokenString = dz_connect_event_get_access_token(libcConnectEventHndl);
                    accessToken = Marshal.PtrToStringAnsi(libcAccessTokenString);
                }
                return new NewAccessTokenConnectEvent(accessToken);
            default:
                return new ConnectEvent(eventType);
        }
    }

        public ConnectEvent(CONNECT_EVENT_TYPE eventType)
    {
        this.eventType = eventType;
    }

        public CONNECT_EVENT_TYPE GetEventType()
    {
        return eventType;
    }

        [DllImport("libdeezer.x64.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe CONNECT_EVENT_TYPE dz_connect_event_get_type(CONNECT_EVENT* dzConnectEvent);

        [DllImport("libdeezer.x64.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe IntPtr dz_connect_event_get_access_token(CONNECT_EVENT* dzConnectEvent);
    }

    public class NewAccessTokenConnectEvent : ConnectEvent
    {
        string accessToken;

        public NewAccessTokenConnectEvent(string accessToken)
        : base(CONNECT_EVENT_TYPE.USER_ACCESS_TOKEN_OK)
    {
        this.accessToken = accessToken;
    }

        public string GetAccessToken()
    {
        return accessToken;
    }
    }

    unsafe public class Connect
    {
        // hash
        static Hashtable refKeeper = new Hashtable();

        internal unsafe CONNECT* libcConnectHndl;
        internal ConnectConfig connectConfig;

        public unsafe Connect(ConnectConfig cc)
        {
            NativeMethods.LoadClass();
            //ConsoleHelper.AllocConsole();
            // attach a console to parent process (launch from cmd.exe)
            //ConsoleHelper.AttachConsole(-1);

            CONNECT_CONFIG libcCc = new CONNECT_CONFIG();

            connectConfig = cc;

            IntPtr intptr = new IntPtr(this.GetHashCode());

            refKeeper[intptr] = this;

            libcCc.ccAppId = cc.ccAppId;
            libcCc.ccAnonymousBlob = cc.anonymousblob;
            //libcCc.ccAppSecret = cc.ccAppSecret;
            libcCc.ccProductBuildId = cc.product_build_id;
            libcCc.ccProductId = cc.product_id;
            libcCc.ccUserProfilePath = UTF8Marshaler.GetInstance(null).MarshalManagedToNative(cc.ccUserProfilePath);
            libcCc.ccConnectEventCb = delegate (CONNECT* libcConnect, CONNECT_EVENT* libcConnectEvent, IntPtr userdata)
            {
                Connect connect = (Connect)refKeeper[userdata];
                ConnectEvent connectEvent = ConnectEvent.newFromLibcEvent(libcConnectEvent);
                DispatcherObject dispather = connect.connectConfig.ccConnectUserdata;

                dispather.Dispatcher.Invoke(connect.connectConfig.ccConnectEventCb, connect, connectEvent, connect.connectConfig.ccConnectUserdata);
            };

            libcConnectHndl = dz_connect_new(libcCc);

            UTF8Marshaler.GetInstance(null).CleanUpNativeData(libcCc.ccUserProfilePath);
        }

        public int Start()
        {
            int ret;
            ret = dz_connect_activate(libcConnectHndl, new IntPtr(this.GetHashCode()));
            return ret;
        }

        public string DeviceId()
    {
        IntPtr libcDeviceId = dz_connect_get_device_id(libcConnectHndl);

        if (libcDeviceId == null)
        {
            return null;
        }

        return Marshal.PtrToStringAnsi(libcDeviceId);
    }

        public int SetAccessToken(string accessToken)
    {
        int ret;
        ret = dz_connect_set_access_token(libcConnectHndl, IntPtr.Zero, IntPtr.Zero, accessToken);
        return ret;
    }

        public int SetSmartCache(string path, int quotaKb)
    {
        int ret;
        ret = dz_connect_cache_path_set(libcConnectHndl, IntPtr.Zero, IntPtr.Zero, path);
        ret = dz_connect_smartcache_quota_set(libcConnectHndl, IntPtr.Zero, IntPtr.Zero, quotaKb);
        return ret;
    }

        [DllImport("libdeezer.x64.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe CONNECT* dz_connect_new(
            [In, MarshalAs(UnmanagedType.LPStruct)]
            CONNECT_CONFIG lpcc);

        [DllImport("libdeezer.x64.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe IntPtr dz_connect_get_device_id(
            CONNECT* dzConnect);

        [DllImport("libdeezer.x64.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe int dz_connect_activate(
            CONNECT* dzConnect, IntPtr userdata);

        [DllImport("libdeezer.x64.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe int dz_connect_set_access_token(
            CONNECT* dzConnect, IntPtr cb, IntPtr userdata, string access_token);

        [DllImport("libdeezer.x64.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe int dz_connect_cache_path_set(
            CONNECT* dzConnect, IntPtr cb, IntPtr userdata,
            [MarshalAs(UnmanagedType.CustomMarshaler,
              MarshalTypeRef=typeof(UTF8Marshaler))]
              string local_path);

        [DllImport("libdeezer.x64.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe int dz_connect_smartcache_quota_set(
            CONNECT* dzConnect, IntPtr cb, IntPtr userdata,
              int quota_kB);
    }

    public class PlayerEvent
    {
        internal PLAYER_EVENT_TYPE eventType;

        /* two design strategies:
     * - we could keep a reference to PLAYER_EVENT* with dz_object_retain and call method on the fly
     * - we extract all info in constructor and have pure managed object
     * 
     * here we keep the second option, because we have to have a managed object anyway, and it's 
     * a lot fewer unsafe method to expose, even though it's making a lot of calls in the constructor..
     */
        public unsafe static PlayerEvent newFromLibcEvent(PLAYER_EVENT* libcPlayerEventHndl)
    {
        PLAYER_EVENT_TYPE eventType;
        unsafe
        {
            eventType = dz_player_event_get_type(libcPlayerEventHndl);
        }
        switch (eventType)
        {
            default:
                return new PlayerEvent(eventType);
        }
    }

        public PlayerEvent(PLAYER_EVENT_TYPE eventType)
        {
            this.eventType = eventType;
        }

        public PLAYER_EVENT_TYPE GetEventType()
    {
        return eventType;
    }

        [DllImport("libdeezer.x64.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe PLAYER_EVENT_TYPE dz_player_event_get_type(PLAYER_EVENT* dzPlayerEvent);
    }

    unsafe public class Player
    {
        // hash
        static Hashtable refKeeper = new Hashtable();

        internal unsafe PLAYER* libcPlayerHndl;
        internal Connect connect;
        internal libcPlayerOnEventCb eventcb;


        public unsafe Player(Connect connect, object observer)
    {
        IntPtr intptr = new IntPtr(this.GetHashCode());

        refKeeper[intptr] = this;

        libcPlayerHndl = dz_player_new(connect.libcConnectHndl);

        this.connect = connect;
    }

        public int Start(PlayerOnEventCb eventcb)
        {
            int ret;
            ret = dz_player_activate(libcPlayerHndl, new IntPtr(this.GetHashCode()));

            this.eventcb = delegate (PLAYER* libcPlayer, PLAYER_EVENT* libcPlayerEvent, IntPtr userdata)
            {
                Player player = (Player)refKeeper[userdata];
                PlayerEvent playerEvent = PlayerEvent.newFromLibcEvent(libcPlayerEvent);
                DispatcherObject dispather = player.connect.connectConfig.ccConnectUserdata;

                dispather.Dispatcher.Invoke(eventcb, player, playerEvent, connect.connectConfig.ccConnectUserdata);
            };

            ret = dz_player_set_event_cb(libcPlayerHndl, this.eventcb);
            return ret;
        }

        public int LoadStream(string url)
        {
            int ret;
            ret = dz_player_load(libcPlayerHndl, IntPtr.Zero, IntPtr.Zero, url);
            return ret;
        }

        public int Play(int idx, PLAYER_COMMANDS cmd)
        {
            int ret;
            ret = dz_player_play(libcPlayerHndl, IntPtr.Zero, IntPtr.Zero, cmd, TRACKLIST_AUTOPLAY_MODE.MANUAL, idx);
            //ret = dz_player_play(libcPlayerHndl, IntPtr.Zero, IntPtr.Zero, cmd, TRACKLIST_AUTOPLAY_MODE.MODE_ONE, idx);
            return ret;
        }

        [DllImport("libdeezer.x64.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe PLAYER* dz_player_new(CONNECT* lpcc);
        //static extern unsafe PLAYER* dz_player_new(CONNECT* lpcc, IntPtr userdata);

        [DllImport("libdeezer.x64.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe int dz_player_set_event_cb(PLAYER* lpcc, libcPlayerOnEventCb cb);

        [DllImport("libdeezer.x64.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe int dz_player_activate(PLAYER* dzPlayer, IntPtr userdata);
        //static extern unsafe int dz_player_activate(PLAYER* dzPlayer, IntPtr userdata);

        [DllImport("libdeezer.x64.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe int dz_player_load(PLAYER* dzPlayer, IntPtr cb, IntPtr userdata, string url);

        [DllImport("libdeezer.x64.dll", CallingConvention = CallingConvention.Cdecl)]
        static extern unsafe int dz_player_play(PLAYER* dzPlayer, IntPtr cb, IntPtr userdata, PLAYER_COMMANDS cmd, TRACKLIST_AUTOPLAY_MODE mode, int idx);
        //static extern unsafe int dz_player_play(PLAYER* dzPlayer, IntPtr cb, IntPtr userdata, int idx, TRACKLIST_AUTOPLAY_MODE mode);
    }



    [StructLayout(LayoutKind.Sequential)]
    public class CONNECT_CONFIG
    {
        public string ccAppId;

        public string ccProductId;
        public string ccProductBuildId;

        public IntPtr ccUserProfilePath;

        public libcConnectOnEventCb ccConnectEventCb;

        public string ccAnonymousBlob;

        public libcAppCrashDelegate ccAppCrashDelegate;

    }




    // trick from http://stackoverflow.com/questions/1573724/cpu-architecture-independent-p-invoke-can-the-dllname-or-path-be-dynamic
// but actually SetDllDirectory works better (for pthread.dll)
    public static class NativeMethods
    {
        // call this to load this class
        public static void LoadClass()
    {
    }

        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetDllDirectory(string lpPathName);

        [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
        static extern IntPtr LoadLibrary(string lpFileName);

        static NativeMethods()
    {
        string arch;
        string basePath = System.IO.Path.GetDirectoryName(typeof(NativeMethods).Assembly.Location);


        if (IntPtr.Size == 4)
            arch = "i386";
        else
            arch = "x86_64";

        System.Diagnostics.Debug.WriteLine("using arch: " + arch);

        SetDllDirectory(System.IO.Path.Combine(basePath, arch));
#if false // can be used to debug library loading
        IntPtr hExe = LoadLibrary("libdeezer.x64.dll");

        if (hExe == IntPtr.Zero)
        {
            Win32Exception ex = new Win32Exception(Marshal.GetLastWin32Error());
            System.Console.WriteLine("exception:" + ex);
            throw ex;
        }
#endif
    }

    }

    // http://stackoverflow.com/questions/10415807/output-console-writeline-from-wpf-windows-applications-to-actual-console
    public class ConsoleHelper
    {
        /// <summary>
    /// Allocates a new console for current process.
    /// </summary>
        [DllImport("kernel32.dll")]
        public static extern Boolean AllocConsole();

        [DllImport("Kernel32.dll")]
        public static extern bool AttachConsole(int processId);

        /// <summary>
    /// Frees the console.
    /// </summary>
        [DllImport("kernel32.dll")]
        public static extern Boolean FreeConsole();
    }

    // http://www.codeproject.com/Articles/138614/Advanced-Topics-in-PInvoke-String-Marshaling
    public class UTF8Marshaler : ICustomMarshaler
    {
        static UTF8Marshaler static_instance;

        // maybe we could play with WideCharToMultiByte too and avoid Marshal.Copy
    // http://stackoverflow.com/questions/537573/how-to-get-intptr-from-byte-in-c-sharp
        /*
        Byte[] byNewData = null;

        iNewDataLen = NativeMethods.WideCharToMultiByte(NativeMethods.CP_UTF8, 0, cc.ccUserProfilePath, -1, null, 0, IntPtr.Zero, IntPtr.Zero);
        Console.WriteLine("iNewDataLen:" + iNewDataLen + " len:" + cc.ccUserProfilePath.Length + " ulen:" + iNewDataLen);
        byNewData = new Byte[iNewDataLen];
        iNewDataLen = NativeMethods.WideCharToMultiByte(NativeMethods.CP_UTF8, 0, cc.ccUserProfilePath, cc.ccUserProfilePath.Length, byNewData, iNewDataLen, IntPtr.Zero, IntPtr.Zero);

        libcCc.ccUserProfilePath = Marshal.UnsafeAddrOfPinnedArrayElement(byNewData, 0);
     */
        public IntPtr MarshalManagedToNative(object managedObj)
    {
        if (managedObj == null)
            return IntPtr.Zero;
        if (!(managedObj is string))
            throw new MarshalDirectiveException(
                   "UTF8Marshaler must be used on a string.");

        // not null terminated
        byte[] strbuf = System.Text.Encoding.UTF8.GetBytes((string)managedObj);
        IntPtr buffer = Marshal.AllocHGlobal(strbuf.Length + 1);
        Marshal.Copy(strbuf, 0, buffer, strbuf.Length);

        // write the terminating null
        Marshal.WriteByte(buffer + strbuf.Length, 0);
        return buffer;
    }
        public unsafe object MarshalNativeToManaged(IntPtr pNativeData)
    {
        byte* walk = (byte*)pNativeData;

        // find the end of the string
        while (*walk != 0)
        {
            walk++;
        }
        int length = (int)(walk - (byte*)pNativeData);

        // should not be null terminated
        byte[] strbuf = new byte[length];
        // skip the trailing null
        Marshal.Copy((IntPtr)pNativeData, strbuf, 0, length);
        string data = System.Text.Encoding.UTF8.GetString(strbuf);
        return data;
    }

        public void CleanUpNativeData(IntPtr pNativeData)
    {
        Marshal.FreeHGlobal(pNativeData);
    }

        public void CleanUpManagedData(object managedObj)
    {
    }

        public int GetNativeDataSize()
    {
        return -1;
    }

        public static ICustomMarshaler GetInstance(string cookie)
    {
        if (static_instance == null)
        {
            return static_instance = new UTF8Marshaler();
        }
        return static_instance;
    }

        [DllImport("kernel32.dll")]
        public static extern int WideCharToMultiByte(uint CodePage, uint dwFlags,
           [MarshalAs(UnmanagedType.LPWStr)] string lpWideCharStr, int cchWideChar,
           [MarshalAs(UnmanagedType.LPArray)] Byte[] lpMultiByteStr, int cbMultiByte, IntPtr lpDefaultChar,
           IntPtr lpUsedDefaultChar);

        public const uint CP_UTF8 = 65001;
    }
}
Steeve
  • 67
  • 1
  • 8
  • Use a sniffer like fiddler and compare website results with application. Usually the issue is the http headers. I would remove the IE history (cookies) between attempts because the cookies are probably effecting the results. – jdweng May 14 '16 at 17:45

2 Answers2

2

i had the same issue

try to first run dz_connect_offline_mode with last parameter as false

IvanR
  • 31
  • 1
  • In your previous sample code (http://stackoverflow.com/questions/37120771/c-sharp-deezer-native-api-adapting-to-c-sharp) i saw you are calling dz_connect_activate after setting the token. I think the correct order is to first call activate and then set token. – IvanR May 17 '16 at 10:52
  • Thanks for your answer but it is still the same problem. Here is a link to my test project : https://onedrive.live.com/redir?resid=9156C1D87978D3B6!1165&authkey=!AE9OBJ-SrEUZNoE&ithint=file%2czip – Steeve May 17 '16 at 12:20
  • 1
    i did these changes and it started to work : -set my app_id and token -added Thread.Sleep(5000); in front of new Player() to let the async login finish (normally you should wait in your callback until the loggin callback tells you login was ok but sleep is a quick workaround) -added dz_connect_offline_mode(libcConnectHndl, IntPtr.Zero, IntPtr.Zero, false); to be called after dz_connect_activate -make sure dConnect.Start() is called before setting token if needed i can send you to sources i have no clue why the force offline mode disable is needed but it works when i add it – IvanR May 17 '16 at 15:46
  • Thank you Ivan !!!! it works !!!!! many many thanks ! it's been months I'm trying to have deezer playing music through code, it's now possible :) And thanks to deezer team to let us doing that :) – Steeve May 17 '16 at 16:03
  • Glad to help. Hopefully Cyrill can check why calling the offline_mode is needed. Maybe its a bug... I also spent like a month tinkering to find out what i was doing wrong :) Sorry i failed formatting the previous reply :( – IvanR May 17 '16 at 16:08
  • No problem Ivan :) I'm happy that works ! Unfortunately, I tried on a WPF application which works pretty well but now I'm trying to implement to my real project that is a UWP C# application and all the code is not supported :/ It seems I will have some other days to search to finally make it work on my true project.... – Steeve May 17 '16 at 19:26
0

Maybe your access_token is not valid anymore? Could you check that the "access_token" you have created has the offline_access enable when calling https://connect.deezer.com/oauth/auth.php?app_id=YOUR_APP_ID&redirect_uri=YOUR_REDIRECT_URI&perms=basic_access,email,offline_access. It should give a more permanent right access. (cf: http://developers.deezer.com/api/oauth and http://developers.deezer.com/api/permissions)

CyrilP
  • 271
  • 1
  • 5
  • doesn't change anything :( What about the fact appSecretId property that disapeared from ConnectConfig and new properties into ConnectConfig like productid and product build id ? I Still don't know how to use them. – Steeve May 17 '16 at 05:03
  • dispatcher is used into the wrapper (that you sent to me at the beginning from our mail conversation). So if I don't give it a dispatcher, I will have a null reference exception. Do you have a wrapper sample somewhere that I could use wihtout this dispatcher ? – Steeve May 17 '16 at 09:51
  • Could you please post some traces for the issue? Thanks. – CyrilP May 17 '16 at 10:29
  • Do you need a trace about null reference exception if I set the dispatcher to null ? or do you need something else ? – Steeve May 17 '16 at 11:49
  • Here is a link to my test project if you want to see where is the problem by testing it. It is a really simple project : https://onedrive.live.com/redir?resid=9156C1D87978D3B6!1165&authkey=!AE9OBJ-SrEUZNoE&ithint=file%2czip – Steeve May 17 '16 at 12:21
  • Cyril, it's ok, it works now (see Ivan solution) Just one question : Do you think I can share source code on github or something like that ? – Steeve May 17 '16 at 16:03
  • Hello Steeve, Yes, it is a good news :) You can share the project on GitHub if you want. Please give me details about it when it is done :) – CyrilP May 19 '16 at 09:24
  • No problem, I will give you details. Just for you information, I will take a moment before I will post this on GitHub, I have many things to do before. So I will post here a comment when it will be done. – Steeve May 22 '16 at 10:38