Goldorak44 Posté(e) le 25 mars 2014 Posté(e) le 25 mars 2014 Bonjour, je suis en train de créer une application en c# qui va chercher des informations dans un dwg choisi par l'utilisateur.A priori, il ne semble pas possible de récupérer les données d'un fichier dwg sans lancer Autocad (si quelqu'un a une astuce, je suis preneur !! ). J'utilise donc ce code pour lancer le processus Autocad avec le plan choisi par l'utilisateur : Process acadProcess = new Process(); acadProcess.StartInfo.Arguments = string.Format("/nologo /p \"{0}\"", profileName); acadProcess.StartInfo.FileName = (@dialog.FileName); acadProcess.Start(); acadProcess.WaitForInputIdle(); acApp = (AcadApplication)Marshal.GetActiveObject(progID); Jusque là pas de problème !! Maintenant, pour que l'opération soit transparente pour l'utilisateur, j'aurai souhaité qu'Autocad, ne soit pas visible. J'ai donc essayé en rajoutant ces lignes : acadProcess.StartInfo.UseShellExecute = false; acadProcess.StartInfo.CreateNoWindow = true; acadProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; Mais celà ne fonctionne pas :( . Siquelqu'un sait comment procéder, cela me serait d'une grand utilité :) Merci d'avance Goldorak PIRO CharlesDeveloppeur Revit, RV/RA - Formateur RevitPIRO CIE
(gile) Posté(e) le 25 mars 2014 Posté(e) le 25 mars 2014 Salut, Il me semble que tu fais fausse route. L'API .NET d'AutoCAD n'est accessible que depuis un processus AutoCAD (In-Process). Je crois qu'il est plus préférable récupérer une instance d'AutoCAD en cours ou d'en créer une nouvelle en utilisant l'interface COM (voir ici). Un petit exemple qui remplit un ListView dans un formulaire avec les blocs trouvé dans l'espace objet du fichier sélectionné.Le code utilise le type dynamic (Framework 4.0) pour ne pas être dépendant des librairies COM AutoCAD. private void buttonOpen_Click(object sender, EventArgs e) { listViewBlocks.Items.Clear(); if (!File.Exists(textBoxFile.Text)) { MessageBox.Show(string.Format("Le fichier '{0}' est introuvable", textBoxFile.Text)); return; } dynamic acadApp; try { acadApp = Marshal.GetActiveObject("AutoCAD.Application"); } catch { try { acadApp = Activator.CreateInstance(System.Type.GetTypeFromProgID("AutoCAD.Application")); } catch { MessageBox.Show("Impossible d'ouvrir AutoCAD"); return; } } WaitForAcadIsQuiescent(acadApp); acadApp.Visible = false; Dictionary<string, int> blocks = new Dictionary<string, int>(); dynamic acadDoc = acadApp.Documents.Open(textBoxFile.Text); WaitForAcadIsQuiescent(acadApp); foreach (dynamic ent in acadDoc.ModelSpace) { if (ent.ObjectName == "AcDbBlockReference") { string name = ent.EffectiveName; if (blocks.ContainsKey(name)) blocks[name]++; else blocks.Add(name, 1); } } acadDoc.Close(false); foreach (var pair in blocks) { listViewBlocks.Items.Add(new ListViewItem(new string[] { pair.Key, pair.Value.ToString() })); } } private void WaitForAcadIsQuiescent(dynamic acadApp) { while (true) { try { dynamic state = acadApp.GetAcadState(); if (state.IsQuiescent) break; } catch { } } } Gilles Chanteau - gileCAD - GitHub Développements sur mesure pour AutoCAD
Goldorak44 Posté(e) le 26 mars 2014 Auteur Posté(e) le 26 mars 2014 Merci, je regarde ça dès que possible ! ;) PIRO CharlesDeveloppeur Revit, RV/RA - Formateur RevitPIRO CIE
Goldorak44 Posté(e) le 26 mars 2014 Auteur Posté(e) le 26 mars 2014 Petite question : tu utilise un type dynamic (Framework 4.0), est-ce compatible avec une version autocad 2010/2011 utilisant le framework 3.5 ? Merci PIRO CharlesDeveloppeur Revit, RV/RA - Formateur RevitPIRO CIE
(gile) Posté(e) le 26 mars 2014 Posté(e) le 26 mars 2014 Petite question : tu utilise un type dynamic (Framework 4.0), est-ce compatible avec une version autocad 2010/2011 utilisant le framework 3.5 ? Le problème n'est pas tant la version d'AutoCAD.On peut très bien avoir AutoCAD 2010 ou 2011 (qui installent le Framework 3.5) et avoir installé le Framework 4.0 par ailleurs. Dans ce cas, le type dynamic est utilisable. Pour ma part, je préfère toujours cibler le Framework correspondant à la version d'AutoCAD visée.donc, dans le cas de A2010/2011, le Framework 3.5. L'utilisation du type dynamic permet d'écrire un code plus lisible (le même aux types près que si on on utilisait le typage fort en référençant les biblothèques COM). Mais on peut très bien faire quelque chose d'équivalent en utilisant la liaison tardive avec Reflection. L'inconvénient de la liaison précoce (typage fort) c'est qu'il faut référencer les biliothèques COM de la version d'AutoCAD ciblée. L'avantage, c'est que l'intellisense de Visual Studio nous aide pour les méthodes et propriétés des objets.La méthode que j'utilise ets de finaliser le code en liaison précoce, puis de supprimer les références à l'interface COM d'AutoCAD et de remplacer les types COM par dynamic (FW 4.0) ou object (FW 3.5). Exemple de code d'une application console pour ouvrir un fichier dans une instance d'AutoCAD (ici 2011) en liaison précoce (il faut référencer les bibliothèques Autodesk.AutoCAD.Interop et Autodesk.AutoCAD.Interop.Common de la version ciblée) using System; using System.IO; using System.Runtime.InteropServices; using Autodesk.AutoCAD.Interop; using Autodesk.AutoCAD.Interop.Common; namespace EarlyBinding { class Program { static void Main() { Console.WriteLine("Entrez le chemin du fichier à ouvrir : "); string filename = Console.ReadLine(); if (!File.Exists(filename)) { Console.WriteLine("Impossible de trouver le fichier spécifié"); } else { Console.WriteLine("Patientez..."); AcadApplication acadApp = null; try { acadApp = (AcadApplication)Marshal.GetActiveObject("AutoCAD.Application.18.1"); } catch { try { acadApp = (AcadApplication)Activator.CreateInstance(System.Type.GetTypeFromProgID("AutoCAD.Application.18.1")); } catch { Console.WriteLine("Impossible de trouver ou de créer une instance d'AutoCAD"); } } if (acadApp != null) { WaitForAcadIsQuiescent(acadApp); AcadDocument acDoc = acadApp.Documents.Open(filename); WaitForAcadIsQuiescent(acadApp); AcadModelSpace mspace = acDoc.ModelSpace; Console.WriteLine("{0} entités dans l'espace objet.", mspace.Count); } } Console.ReadLine(); } static void WaitForAcadIsQuiescent(AcadApplication acadApp) { while (true) { try { AcadState state = acadApp.GetAcadState(); if (state.IsQuiescent) break; } catch { } } } } } La même chose en utilisant le type dynamic, ici on n'est plus lié à une version d'AutoCAD mais il faut que le Framework 4.0 soit installé. using System; using System.IO; using System.Runtime.InteropServices; namespace Dynamic { class Program { static void Main(string[] args) { Console.WriteLine("Entrez le chemin du fichier à ouvrir : "); string filename = Console.ReadLine(); if (!File.Exists(filename)) { Console.WriteLine("Impossible de trouver le fichier spécifié"); } else { Console.WriteLine("Patientez..."); dynamic acadApp = null; try { acadApp = Marshal.GetActiveObject("AutoCAD.Application"); } catch { try { acadApp = Activator.CreateInstance(System.Type.GetTypeFromProgID("AutoCAD.Application")); } catch { Console.WriteLine("Impossible de trouver ou de créer une instance d'AutoCAD"); } } if (acadApp != null) { WaitForAcadIsQuiescent(acadApp); dynamic acDoc = acadApp.Documents.Open(filename); WaitForAcadIsQuiescent(acadApp); dynamic mspace = acDoc.ModelSpace; Console.WriteLine("{0} entités dans l'espace objet.", mspace.Count); } } Console.ReadLine(); } static void WaitForAcadIsQuiescent(dynamic acadApp) { while (true) { try { dynamic state = acadApp.GetAcadState(); if (state.IsQuiescent) break; } catch { } } } } } La même chose en utilisant la liaison tardive et Reflection. Comme avec le type dynamic, on n'est plus lié à une version d'AutoCAD. L'inconvénient de cette méthode, c'est la lourdeur du code pour appeler une propriété ou une méthode :obj.GetType().InvokeMember(...)Pour palier à ça on peut utiliser des méthodes d'extensions qui rendent le code un peu plus digeste. using System; using System.IO; using System.Reflection; using System.Runtime.InteropServices; namespace LateBinding { class Program { static void Main(string[] args) { Console.WriteLine("Entrez le chemin du fichier à ouvrir : "); string filename = Console.ReadLine(); if (!File.Exists(filename)) { Console.WriteLine("Impossible de trouver le fichier spécifié"); } else { Console.WriteLine("Patientez..."); object acadApp = null; try { acadApp = Marshal.GetActiveObject("AutoCAD.Application.18"); } catch { try { acadApp = Activator.CreateInstance(System.Type.GetTypeFromProgID("AutoCAD.Application.18")); } catch { Console.WriteLine("Impossible de trouver ou de créer une instance d'AutoCAD"); } } if (acadApp != null) { WaitForAcadIsQuiescent(acadApp); object acDoc = acadApp.Get("Documents").Invoke("Open", filename); WaitForAcadIsQuiescent(acadApp); object mspace = acDoc.Get("ModelSpace"); Console.WriteLine("{0} entités dans l'espace objet.", mspace.Get("Count")); } } Console.ReadLine(); } static void WaitForAcadIsQuiescent(object acadApp) { while (true) { try { object state = acadApp.Invoke("GetAcadState"); if ((bool)state.Get("IsQuiescent")) break; } catch { } } } } static class Extension { public static object Get(this object obj, string propName) { return obj.GetType().InvokeMember(propName, BindingFlags.GetProperty, null, obj, new object[0]); } public static void Set(this object obj, string propName, object value) { obj.GetType().InvokeMember(propName, BindingFlags.SetProperty, null, obj, new object[1] { value }); } public static object Invoke(this object obj, string methName, params object[] parameters) { return obj.GetType().InvokeMember(methName, BindingFlags.InvokeMethod, null, obj, parameters); } } } Gilles Chanteau - gileCAD - GitHub Développements sur mesure pour AutoCAD
Goldorak44 Posté(e) le 26 mars 2014 Auteur Posté(e) le 26 mars 2014 Très intéressant tout ça.Je vais analyser toutes ces possibilités, mais déjà je peux voir que le type dynamic va résoudre certains problèmes que je rencontrais. Merci beaucoup. PIRO CharlesDeveloppeur Revit, RV/RA - Formateur RevitPIRO CIE
Goldorak44 Posté(e) le 26 mars 2014 Auteur Posté(e) le 26 mars 2014 Merci beaucoup pour ces 3 exemples.Concernant l'application (simple exercice d'apprentissage !!!) je pense que le type dynamic est le plus adapté car on est pas lié à une version d'Autocad et la syntaxe reste "simple".Seul inconvénient, le manque d'intellisense (ce qui est logique), il faut donc très bien connaitre ce que l'on cherche !! Cela demande un peu d'entrainement. Je commence avec les exemple de l'aide Autodesk et des articles de Kean Walmsley. Je pense que j'aurai bientôt de nouvelle questions !!! :P PIRO CharlesDeveloppeur Revit, RV/RA - Formateur RevitPIRO CIE
(gile) Posté(e) le 26 mars 2014 Posté(e) le 26 mars 2014 Tu peux procéder comme je l'indiquais plus haut.Tu développes ton code en référençant les bibliothèques Autodesk.AutoCAD.Interop et Autodesk.AutoCAD.Interop.Common correspondant à la version d'AutoCAD que tu utilises pour déboguer, ainsi tu as l'intellisense pendant la phase de développement.Une fois ton programme abouti (si tant est qu'un programme puisse être abouti), tu supprimes ces références et tu remplaces tous les types COM (qui ne sont plus reconnus par Visual Sudio) par 'dynamic'.Attention avec les enumérations, avant de supprimer les référence, il faut connaitre l'entier qui correspond à la valeur de l'énumération (dans Visual Studio, double clic sur la valeur de l'énumération et F12). Exemple, typage fort :AcadDocument acDoc = acadApp.ActiveDocument; acDoc.Regen(AcRegenType.acActiveViewport);Typage dynamique :dynamic acDoc = acadApp.ActiveDocument; acDoc.Regen(0); Gilles Chanteau - gileCAD - GitHub Développements sur mesure pour AutoCAD
Goldorak44 Posté(e) le 26 mars 2014 Auteur Posté(e) le 26 mars 2014 La méthode que j'utilise ets de finaliser le code en liaison précoce, puis de supprimer les références à l'interface COM d'AutoCAD et de remplacer les types COM par dynamic (FW 4.0) ou object (FW 3.5). Effectivement tu me l'indiquais plus haut !!! :unsure: Merci pour la méthode. :) PIRO CharlesDeveloppeur Revit, RV/RA - Formateur RevitPIRO CIE
Goldorak44 Posté(e) le 5 avril 2014 Auteur Posté(e) le 5 avril 2014 Nouvelle petite question ! Si je créer une instance, lors de la fermeture d'autocad (acadDoc.Close();), Autocad se ferme bien mais dans le gestionnaire de tache, il y a toujours un processus AutoCAD.exe de présent. Comment couper ce processus ? Pour les autres logiciels, j'utilise l'interop et je coupe la liaison via Marchal.FinalRealiseObject. Cela fonctionne si autocad est déjà lancé au moment de l’exécution de mon application mais pas lors de lorsqu'il y a création de l'instance. PIRO CharlesDeveloppeur Revit, RV/RA - Formateur RevitPIRO CIE
(gile) Posté(e) le 5 avril 2014 Posté(e) le 5 avril 2014 Salut, Pour fermer proprement AutoCAD, je ferme le document avec acDoc.Close() en passant true pour enregistrer les modifications ou false pour ne pas enregistrerje ferme AutoCAD : acadApp.Quit() Tu peux utiliser une variable de type bool pour savoir si un processus existait déjà (auquel cas on ne le ferme pas) ou s'il a fallu en créer un (auquel cas, on le ferme. using System; using System.IO; using System.Runtime.InteropServices; namespace Dynamic { class Program { static void Main(string[] args) { Console.WriteLine("Entrez le chemin du fichier à ouvrir : "); string filename = Console.ReadLine(); if (!File.Exists(filename)) { Console.WriteLine("Impossible de trouver le fichier spécifié"); } else { Console.WriteLine("Patientez..."); dynamic acadApp = null; bool created = false; try { acadApp = Marshal.GetActiveObject("AutoCAD.Application"); } catch { try { acadApp = Activator.CreateInstance(System.Type.GetTypeFromProgID("AutoCAD.Application")); created = true; } catch { Console.WriteLine("Impossible de trouver ou de créer une instance d'AutoCAD"); } } if (acadApp != null) { WaitForAcadIsQuiescent(acadApp); dynamic acDoc = acadApp.Documents.Open(filename); WaitForAcadIsQuiescent(acadApp); dynamic mspace = acDoc.ModelSpace; Console.WriteLine("{0} entités dans l'espace objet.", mspace.Count); if (created) { acDoc.Close(false); // Fermer le document sans sauvegarder acadApp.Quit(); // Fermer AutoCAD (arrête le processus) Console.WriteLine("AutoCAD fermé"); } } } Console.ReadLine(); } static void WaitForAcadIsQuiescent(dynamic acadApp) { while (true) { try { dynamic state = acadApp.GetAcadState(); if (state.IsQuiescent) break; } catch { } } } } } Gilles Chanteau - gileCAD - GitHub Développements sur mesure pour AutoCAD
Goldorak44 Posté(e) le 5 avril 2014 Auteur Posté(e) le 5 avril 2014 Arf suis-je bête, effectivement, .Quit() est logique. Je m'entêtais avec acadApp.Close() .Pour la variable de type bool, tu confirme ce que j'ai fait. Merci :) PIRO CharlesDeveloppeur Revit, RV/RA - Formateur RevitPIRO CIE
Messages recommandés
Créer un compte ou se connecter pour commenter
Vous devez être membre afin de pouvoir déposer un commentaire
Créer un compte
Créez un compte sur notre communauté. C’est facile !
Créer un nouveau compteSe connecter
Vous avez déjà un compte ? Connectez-vous ici.
Connectez-vous maintenant