IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Explorer, utiliser les api du Shell ou bien partir sur du c# pur ?


Sujet :

C#

  1. #1
    Membre éprouvé
    Homme Profil pro
    Administrateur Systèmes, Clouds et Réseaux /CAO/DAO/Ingénierie Electrotechnique
    Inscrit en
    Décembre 2014
    Messages
    454
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Administrateur Systèmes, Clouds et Réseaux /CAO/DAO/Ingénierie Electrotechnique

    Informations forums :
    Inscription : Décembre 2014
    Messages : 454
    Points : 998
    Points
    998
    Par défaut Explorer, utiliser les api du Shell ou bien partir sur du c# pur ?
    Salut à tous,

    (Vous pouvez aller directement à "MA QUESTION" si vous voulez la version courte)

    Dans le cadre de certains de mes programmes je me suis fait une fenêtre explorer qui a des fonctions que filebrowserdialog et cie n'ont pas. J'avais envie de vraiment plus développer ça, et si j'ai le temps un de ces quatre j'ai une application à faire qui aura besoin d'une interface complète de type explorer. L'idée ça va être de s'en servir pour du copier coller et faire évoluer une liste de somme de ces fichiers, contrôler l'intégrité etc... pour garantir les transferts. personnellement ça m'est déjà arrivé d'avoir des fichiers dont la somme md5 avait changé en cours de route, et le contenu inutilisable. Donc actuellement j'ia déjà un petit programme qui surveille certains dossiers et calcule automatiquement les sommes, il est sous protobuff pour être interrogeable de n'importe où vu qu'il tourne sur un nas "maison". Cet explorer va aussi dialoguer avec lui. Bref ça c'est le projet si j'ai le temps.

    Là j'ai besoin de poser ma question en Français, je ne trouve pas certains mots dédiés en Anglais, puis StackOverFlow par exemple me sort un peu par les trous de nez, 90% des réponses ne vont pas répondre directement et vont faire totalement dévier le sujet. Du style "han mais t'as pas mis de code", s'ils veulent prendre 1 000 lignes de code dans les gencives je ne vois pas trop ce que ça va leur rapporter, et quand à mon taff il est en libre accès sur github, donc c'st pas comme si je faisais le rat à prendre des conseils dans redonner à la communauté. Après je ne suis qu'un amateur... (désolé pour la parenthèse, ils m'ont encore saoulé hier, 1 jour et demi de perdu, tu poses une question las de chercher à blanc.... 0 réponse en rapport, on te demande de poster ton code qui est une boucle sur la Même fonction, que tu as déjà cité... ben c'est une boucle les gars, voilà ça commence à 0 puis ça finit à X, une boucle quoi...). J'avais posté des screenshots du résultats, ce qui était plus parlant, mais faut croire que certains adorent placer "mets ton code" à chaque fois.

    Bon désolé, en tout ça fait 3 jours pour rien, ou peu du moins.

    MA QUESTION: (enfin plusieurs)
    - Pour faire un explorer de fichiers en 2021, y 'a t'il un intérêt à utiliser les API de win32.dll (enfin à part une mais je vais y revenir) ? (Ne me parlez pas de l'exemple donné par Microsoft, je sais déjà le faire, mieux en fait)
    - Existe t'il une solution pour récupérer les icones d'un dossier en passant par autre chose que win32.dll (par exemple pour one drive, mega, voire si possible la personnalisation qu'on fait nous mêmes de certains dossiers) ? (extract fonctionne pour les fichiers)
    - Est ce qu'un couple "je lis le registre windows" + "les fonctions déjà intégrées à c#" est aussi intéressant (comparaison si possible) que d'utiliser le Shell ? (Ou c'est peut être pareil).
    - Je cherche à faire une solution la plus évolutive possible, enfin de préférence, qui ne nécessite pas des mises à jour pour intégrer tel ou tel programme qui rajoute un élément dans le panneau de navigation (voir screenshot).
    - Actuellement je peux récupérer les dossiers utilisés par Mega via le registre, existe t'il une autre solution ? Est ce que c'est ce qu'on fait en général dans ces cas là ? (J'ai développé jusque là des trucs qui n'en ont pas besoin).

    Ce que j'envisage de faire c'est déjà d'avoir les mêmes éléments que dans le panneau navigation de l'explorer, avec les icones par défaut. J'ai trouvé pour l'accès rapide apparemment c'est un knownfolder donc ça sera pas dur, je me disais en fait qu'il existait peut être quelque chose d'autre que de lire dans la base de registre, après s'il n'y a pas et que c'est bien de passer par elle, je continuerai dans cette voie là. J'avoue avoir pas mal de soucis avec les API windows, ça va un peu mieux depuis ce matin mais il y a tellement de vieux code qui date de début 2000, j'ai du apprendre en vitesse express ce qui était deprecated etc... Disons que si c# fait la même chose et que ce n'est pas plus lent, ça règle le soucis. Après j'ai noté que mes HDD externes doivent démarrer parfois, et la fenêtre explorer de windows, semble ne pas avoir ce soucis.... Alors que pour la mienne, il semble démarrer au moment où ça va s'afficher, et même en asynchrone j'ai parfois une attente.

    Nom : 2021-05-20_01h36_34.png
Affichages : 158
Taille : 5,1 Ko


    au cas où si ça peut servir le code pour avoir les chemins de Mega (de l'utilisateur actuel).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
         var identity = System.Security.Principal.WindowsIdentity.GetCurrent();
                string userName = identity.Name.ToString();
                string userSID = identity.User.ToString();
                List<string> MegaFolderPaths = new List<string>();
                List<string> KnownFoldersIDExt = new List<string>();
     
                RegistryKey clsidKey = Registry.Users.OpenSubKey($"{userSID}\\Software\\Classes\\CLSID");
     
                //Get all the sub keys it contains
                foreach (string subKey in clsidKey.GetSubKeyNames())
                {
                    RegistryKey clsidSubKey = Registry.Users.OpenSubKey($"{userSID}\\Software\\Classes\\CLSID\\{subKey}\\Instance\\InitPropertyBag");
     
                    if (clsidSubKey == null)
                        continue;
     
                    string defaultValue = (string)clsidSubKey.GetValue("TargetFolderPath");
     
                    if (!string.IsNullOrEmpty(defaultValue))
                    {
                        MegaFolderPaths.Add(defaultValue);
                        KnownFoldersIDExt.Add(subKey);
     
                    }
                }
    // --- Edit 21/05/2021 J'ai cru voir des réponses mais en fait non...

    Bon avec un test via l'api, ayant finalement réussi à le faire, j'obtiens 90ms pour faire ma racine complète, contre 102ms en listant les disques durs et en extrayant les clés de la base de registre.
    Sachant en passant donc que l'option 2 c'est sans extraire les icones, ayant fait un module pour les charger à part, quand la fenêtre wpf se charge et qu'elle demande l'image chaque objet renvoie vers un manager qui extrait l'icone (afin de pouvoir changer le set d'icones à la volée). Donc même si c'est fluide, ça devrait forcément alourdir.

  2. #2
    Membre éprouvé
    Homme Profil pro
    Administrateur Systèmes, Clouds et Réseaux /CAO/DAO/Ingénierie Electrotechnique
    Inscrit en
    Décembre 2014
    Messages
    454
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Administrateur Systèmes, Clouds et Réseaux /CAO/DAO/Ingénierie Electrotechnique

    Informations forums :
    Inscription : Décembre 2014
    Messages : 454
    Points : 998
    Points
    998
    Par défaut
    Bon, j'ai pu avancer aujourd'hui, vu la galère pas possible pour réussir à trouver des informations je me suis dit que ça serait pas mal que ça reste en ligne quelque part pour les autres.
    Donc là c'est le fruit de la recherche entre le net (des centaines de messages), les api windows en ligne, retrouver des informations sur http://pinvoke.net/ et finir par lire le blog de "Raymond Chen" https://devblogs.microsoft.com/oldne...26-00/?p=44833

    C'est moins compliqué si on continue à commencer à faire sa racine avec "int hRes = ShellAPI.SHGetDesktopFolder(out pointeur);" et ça j'y reviendrai plus tard en faisant une exemple simple avec l'ancienne manière de faire.

    Dans cet exemple on va directement récupérer le Dossier connu (KnownFolderID) "Mon PC"... si j'ai bien compris le "nouveau système" (par rapport à tout ce que j'ai pu lire qui date quand même de 2003) qui est venu avec vista et plus (si je me souviens bien) s'articule donc sur des ID de dossier (on peut en lancer même certains avec win+R dans la fenêtre taper ::{ID}). Bref c'était fabuleux à lire mais pour trouver des informations claires et simples, surtout quand on est en c# c'est chaud. J'ai eu codé en c++; Ca fait un moment que je suis plus sur c# mais passer du c++ sur du c# par moi même je n'avais jamais fait, là j'ai eu quelques fois à le faire car je ne trouvais pas la transcription en c#.. apparemment c'est bon. Mais fiuu...

    edit: finalement j'ai fait un petit programme, le plus simple possible pour illustrer le tout, disponible ici: https://github.com/daerlnaxe/Use-API...-w7-/tree/mainUseAPIForKnownFolder.zip
    + copie au cas où : UseAPIForKnownFolder.zip



    - Step 1
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
     
                IShellItem spsiFolder;
                int res = ShellAPI.SHGetKnownFolderItem(
                            new Guid(KnownFolderID.ComputerFolder),
                            (uint)KnownFolderFlags.None,
                            IntPtr.Zero,
                            typeof(IShellItem).GUID,
                            out spsiFolder);
    La méthode à appeler (je vous invite à aller voir la page de windows sur les API car le descriptif des paramètres est trop long pour que je le copie ici. Pour ma part je sépare dans un fichier dédié, libre à vous de tout mettre dans le même.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
            [DllImport("Shell32.dll", ExactSpelling = true, SetLastError = false)]
            public static extern int SHGetKnownFolderItem(
                [In, MarshalAs(UnmanagedType.LPStruct)] Guid rfid,
                uint dwFlags,
                IntPtr hToken,
                [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
                out IShellItem ppv);
    IShellItem :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
     [ComImport]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        [Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
        public interface IShellItem
        {
            HResult BindToHandler(IntPtr pbc,
                        [In, MarshalAs(UnmanagedType.LPStruct)] Guid bhid,
                        [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
                        out IntPtr ppv);
     
     
            void GetParent(out IShellItem ppsi);
     
            HResult GetDisplayName(SIGDN sigdnName, out IntPtr ppszName);
     
            void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
     
            [PreserveSig]
            int Compare(IShellItem psi, uint hint, out int piOrder);
        }
    HResult
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
        public enum HResult : int
        {
            S_OK = 0,
            S_FALSE = 1,
            E_NOINTERFACE = unchecked((int)0x80004002),
            E_NOTIMPL = unchecked((int)0x80004001),
            E_FAIL = unchecked((int)0x80004005)
        }
    - On passe donc la seconde maintenant:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
      IntPtr pEnum;
      var reslt = spsiFolder.BindToHandler(IntPtr.Zero, new Guid(BHID.EnumItems),
                                    typeof(IEnumShellItems).GUID, out pEnum);
    Ici il faut la clé EnumItems, il y en en fait un sacré lot ICI
    Personnellement j'ai fait une grosse copie mise en forme sous enum, celle qui nous intéresse sera:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
            /// <summary>
            /// Introduced in Windows Vista: If the item is a folder, gets an IEnumShellItems object
            /// that enumerates all items in the folder. This includes folders, nonfolders, and hidden
            /// items.
            /// </summary>
            public const string EnumItems = "{94f60519-2850-4924-aa5a-d15e84868039}";
    La classe IEnumShellItems

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
     [ComImport]
        [Guid("70629033-e363-4a28-a567-0db78006e6d7")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IEnumShellItems
        {
     
            HResult Next(uint celt, out IShellItem rgelt, out uint pceltFetched);
     
            HResult Skip(uint celt);
     
            HResult Reset();
     
            HResult Clone(out IEnumShellItems ppenum);
        }


    - Etape 3:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
     
     
                if (reslt == HResult.S_OK)
                {
                    List<IShellItem> children = new List<IShellItem>();
                    IntPtr pszName = IntPtr.Zero;
                    IntPtr pszPath = IntPtr.Zero;
                    IEnumShellItems pEnumShellItems = null;
                    pEnumShellItems = Marshal.GetObjectForIUnknown(pEnum) as IEnumShellItems;
     
     
                    IShellItem psi = null;
                    uint nFetched = 0;
     
                    // 
                    while (HResult.S_OK == pEnumShellItems.Next(1, out psi, out nFetched) && nFetched == 1)
                    {
                        pszName = pszPath = IntPtr.Zero;
     
                        children.Add(psi);
     
                        reslt = psi.GetDisplayName(SIGDN.NORMALDISPLAY, out pszName);
                        reslt = psi.GetDisplayName(SIGDN.DESKTOPABSOLUTEEDITING, out pszPath);
     
                        if (reslt == HResult.S_OK)
                        {
                            // récupère les valeurs
                            string sDisplayName = Marshal.PtrToStringUni(pszName);
                            string sDisplayPath = Marshal.PtrToStringUni(pszPath); ;
     
                            // Juste pour afficher
                            Debug.WriteLine(string.Format("\tItem Name : {0}", sDisplayName));
                            Debug.WriteLine(string.Format("\tItem Path : {0}", sDisplayPath));
     
     
                            // On libère
                            Marshal.FreeCoTaskMem(pszName);
                            Marshal.FreeCoTaskMem(pszPath);
                        }
                    }
                }
    Les valeurs SIGDN:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
     
     public enum SIGDN : uint
        {
            /// <summary>
            /// Returns the display name relative to the parent folder.
            /// In UI this name is generally ideal for display to the user.
            /// </summary>
            NORMALDISPLAY = 0x00000000,
            /// <summary>
            /// Returns the parsing name relative to the parent folder. 
            /// This name is not suitable for use in UI.
            /// </summary>
            PARENTRELATIVEPARSING = 0x80018001,
            /// <summary>
            /// Returns the parsing name relative to the desktop. This name is not suitable for use in UI.
            /// </summary>
            DESKTOPABSOLUTEPARSING = 0x80028000,
            /// <summary>
            /// Returns the editing name relative to the parent folder. In UI this name is suitable for display
            /// to the user.
            /// </summary>
            PARENTRELATIVEEDITING = 0x80031001,
            /// <summary>
            /// Returns the editing name relative to the desktop. In UI this name is suitable for
            /// display to the user.
            /// </summary>
            DESKTOPABSOLUTEEDITING = 0x8004c000,
            /// <summary>
            ///  Returns the item's file system path, if it has one. Only items that report SFGAO_FILESYSTEM
            ///  have a file system path. When an item does not have a file system path, a call
            ///  to IShellItem::GetDisplayName on that item will fail. In UI this name is suitable for display
            ///  to the user in some cases, but note that it might not be specified for all items.
            /// </summary>
            FILESYSPATH = 0x80058000,
            /// <summary>
            ///  Returns the item's URL, if it has one. Some items do not have a URL, and in those cases
            ///  a call to IShellItem::GetDisplayName will fail. This name is suitable for display to the 
            ///  user in some cases, but note that it might not be specified for all items.
            /// </summary>
            URL = 0x80068000,
            /// <summary>
            /// Returns the path relative to the parent folder in a friendly format as displayed in an
            /// address bar. This name is suitable for display to the user.
            /// </summary>
            PARENTRELATIVEFORADDRESSBAR = 0x8007c001,
            /// <summary>
            /// Returns the path relative to the parent folder.
            /// </summary>
            PARENTRELATIVE = 0x80080001,
            /// <summary>
            /// Introduced in Windows 8.
            /// </summary>
            PARENTRELATIVEFORUI = 0x80094001
        }

  3. #3
    Membre éprouvé
    Homme Profil pro
    Administrateur Systèmes, Clouds et Réseaux /CAO/DAO/Ingénierie Electrotechnique
    Inscrit en
    Décembre 2014
    Messages
    454
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Administrateur Systèmes, Clouds et Réseaux /CAO/DAO/Ingénierie Electrotechnique

    Informations forums :
    Inscription : Décembre 2014
    Messages : 454
    Points : 998
    Points
    998
    Par défaut
    Après avoir testé plusieurs pistes dont le WMI, lancé d'ailleurs des tasks pour récupérer séparément les drives logiques par c# les clouds par le registre (très rapide) et enfin parcouru via le WMI pour chopper les disques qui étaient en usb, je suis revenu sur du c++ après être tombé sur plusieurs articles de Raymond Chen, j'ai fait la liaison entre eux. Ca faisait un bail que je n'avais pas vu de c++ donc franchement grosse galère pour me replonger dedans, j'espère ne rien avoir oublié concernant la gestion de la mémoire ou les pointeurs.

    Donc avec ce code en partant simplement d'un path, ou mieux de la racine on arrive à savoir si en bout on a un lecteur usb ou autre. Je livre le code si ça peut servir à d'autres, parce que franchement les informations à ce sujet sont très difficiles à trouver, du moins pour un mec comme moi dont ce n'est pas le métier, qui n'a donc pas eu un seul morceau de formation en fac (juste lu quelques bouquins) et apprend divers langages sur le tas. J'ai compris d'ailleurs plusieurs choses qui même si je les connais indépendamment pour faire du dépannage hardware et du montage de pc (y compris en entreprise, j'ai eu la chance en multitechnique de pouvoir avoir deux fois ça dans mes fonctions mais pareil pas de diplôme)... Partir du début et remonter la chaine, ça passe par les partitions, c'est mine de rien pas évident car avec le JBOD etc ça peut compliquer les choses...

    Savoir l'interface d'un disque via un path en c++
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
     
    	LPCWSTR filePath = L"H:";
    	HANDLE volumeHandle = NULL;
    	HANDLE driveHandle = NULL;
     
     
    	// Récupère le nom du volume
    	TCHAR volumePath[MAX_PATH]; // for expository purposes
    	bool res = GetVolumePathName(filePath, volumePath, ARRAYSIZE(volumePath));
    	wprintf(L"%s \n", volumePath);
     
    	/*
    		Récupère le GUID (ne marche pas si c'est pas un disque dur local) \\?\Volume{guid}\
    	*/
    	TCHAR volumeName[MAX_PATH]; // for expository purposes
    	GetVolumeNameForVolumeMountPoint(volumePath, volumeName, ARRAYSIZE(volumeName));
     
    	wprintf(L"%s \n", volumeName);
     
    	/*
    		If you pass that path to the CreateFile function with the trailing backslash intact, then you are opening a handle to the root
    		directory of the volume.
    		If you pass that path to the CreateFile function with the trailing backslash removed, then you are opening a handle
    		to the volume itself.
    	*/
     
    	// In our case, we want to open a handle to the volume itself, so we need to remove that trailing backslash.The call to CreateFile looks like this:
    	CString test = volumeName;
    	test.TrimRight('\\');
     
    	LPCWSTR volumeNameWithoutTrailingBackslash = test;
     
    	/// Handle sur le volume (en retirant le dernier \)
    	volumeHandle = CreateFile(volumeNameWithoutTrailingBackslash,
    		0, /* no special access requested */
    		FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    		nullptr, /* no custom security */
    		OPEN_EXISTING,
    		FILE_FLAG_BACKUP_SEMANTICS,
    		nullptr); /* template */
     
    	// --- Récupération du disque dur physique contenant le volume
    	/*
    		On demande au volume ses disques étendus, en cas de volume installé sur plusieurs disques, la première fois va dire la taille de la
    		structure dont on a besoin, et la seconde donnera la structure.
    		Puisque la plupart des volumes n'ont qu'une bande (voir raid), on peut optimiser légèrement pour ce cas par passer un buffer initial
    		assez gros pour contenir une seule bande. Si ça fonctionne, c'est qu'il n'y a pas besoin d'essayer une seconde fois.
    		(Raymond Chen)
    	*/
     
    	VOLUME_DISK_EXTENTS* extents = nullptr;
     
    	// Anticipate common case where there is only one extent.
    	VOLUME_DISK_EXTENTS singleExtent;
     
    	// But also have a place to manage allocated data.
    	std::unique_ptr<BYTE[]> lifetime;
     
    	DWORD bytesWritten;
    	if (DeviceIoControl(volumeHandle, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
    		nullptr, 0,
    		&singleExtent, sizeof(singleExtent),
    		&bytesWritten,
    		nullptr))
    	{
    		// Worked on the first try. Use the preallocated buffer.
    		extents = &singleExtent;
    	}
    	else
    	{
    		VOLUME_DISK_EXTENTS* lastQuery = &singleExtent;
    		while (GetLastError() == ERROR_MORE_DATA)
    		{
    			// Evaluation de la requête
    			assert(RTL_CONTAINS_FIELD(lastQuery, bytesWritten, NumberOfDiskExtents));
     
    			DWORD extentCount = lastQuery->NumberOfDiskExtents;
    			DWORD allocatedSize = FIELD_OFFSET(VOLUME_DISK_EXTENTS, Extents[extentCount]);
    			lifetime.reset(new BYTE[allocatedSize]);
    			lastQuery = (VOLUME_DISK_EXTENTS*)lifetime.get();
    			if (DeviceIoControl(volumeHandle, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
    				nullptr, 0,
    				lastQuery, allocatedSize,
    				&bytesWritten,
    				nullptr)) {
    				extents = lastQuery;
    				break;
    			}
    		}
    	}
     
    	/*
    		The extents tell you which physical drives the volume draws its storage from, and which bytes on those physical drives are devoted
    		to the volume. But for this exercise, we just want the physical drives.
    	 */
    	if (extents)
    	{
    		// process the extents
    		int physicalDriveNumber = extents->Extents->DiskNumber;
     
    		/*
    			Once you have the physical drive numbers, you can convert them to physical drive handles by building a path of the form
    			\\.\PhysicalDrive# where the # is the decimal expansion of the drive number.
    		*/
     
    		wchar_t physicalDrivePath[80];
    		swprintf_s(physicalDrivePath, L"\\\\.\\PhysicalDrive%d", physicalDriveNumber);
    		driveHandle = CreateFile(physicalDrivePath,
    			0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    			nullptr, OPEN_EXISTING, 0, nullptr);
     
     
    		STORAGE_PROPERTY_QUERY query{};
    		query.PropertyId = StorageAdapterProperty;
    		query.QueryType = PropertyStandardQuery;
    		DWORD bytesWritten;
    		STORAGE_ADAPTER_DESCRIPTOR result{};
     
    		if (DeviceIoControl(driveHandle, IOCTL_STORAGE_QUERY_PROPERTY,
    			&query, sizeof(query),
    			&result, sizeof(result),
    			&bytesWritten, nullptr))
    		{
    			// type 7 = usb
    			printf("Interface type: %d", result.BusType);
     
    		}
    	}
     
    	CloseHandle(volumeHandle);
    	CloseHandle(driveHandle);

    - Lister les disques durs de type usb en wmi via c# (je suis en rade d'inspiration pour les noms de variable), le wmi est pratique mais j'ai noté que le temps de réponse pouvait être très aléatoire. D'où le fait que je me sois de nouveau tourné vers c++. J'ai pu également faire l'inverse, partir du setup en c++ et définir quels disques étaient en usb, je me suis pas encore occupé de faire la liaison avec les disques logiques.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
     
      internal static List<ManagementBaseObject> GetFloatingDisks()
            {
     
                List<ManagementBaseObject> usbStocks = new List<ManagementBaseObject>();
                foreach (var mbObj in new ManagementObjectSearcher($"Select Caption, DeviceID, InterfaceType  FROM  Win32_DiskDrive" +
                                       $" Where {Win32_DiskDriveCol.InterfaceType}='{Win32_DD_Value.USB}'").Get())
                {
                    var objets = PhysicToLogical(mbObj);
     
                    foreach (var obj in objets)
                    {
                        usbStocks.Add(obj);
     
                    }
     
                }
     
     
                return usbStocks;
            }
     
         public static IEnumerable<ManagementBaseObject> PhysicToLogical(ManagementBaseObject mbObj)
            {
                List<ManagementBaseObject> objects = new List<ManagementBaseObject>();
                foreach (ManagementBaseObject wmiDiskPartition in new ManagementObjectSearcher
                    (
                        @"ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + mbObj["DeviceID"] + "'} " +
                        "WHERE AssocClass = Win32_DiskDriveToDiskPartition"
                    ).Get())
                {
     
     
                    foreach (ManagementBaseObject saisplus in new ManagementObjectSearcher
                        (
                            "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" + wmiDiskPartition["DeviceID"] + "'} " +
                            "WHERE AssocClass = Win32_LogicalDiskToPartition"
                         ).Get())
                    {
                        objects.Add(saisplus);
                    }
     
     
                }
     
                return objects;
            }

    Note:
    DriveType avec c# est totalement faux, un disque usb est considéré comme "fixed", donc merci de ne JAMAIS me parler de cette option ^^ . Il en est de même au final même avec la version c++ c'est d'ailleurs clairement expliqué dans le jeu d'api Windows, ils expliquent qu'il faut passer par SetupDiGetDeviceRegistryProperty. Donc en fait sur plus d'une centaine de messages de "solutions" qui ont été apportées aux gens qui s'interrogeaient j'ai lu plus de 90% de réponses fausses sur toute la toile. C'est ce qui m'a amené à persévérer et puis je me suis dit que ramener tout ici serait utile aux autres, surtout pour les anglophobes (StackOverFlow surtout, c'est pas que j'ai une dent contre eux mais beaucoup de faux). Il me reste une dernière piste à explorer, donc j'ai pas mal abattu de boulot, partir du setup pour arriver en bout à la lettre de lecteur, j'ai déjà pu identifier tous les disques en usb, je dois faire les liaisons avec les partitions puis enfin avec les lettres associées...

    Bref ce qu'il faut retenir c'est qu'au niveau des API c'est géré (hélas à mon gout) comme ça l'est physiquement, disques durs/volumes/lettres... c'est sûr que ça permet beaucoup de souplesses, mais c'est très fastidieux quand on veut programmer avec je trouve... D'ailleurs en passant en fait j'invite à regarder explorer... le volet de gauche. Sous "my pc" vous avez vos disques durs fixes ET les disques durs usb, puis après "my pc" vous avez de nouveau les disques usb. Une redondance qui justement explique les raisons pour lesquelles j'ai cherché tout ça, voulant bien séparer ce qui est usb du reste.


    Edit: Etant autodidacte il y a probablement beaucoup de choses que je ne sais pas, je connaissais même pas WMI jusqu'hier, je vois que maintenant c'est CIM pour être multiplateforme mais CIM parait il ne donnerait pas autant d'infos selon des bloggeurs, sachant donc que selon le site de MS WMI hérite de CIM (mais peut être justement que CIM est plus multiplateforme et WMI va étendre en rajoutant des infos), je n'ai pas vu d'implantation CIM Pour c# de toute manière. Bref je trouve l'environnement très complexe, plus complexe que je m'attendais... y'a RT après... C'est possible que j'interprète mal des choses, que je ne connaisse pas certaines autres, donc toute critique objective est bienvenue (me taillez pas svp ). A la limite mes failles seront représentatives de ce qu'un développeur amateur peut rencontrer, et je ne sais pas si c'est moi qui ait dormi des années (pas mal occupé par mon taff puis ma maladie) mais j'ai l'impression qu'il y a 20 ans les choses étaient plus accessibles tout en disposant de moins d'outils. Là j'ai la désagréable sensation qu'on se retrouve cantonnés à devoir utiliser ce qu'on veut bien laisser, bon c'est sûr que l'os est devenu beaucoup plus compliqué, de nouvelles choses ont émergé aussi.... Je ne suis pas fan de me retrouver à installer tel ou tel packet tiers sans savoir faire par moi même. Qu'en passant aussi l'information se perd aussi du coup, enfin elle me parait moins accessible qu'avant. Moins de sites, ou du moins google en ramène moins, pire pour duck duck go... des posts qui ont 15/20 ans.... Enfin bref open pour discuter, élargir mes horizons même selon, apprendre plus.

    Edit2:
    ayant soumis mon code sur CodeProject pour demander également là bas s'il y pouvait y avoir un soucis avec ce code on m'a fait remarquer quelque chose de juste concernant l'agrégation par bande, et que mon code ne prenait pas en compte donc j'ai modifié.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
    if (extents)
    	{
    		vector<int> interfaces;
    		for (int i = 0; i < extents->NumberOfDiskExtents; i++)
    		{
    			// process the extents
    			int physicalDriveNumber = extents->Extents->DiskNumber;
     
    			/*
    				Once you have the physical drive numbers, you can convert them to physical drive  
                    handles by building a path of the form
    				\\.\PhysicalDrive# where the # is the decimal expansion of the drive number.
    			*/
     
    			wchar_t physicalDrivePath[80];
    			swprintf_s(physicalDrivePath, L"\\\\.\\PhysicalDrive%d", physicalDriveNumber);
    			driveHandle = CreateFile(physicalDrivePath,
    				0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
    				nullptr, OPEN_EXISTING, 0, nullptr);
     
    			interfaces.push_back( GetInterfaceType(driveHandle));
    		}
     
    		return interfaces;
    	}
    On obtient donc une "liste" de valeurs numériques, leurs valeurs sont consultables pour faire une gestion derrière par énumération ou peu importe, à consulter ici si j'ai oublié de la mettre et au cas où un jour ça disparaisse...

    Nom : screencapture-docs-microsoft-en-us-windows-hardware-drivers-ddi-ntddstor-ne-ntddstor-storage-bus.png
Affichages : 109
Taille : 581,3 Ko

  4. #4
    Membre éprouvé
    Homme Profil pro
    Administrateur Systèmes, Clouds et Réseaux /CAO/DAO/Ingénierie Electrotechnique
    Inscrit en
    Décembre 2014
    Messages
    454
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Administrateur Systèmes, Clouds et Réseaux /CAO/DAO/Ingénierie Electrotechnique

    Informations forums :
    Inscription : Décembre 2014
    Messages : 454
    Points : 998
    Points
    998
    Par défaut
    Bon ben flûte je ne voulais pas up mais juste éditer.

    Juste pour dire si un jour quelqu'un se demande j'ai porté le code sous c++/cli j'obtiens l'état des disques, les interfaces de chaque disque logique, en 9 ms. Comparaison WMI entre 153ms jusqu'à 8s !!! Sachant donc que j'ai 4 disques usb dans le lot et un réseau. Et avec les instructions c# on a la déségréable surprise d'avoir parfois 3-4 s de délais également si on veut savoir le type des disques, je pense que la différence peut facilement s'expliquer par le fait de voir si on veut récupérer le directoryinfo ou pas qui oblige à récupérer des informations sur la taille du disque, son espace disponible etc... je me dis cela car j'avais eu la même chose quand je faisais de la surveillance de fichiers, au début mon code était tout bonnement génial, ça tournait du feu de Dieu ... Parce que je le faisais sur des petits fichiers. Ce code à l'époque récupérait aussi la taille, (je sais qu'il existe file system watcher) mais j'arrivais de Python sous linux avec le GUID direct, bon maintenant on l'a sur les disques en GPT.... Et quand j'ai pris des fichiers réels jusqu'à plusieurs GO j'ai compris ma douleur, en effet j'utilisais une API de windows qui récupérait AUSSI la taille, et immédiatement ça a allongé le temps de récupération des infos. C'est pour ça que là je m'interrogeais donc sur comment récupérer autrement, juste les informations que l'on désire, et le plus vite possible.

Discussions similaires

  1. Utiliser les APIs Orange dans Windev
    Par Orange Api Team dans le forum WinDev
    Réponses: 1
    Dernier message: 15/04/2010, 17h06
  2. utiliser les API d'eclipse
    Par Pascale38 dans le forum Eclipse Platform
    Réponses: 0
    Dernier message: 18/08/2008, 11h00
  3. comment utiliser les API avec Perl?
    Par megapacman dans le forum Langage
    Réponses: 5
    Dernier message: 23/08/2006, 15h18
  4. Utiliser les commandes du shell bash
    Par man in the hill dans le forum Linux
    Réponses: 3
    Dernier message: 20/07/2006, 10h43
  5. []Recuperer le nom d'un contact en utilisant les API msn ?
    Par trotter dans le forum VB 6 et antérieur
    Réponses: 4
    Dernier message: 09/10/2005, 22h07

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo