Re[4]: Выполнение функции GetClassInfoExA в C#
От: Meverik  
Дата: 08.05.13 06:52
Оценка: 6 (1)
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, Meverik, Вы писали:


M>>В C# пытаюсь работать с неуправляемым кодом DLL а конкретно с DLL написанной на Delphi содержащей форму.

M>>Все создается, формой управляю, но вот когда из Delphi еще одна форма создается,
M>>вот тут начинаются проблемы по созданию компонентов (расположенных на данной форме) основанных на стандартных оконных классах (TButton, TEdit и т.п.). Точнее объект Delphi класса создается, но
M>>когда происходит создание Windows окна компонента (непосредственно метод TWinControl.CreateWnd), то с помощью GetClassInfoA вычисляется адрес стандартного оконного класса, а этот адрес уже не тот, что при создании компонентов на первой форме и соответственно при передачи его в CreateWindowEx функция в итоге не возвращает Handle окна, что приводит к ошибке RaiseLastOSError (A call to an OS function failed). После длительного разбора "полетов" пришел вот к такой особенности C#.... И как это обойти пока не ясно....

PD>Могу предложить попробовать трюк. А именно, создать никому не нужную форму из C# до всей этой деятельности (сделать ее невидимой, конечно). Возможно, при этом вся деятельность по сабклассингу пройдет, так что создание как первой , так и второй Delphi форм будет происходить уже в условиях свершившегося сабклассинга. Поможет или нет — бог знает.


Так дело в том что первая форма создается нормально, а вот если из нее еще одну создавать.... вообщем уже не суть....

В итоге проблема решена. Для истории опишу что было:

В C# есть метод класса Application: EnableVisualStyles(); -- Этот метод включает визуальные стили приложения, что в итоге приводит к тому, что для прорисовки управляющих элементов (которые соответственно основаны на стандартных оконных классах типа BUTTON, EDIT и т.п.) необходимо использовать библиотеку comctl32.dll 6-ой версии, а по умолчанию используется comctl32.dll 5-ой версии, для отрисовки все тех же элементов управления. Так вот в Windows XP уже была добавлена возможность работать одновременно с двумя одинаковыми библиотеками разных версий, что успешно делает C#. Вот почему до создания формы в C# GetClassInfoExA возвращал один адрес оконной процедуры (которая прописана для данного оконного класса в comctl32.dll 5-ой версии), а при созданной форме уже использовал для элементов управления библиотеку comctl32.dll 6-ой версии, вот GetClassInfoExA и возвращая другой адрес, т.к. фактически классы EDIT, BUTTON и т.п. созданы в другой библиотеки. Ну а Delphi по умолчанию просто использует comctl32.dll 5-ой версии и в нем таких проблем не было.

Решение следующее (возможно можно как-то по другому, если есть идея, буду рад ознакомиться с ней): Комментим Application.EnableVisualStyles() для того, чтобы C# использовал только comctl32.dll 5-ой версии и все.... все будет работать, только визуальные компоненты отображаются уже не "красивыми" как изначально с данной опцией.... !!! НО !!! Есть, начиная с Windows XP такие файлы: Manifest называются )) Вот с помощью их описываем, что приложение должно использовать comctl32.dll 6-ой версии и C# тогда начинает во всем приложении использовать только 6-ую версию библиотеки... включая модули (DLL формы) написанные на Delphi и все управляющие элементы становятся снова "красивыми" ))

Манифест можно положить рядом с EXE файлом программы (с названием: [имя файла программы].exe.Manifest) или добавить его в C# проект и в настройка проекта выбрать данный файл манифеста, чтобы он присутствовал в самом EXE файле программы, что я и сделал.

Манифест у меня такой:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> 
      <application> 
        <!--The ID below indicates application support for Windows Vista --> 
          <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> 
        <!--The ID below indicates application support for Windows 7 --> 
          <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> 
      </application> 
    </compatibility>

<assemblyIdentity
   name="Название моей программы"
   processorArchitecture="*"
   version="1.0.0.0"
   type="win32"/>
<description>Описание моей программы</description>
<dependency>
   <dependentAssembly>
       <assemblyIdentity
           type="win32"
           name="Microsoft.Windows.Common-Controls"
           version="6.0.0.0"
           processorArchitecture="*"
           publicKeyToken="6595b64144ccf1df"
           language="*"
       />
   </dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
      <security>
      <requestedPrivileges>
        <requestedExecutionLevel
          level="asInvoker"
          uiAccess="false"/>
      </requestedPrivileges>
    </security>
   </trustInfo>
</assembly>
Выполнение функции GetClassInfoExA в C#
От: Meverik  
Дата: 06.05.13 07:38
Оценка:
Доброго дня.

Заметил в C# такую особенность:
При попытке получить информацию по любому стандартному классу Windows (EDIT, BUTTON, COMBOBOX и др.) с помощью API функции GetClassInfoExA, получаю разные адреса оконной процедуры класса.

Пример:

Класс NativeMethods:
public partial class NativeMethods
    {                
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct TWndRecordEx
        {
            uint cbSize;
            uint style;
            public IntPtr lpfnWndProc;
            public int cbClsExtra;
            public int cbWndExtra;
            public uint hInstance;
            public uint HICON;
            public uint hCursor;            
            public uint hbrBackground;
            [MarshalAs(UnmanagedType.LPStr)]
            public string lpszMenuName;
            [MarshalAs(UnmanagedType.LPStr)]
            public string lpszClassName;
            public uint hIconSm;
        }
        
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false), DebuggerStepThrough]
        public static extern bool GetClassInfoExA(uint hInstance, [MarshalAs(UnmanagedType.LPStr)]string lpClassName, out TWndRecordEx lpWndClass);
        
        public static void WriteClassInfo()
        {
            TWndRecordEx wndRecEx = new TWndRecordEx();
            if (GetClassInfoExA(0, "EDIT", out wndRecEx))
            {              
                Debug.WriteLine(String.Format("GetClassInfoExA: Result=True;  lpfnWndProc={0}; hInstance={1}", Convert.ToString(wndRecEx.lpfnWndProc.ToInt32(), 16), Convert.ToString(wndRecEx.hInstance, 16)));
            }
            else
            {
                Debug.WriteLine("GetClassInfoExA: Result=False");
            }                       
        }
    }


Класс основной C# программы:
static class Program
    {
        /// <summary>
        /// Главная точка входа для приложения.
        /// </summary>
        [STAThread]        
        static void Main()
        {                                    
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            
            NativeMethods.WriteClassInfo(); // точка 1

            TextBox tb = new TextBox();
            Button b = new Button();

            b.Click += new EventHandler(b_Click);

            Form btn = new Form();
            //btn.Controls.Add(tb);
            btn.Controls.Add(b);
            btn.CreateControl();           
            btn.Shown += new EventHandler(btn_Shown);
            btn.Show();  
          
            NativeMethods.WriteClassInfo(); // точка 2

            //Application.Run(new Form1());
            Application.Run(btn);

            NativeMethods.WriteClassInfo(); // точка 3
        }

        static void b_Click(object sender, EventArgs e)
        {            
            ((sender as Button).Parent as Form).Hide();
            NativeMethods.WriteClassInfo(); // точка 5        
            ((sender as Button).Parent as Form).Show();
            NativeMethods.WriteClassInfo(); // точка 6
        }

        static void btn_Shown(object sender, EventArgs e)
        {            
            NativeMethods.WriteClassInfo();  // точка 4                    
        }
    }


Так вот в точке 1,2 и 3 адрес оконной процедуры стандартного класса Windows один, а в точке 4,5 и 6 совершенно другой адрес оконной функции возвращается.
Т.е. пока форма не отображается адрес один, как только форма отобразилась, адрес другой, при этом когда форма закрывается, адрес восстанавливается на первоначальный (точка 3).

При этом в Delphi например такой особенности нет, что создана форма, что нет, никак не влияет на стандартные оконные классы Windows.

В чем может быть такая особенность/проблема ? можно ли как-то указать в C# чтобы адреса возвращались постоянно одинаковые ?
Re: Выполнение функции GetClassInfoExA в C#
От: Pavel Dvorkin Россия  
Дата: 06.05.13 12:18
Оценка:
Здравствуйте, Meverik, Вы писали:

M>В чем может быть такая особенность/проблема ?


По-видимому, сабклассинг.


>можно ли как-то указать в C# чтобы адреса возвращались постоянно одинаковые ?


Если сабклассинг, то нет. А зачем ?
With best regards
Pavel Dvorkin
Re[2]: Выполнение функции GetClassInfoExA в C#
От: Meverik  
Дата: 07.05.13 01:07
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, Meverik, Вы писали:


M>>В чем может быть такая особенность/проблема ?


PD>По-видимому, сабклассинг.


>>можно ли как-то указать в C# чтобы адреса возвращались постоянно одинаковые ?


PD>Если сабклассинг, то нет. А зачем ?


В C# пытаюсь работать с неуправляемым кодом DLL а конкретно с DLL написанной на Delphi содержащей форму.
Все создается, формой управляю, но вот когда из Delphi еще одна форма создается,
вот тут начинаются проблемы по созданию компонентов (расположенных на данной форме) основанных на стандартных оконных классах (TButton, TEdit и т.п.). Точнее объект Delphi класса создается, но
когда происходит создание Windows окна компонента (непосредственно метод TWinControl.CreateWnd), то с помощью GetClassInfoA вычисляется адрес стандартного оконного класса, а этот адрес уже не тот, что при создании компонентов на первой форме и соответственно при передачи его в CreateWindowEx функция в итоге не возвращает Handle окна, что приводит к ошибке RaiseLastOSError (A call to an OS function failed). После длительного разбора "полетов" пришел вот к такой особенности C#.... И как это обойти пока не ясно....
Re[3]: Выполнение функции GetClassInfoExA в C#
От: Pavel Dvorkin Россия  
Дата: 07.05.13 06:27
Оценка:
Здравствуйте, Meverik, Вы писали:

M>В C# пытаюсь работать с неуправляемым кодом DLL а конкретно с DLL написанной на Delphi содержащей форму.

M>Все создается, формой управляю, но вот когда из Delphi еще одна форма создается,
M>вот тут начинаются проблемы по созданию компонентов (расположенных на данной форме) основанных на стандартных оконных классах (TButton, TEdit и т.п.). Точнее объект Delphi класса создается, но
M>когда происходит создание Windows окна компонента (непосредственно метод TWinControl.CreateWnd), то с помощью GetClassInfoA вычисляется адрес стандартного оконного класса, а этот адрес уже не тот, что при создании компонентов на первой форме и соответственно при передачи его в CreateWindowEx функция в итоге не возвращает Handle окна, что приводит к ошибке RaiseLastOSError (A call to an OS function failed). После длительного разбора "полетов" пришел вот к такой особенности C#.... И как это обойти пока не ясно....

Могу предложить попробовать трюк. А именно, создать никому не нужную форму из C# до всей этой деятельности (сделать ее невидимой, конечно). Возможно, при этом вся деятельность по сабклассингу пройдет, так что создание как первой , так и второй Delphi форм будет происходить уже в условиях свершившегося сабклассинга. Поможет или нет — бог знает.
With best regards
Pavel Dvorkin
Re[5]: Выполнение функции GetClassInfoExA в C#
От: Pavel Dvorkin Россия  
Дата: 08.05.13 14:59
Оценка:
Здравствуйте, Meverik, Вы писали:

M>В итоге проблема решена. Для истории опишу что было:


Меня одно смущает. EDIT и BUTTON не относятся к Common Contols, они существуют с незапамятных времен, когда и comctl32 не было.
With best regards
Pavel Dvorkin
Re[6]: Выполнение функции GetClassInfoExA в C#
От: Meverik  
Дата: 13.05.13 01:42
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Здравствуйте, Meverik, Вы писали:


M>>В итоге проблема решена. Для истории опишу что было:


PD>Меня одно смущает. EDIT и BUTTON не относятся к Common Contols, они существуют с незапамятных времен, когда и comctl32 не было.


Ну судя по всему они каким то образом связаны с comctl32, иначе адреса оконной процедуры стандартных классов были бы одинаковыми....
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.