Отладка пользовательской библиотеки DLL, на которую ссылается макрос Visual Studio

Ранее я спрашивал: Добавить ссылку на dll в макросы Visual Studio

идея создания макросов на моем языке (C #) упрощает создание макросов. Проблема в том, что я не могу отладить dll

Чтобы решить проблему, я попытался:

  1. Я поместил myClassLibrary.pdb рядом с myClassLibrary.dll, надеясь, что я смогу отлаживать методы в dll, перейдя к ним.

  2. Создал сервис WCF. Поскольку я не знал, как ссылаться на службу из vba, я ссылаюсь на нее из библиотеки классов. Проблема в том, что мне нужно использовать такие переменные, как DTE.ActiveDocument, и эти переменные не сериализуемы, что означает, что я не мог передать их службе wcf.

идея работы на C # очень хороша, но невозможность отлаживать и видеть, что происходит, несколько затрудняет ее. Возможно, мне придется перейти к моему старому варианту, где я создал свой код на C #, скомпилированный, а затем декомпилированный в vba с отражателем.


Редактировать

Думаю, я близок к решению. Я подумал, а почему бы не создать макрос в консольном приложении? Я могу получить активный текст документа, но не могу его изменить.

        EnvDTE80.DTE2 MyDte;
        MyDte = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject( "VisualStudio.DTE.10.0" );
        Console.WriteLine( "The Edition is " + MyDte.Edition );

        Console.ReadLine( );

        // write to the console the text that is selected. "sometimes it does not work don't know why"
        Console.WriteLine(
            MyDte.ActiveDocument.Selection.Text
        );

обратите внимание, что я добавил следующие ссылки, а также все, что есть в макросах vba:

введите описание изображения здесь


person Tono Nam    schedule 25.07.2012    source источник
comment
Если ваша проблема в том, что вы не можете отладить dll, есть несколько способов ... во-первых, если вы каким-то образом загружаете ее в сеансе отладки, но точки останова не попадают, вы, вероятно, загружаете неправильную dll (посмотрите на путь при загрузке ). Если вы не можете отлаживать управляемую отладку, возможно, вам придется включить отладку в смешанном режиме ... собственный и управляемый. Вы также можете принудительно установить точку останова с помощью Debugger.Break msdn.microsoft.com/en-us/library/, а затем выполните отладку из другого экземпляра.   -  person Andreas Reiff    schedule 03.08.2012


Ответы (5)


Наконец, вот решение:

На следующих шагах я опишу, как можно будет отлаживать dll, которая будет выполняться макросом.

Если вы хотите сделать что-то вроде:

введите описание изображения здесь(Обратите внимание, я отлаживаю макрос на C # в Visual Studio !!!) < / сильный>

  1. Создайте новое Решение в Visual Studio введите описание изображения здесь

  2. Теперь добавьте в это решение новую библиотеку классов Project. (Это класс, который будет выполнять макросы) введите описание изображения здесь

  3. Добавьте ссылки EnvDTE, EbvDTE100, EnvDTE80, EnvDTE90, EnvDTE90a. В основном те же ссылки, что и у макросов Visual Studio: введите описание изображения здесь

  4. Создайте метод, который будет выполнять макрос, который вы планируете использовать в библиотеке классов.

       namespace ClassLibrary1
       {
           public static class Class1
           {
               public static void Macro1(EnvDTE80.DTE2 DTE)
               {
                   // make sure an active text document is open before calling this method
                   DTE.ActiveDocument.Selection.Insert("Hello World!!!");
               }
           }
       }
    
  5. Добавьте еще один проект (надстройка Visual Studio) введите описание изображения здесь

  6. Следуйте указаниям мастера, оставьте значения по умолчанию, кроме страницы 4, выберите: введите описание изображения здесь

  7. Продолжайте выбирать параметры по умолчанию в мастере, пока проект не будет создан: введите описание изображения здесь

  8. Установите этот проект как запускаемый, чтобы при нажатии клавиши f5 надстройка запускалась.

  9. Добавить ссылку из MyAddin1 в библиотеку классов

  10. Как только у нас будет эта ссылка, мы сможем выполнить макрос из надстройки. Для этого откройте Connect.cs и перейдите к методу Exec добавить ClassLibrary1.Class1.Macro1(_applicationObject);, чтобы он выглядел так: введите здесь описание изображения

  11. Добавьте точку останова в начале метода Exec, чтобы мы могли его отладить.

  12. Выполните MyAddin1, нажав F5. Должен открыться новый экземпляр Visual Studio.

  13. На новом экземпляре Visual Studio откройте любое решение. В этом случае я снова открываю то же решение>

  14. Перейдите к инструментам, затем нажмите MyAddin1, но убедитесь, что документ открыт: введите описание изображения здесь

  15. Как только вы нажмете на мою надстройку, вы должны попасть в точку останова! введите описание изображения здесь

15. ПРИМЕЧАНИЕ! Почему-то пришлось прокомментировать строчку ClassLibrary1.Class1.Macro1(_applicationObject);

Я закомментирую эту строку и в эту строку я поместил:

 var textDoc = (TextDocument)(_applicationObject.ActiveDocument.Object(string.Empty));
 textDoc.Selection.Insert("Hello world");

наконец, когда я нажимаю MyAddin1 в инструментах, будет вставлен Hello world!


Как только я узнаю, что макрос работает нормально, я могу экспортировать класс в библиотеку классов и заставить макрос вызывать метод в dll вместо плагина.

person Tono Nam    schedule 28.07.2012

У меня есть еще один ответ, даже лучше!

Единственная причина, по которой я создал надстройку, - это то, что мне нужна была ссылка на DTE. Почему бы не сослаться на то, что мне нужно?

Алгоритм следующий:

  1. Используйте класс Ide, чтобы получить DTE любого экземпляра Visual Studio.

  2. Как только у вас есть это dte, создайте макрос.

Вот класс Ide:

public class Ide
{        
    [DllImport("ole32.dll")]
    private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);

    [DllImport("ole32.dll")]
    private static extern void GetRunningObjectTable(int reserved, out IRunningObjectTable prot);

    public static DTE2 GetDte(string solutionName)
    {
        DTE2 dte = null;

        GetDte((displayName, x) =>
        {
            if (System.IO.Path.GetFileName(x.Solution.FullName).Contains(solutionName))
            {
                dte = x;
                return false; // we found it stop seraching
            }
            else
            {
                return true; // continue searching
            }

        });

        return dte;
    }

    public static DTE2 GetDte(int processId)
    {
        DTE2 dte = null;

        GetDte((displayName, x) =>
        {
            if (displayName.Contains(processId.ToString()))
            {
                dte = x;
                return false; // stop searching we found matching dte
            }
            else
            {
                return true; // continue searching
            }
        });

        return dte;
    }

    public static List<DTE2> GetAllDte()
    {
        List<DTE2> list = new List<DTE2>();
        GetDte((displayName, x) =>
        {
            list.Add(x);
            return true; // continue serching we want all dte's
        });
        return list;
    }

    private static void GetDte(Func<string, DTE2, bool> foo)
    {
        Dictionary<string, string> dtesProcessIds = new Dictionary<string, string>();

        //rot entry for visual studio running under current process.            
        IRunningObjectTable rot;
        GetRunningObjectTable(0, out rot);
        IEnumMoniker enumMoniker;
        rot.EnumRunning(out enumMoniker);
        enumMoniker.Reset();
        IntPtr fetched = IntPtr.Zero;
        IMoniker[] moniker = new IMoniker[1];
        while (enumMoniker.Next(1, moniker, fetched) == 0)
        {
            IBindCtx bindCtx;
            CreateBindCtx(0, out bindCtx);
            string displayName;
            moniker[0].GetDisplayName(bindCtx, null, out displayName);
            object comObject;
            rot.GetObject(moniker[0], out comObject);

            if (comObject != null)
            {
                DTE2 dteCurrent = null;
                try
                {
                    dteCurrent = (EnvDTE80.DTE2)comObject;

                    // if solution is not open continue
                    // this will cause an exception if it is not open
                    var temp = dteCurrent.Solution.IsOpen;

                    string solName = dteCurrent.Solution.FullName;

                    // if there is an instance of visual studio with no solution open continue                        
                    if (string.IsNullOrEmpty(solName))
                    {
                        continue;
                    }

                    // avoid adding duplicate ide's
                    if (dtesProcessIds.ContainsKey(displayName) == false)
                    {
                        dtesProcessIds.Add(displayName, displayName);
                    }
                    else
                    {
                        continue;
                    }

                }
                catch (System.Runtime.InteropServices.COMException e)
                {
                    continue;
                }
                catch (Exception e)
                {
                    continue;
                }
                if (dteCurrent != null)
                {
                    var cont = foo(displayName, dteCurrent);

                    if (cont == false)
                        return;
                }
            }

        }
    }
}

то, если у меня есть экземпляр запущенной визуальной студии, содержащий решение с именем ConsoleApp1, я смогу сделать:

 var dte = Ide.GetDte("ConsoleApp1");
 dte.ActiveDocument.Selection.Insert("My macro is working!");

и текст My macro is working! будет вставлен в активный документ. убедитесь, что есть активный документ

person Tono Nam    schedule 29.07.2012

Тоно,

Вы ссылались на свою dll для приложения wcf и сделали ее отладочной версией?

От: http://msdn.microsoft.com/en-us/library/ms164704.aspx:

Независимо от того, как вы начинаете отладку, убедитесь, что вы сначала создали отладочную версию библиотеки DLL, и убедитесь, что отладочная версия находится в том месте, где приложение ожидает ее найти. Это может показаться очевидным, но если вы забудете этот шаг, приложение может найти другую версию DLL и загрузить ее. После этого программа продолжит работу, а вы задаетесь вопросом, почему ваша точка останова не была достигнута. Во время отладки вы можете проверить, какие библиотеки DLL загружены в вашу программу, открыв окно «Модули» отладчика. В окне «Модули» перечислены все библиотеки DLL или EXE, загруженные в отлаживаемом процессе. Для получения дополнительной информации см. Как: использовать окно модулей.


Если вы пытаетесь отладить DLL, на которую указывает ссылка, эта статья SO может быть здесь полезной: Как для отладки указанной DLL (имеющей pdb)

~ JOL

person DotNetDeveloper    schedule 25.07.2012
comment
Мне надоели эти ссылки, и я не могу зайти в dll. Я также попытался поместить файл .pdb в system / windows / system32, потому что это рабочий каталог при выполнении макросов vba. Спасибо за помощь - person Tono Nam; 26.07.2012
comment
Тоно, из любопытства, какую версию Visual Studio вы используете для справок в будущем? - person DotNetDeveloper; 26.07.2012
comment
Visual Studio 2010 Professional Edition - person Tono Nam; 28.07.2012

Использовать библиотеки DLL других издателей очень просто. Но важно понимать код. Я использую IlSpy. Его бесплатно и использовать. Он декомпилирует dll для просмотра всех методов, классов и пространств имен.

person eL-Prova    schedule 31.07.2012

Способ отладки надстроек Visual Studio - открыть другой экземпляр Visual Studio и присоединиться к тому, который активирует вашу надстройку. Это описано здесь. Также убедитесь, что надстройка создана на локальном компьютере и доступны pdbs, иначе она не попадет в ваши точки останова.

person Cosmin Onea    schedule 03.08.2012