Приветствую всех васмовцев. Хочу маленько пообсуждать COM. Вот код который генерирует VS2003, для доcтупа к функции. Код (Text): p_inputDeviceJoystick->SetCooperativeLevel(hWnd, DISCL_EXCLUSIVE|DISCL_FOREGROUND); 0047E0F2 mov esi,esp 0047E0F4 push 5 0047E0F6 mov eax,dword ptr [hWnd] 0047E0F9 push eax 0047E0FA mov ecx,dword ptr [this] 0047E0FD mov edx,dword ptr [ecx+14Ch] 0047E103 mov eax,dword ptr [this] 0047E106 mov ecx,dword ptr [eax+14Ch] 0047E10C mov edx,dword ptr [edx] 0047E10E push ecx 0047E10F call dword ptr [edx+34h] 0047E112 cmp esi,esp 0047E114 call @ILT+13660(__RTC_CheckEsp) (47A561h) Сразу бросается в глаза то что edx и ecx инициализируются очень смешно. Код (Text): 0047E0FA mov ecx,dword ptr [this] 0047E0FD mov edx,dword ptr [ecx+14Ch] 0047E103 mov eax,dword ptr [this] 0047E106 mov ecx,dword ptr [eax+14Ch] Почему не mov ecx, edx? Ну да ладно оставим это на совести разработчиков компилятора. Обращает на себя внимание строка: В статье многоуважаемого мной Aquila "Как получить доступ к COM-объекту, используя ассемблер", по всей видимости вкралась ошибка. Код (Text): ; получаем указатель на объект mov eax, ppv ; и используем его, чтобы найти структуру интерфейса mov edx, [eax] ; загоняем параметры функции в стек push OFFSET ppv2 push OFFSET IID_ISomeOtherInterface push dword ppv ; а затем вызываем этот метод call dword ptr [eax + 0] Наверное он всё же имел ввиду call dword ptr [edx + 0], а не eax. Ну да ладно, меня же больше интересует первая строка из этого. Код (Text): 0047E10E push ecx 0047E10F call dword ptr [edx+34h] Для чего нужно передавать указатель на объект в вызов Com метода. Мои соображения таковы: Указатель на объект который возвращается например CoCreateInstance, это область памяти переменных объекта, так называемый указатель на this объекта класса. Перавая переменная в этой области, это указатель на vtbl. Причём vtbl всех объектов определённого класса одна и таже. И соответственно местоположение кода идентично. Это и логично ведь код у всех экземпляров одного класса идентичен, и не зачем для каждого экземпляра создавать отдельный участок памяти для него. А вот область памяти переменных у каждого экземпляра своя. Вообщем может кому-то это может показаться очевидным, но это нужно чётко себе представлять когда делается перехват методов объекта. Получается что перехватив функцию в vtbl одного экземпляра, мы автоматически перехватываем во всех экземплярах этого класса. Я этого по началу не понимал, и пытался перехватывать во всех экземплярах, что вело к ошибкам. Но ведь есть ещё понятие интерфейса, и похоже что функции во всех vtbl одного интерфейса, ссылаются на один и тот же код. То есть экземпляр IUnknown один не только для экземпляров одного класса, но и для экземпляров всех других классов, или я не прав? Ещё есть когда интефейс один, но реализаций может быть много, например в DInput функции CreateDevice передаётся GUID устройства. И вроде получаем интерфейс один, но реализаций несколько, а может и не нет, тут у меня ясности пока нет. Интересно что скажут знатоки COM Aquila, Roustem, и другие по этому интересному вопросу.
Вообще-то если смотреть на генерацию кода - надо смотреть на оптимизированный код. Присутствие __RTC_CheckEsp говорит, что код скомпилирован в DEBUG режиме. Про COM: в теории, каждый COM интерфейс имеет свою реализацию. Это значит, что реализаций должно быть столько сколько есть COM интерфейсов. Надо посмотреть в отладчике виртуальные таблицы для разных интерфейсов - какие адреса там проставлены в первых трёх DWORD-ах.
AsmGuru62[/b Да, тут я поспешил с оценкой, в DEBUG наверно лучше такой код. То есть реализация IUnknown всегда одна? Но как тогда быть с интерфейсом объекта возращаемого CreateDevice. Интерфейс то один, а устройств несколько, зависит только от передаваемого функции CreateDevice гуида устройства. Не ужели по стандарту нужно код всех устройств пихать в одну реализацию? Похоже что так и сделано.
мне кажется некоторые вопросы от недостатка теории. я очень советую: "Дейл Роджерсон - Основы COM" в сети есть пдфка на русском. очень все по полочкам разобрано.