Blog. Just Blog

Работа с TypeLib на Ассемблере

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Работа с TypeLib на Ассемблере
Работа с TypeLib на Ассемблере

В предыдущей статье, посвященной программам для работы с TypeLib, я обещал рассказать о возможностях программного доступа к содержимому этих библиотек. Напомню, что в TypeLib или, если по-русски, в библиотеке типов содержится информация о COM-объектах, их свойствах и методах, константах, структурах и т.п. Тема большая и интересная, по крайней мере для меня.

Информации по данному вопросу в интернете практически нет, тем более касающейся работы с библиотеками типов на Ассемблере. Что-то удалось собрать по крупицам из разрозненных источников, что-то нашлось в исходниках, но большую часть пришлось разбирать самостоятельно. В статье я постараюсь осветить все основные принципы разбора TypeLib. Но сразу скажу, что разбор библиотек типов мне требуется в первую очередь для пополнения базы моих программ GUID Helper и Constant Helper, поэтому часть малозначащей, на мой взгляд, информации сознательно будет пропущена. При необходимости вы всегда можете дописать весь недостающий вам функционал на базе приведенного в статье кода.

Практически все тут будет основано на взаимодействии с COM, так что традиционно начинаем с описания здоровенной пачки интерфейсов, структур и констант, которые нам понадобятся для дальнейшей работы. Обратите внимание, что некоторые структуры приходится выравнивать до границы DWORD, иначе адресация будет работать неправильно. Что ж, несколько часов в отладчике стоили того, чтобы приобрести эти знания. А в официальной документации MSDN об этой важной особенности не сказано ни слова.
  1. struct DECIMAL
  2.     wReserved dw ?
  3.     union
  4.         struct
  5.             scale db ?
  6.             sign db ?
  7.         ends
  8.         signscale dw ?
  9.     ends
  10.     Hi32 dd ?
  11.     union
  12.         struct
  13.             Lo32 dd ?
  14.             Mid32 dd ?
  15.         ends
  16.         Lo64 dq ?
  17.     ends
  18. ends
  19.  
  20. struct VARIANT
  21.     union
  22.         struct
  23.             vt dw ?
  24.             wReserved rw 3
  25.             union
  26.                 llVal dq ?
  27.                 lVal  dd ?
  28.                 iVal  dw ?
  29.                 bVal  db ?
  30.             ends
  31.         ends
  32.         decVal DECIMAL
  33.     ends
  34. ends
  35.  
  36. ; ITypeLib Interface
  37. struct ITypeLib
  38.     ; IUnknown
  39.     QueryInterface    dd ?   ; 000h
  40.     AddRef            dd ?   ; 004h
  41.     Release           dd ?   ; 008h
  42.     ; ITypeLib
  43.     GetTypeInfoCount  dd ?   ; 00Ch
  44.     GetTypeInfo       dd ?   ; 010h
  45.     GetTypeInfoType   dd ?   ; 014h
  46.     GetTypeInfoOfGuid dd ?   ; 018h
  47.     GetLibAttr        dd ?   ; 01Ch
  48.     GetTypeComp       dd ?   ; 020h
  49.     GetDocumentation  dd ?   ; 024h
  50.     IsName            dd ?   ; 028h
  51.     FindName          dd ?   ; 02Ch
  52.     ReleaseTLibAttr   dd ?   ; 030h
  53. ends
  54.  
  55. ; ITypeInfo Interface
  56. struct ITypeInfo
  57.     ; IUnknown
  58.     QueryInterface       dd ?   ; 000h
  59.     AddRef               dd ?   ; 004h
  60.     Release              dd ?   ; 008h
  61.     ; ITypeInfo
  62.     GetTypeAttr          dd ?   ; 00Ch
  63.     GetTypeComp          dd ?   ; 010h
  64.     GetFuncDesc          dd ?   ; 014h
  65.     GetVarDesc           dd ?   ; 018h
  66.     GetNames             dd ?   ; 01Ch
  67.     GetRefTypeOfImplType dd ?   ; 020h
  68.     GetImplTypeFlags     dd ?   ; 024h
  69.     GetIDsOfNames        dd ?   ; 028h
  70.     _Invoke              dd ?   ; 02Ch
  71.     GetDocumentation     dd ?   ; 030h
  72.     GetDllEntry          dd ?   ; 034h
  73.     GetRefTypeInfo       dd ?   ; 038h
  74.     AddressOfMember      dd ?   ; 03Ch
  75.     CreateInstance       dd ?   ; 040h
  76.     GetMops              dd ?   ; 044h
  77.     GetContainingTypeLib dd ?   ; 048h
  78.     ReleaseTypeAttr      dd ?   ; 04Ch
  79.     ReleaseFuncDesc      dd ?   ; 050h
  80.     ReleaseVarDesc       dd ?   ; 054h
  81. ends
  82.  
  83. struct TYPEDESC
  84.     union
  85.       lptdesc  dd ?
  86.       lpadesc  dd ?
  87.       hreftype dd ?
  88.     ends
  89.     vt         dw ?
  90.                dw ?
  91. ends
  92.  
  93. struct PARAMDESC
  94.     pparamdescex dd ?
  95.     wParamFlags  dw ?
  96.                  dw ?
  97. ends
  98.  
  99. struct IDLDESC
  100.     dwReserved dd ?
  101.     wIDLFlags  dw ?
  102.                dw ?
  103. ends
  104.  
  105. struct ELEMDESC
  106.     tdesc TYPEDESC
  107.     union
  108.        idldesc   IDLDESC
  109.        paramdesc PARAMDESC
  110.     ends
  111. ends
  112.  
  113. struct SAFEARRAYBOUND
  114.      cElements dd ?
  115.      lLbound   dd ?
  116. ends
  117.  
  118. struct ARRAYDESC
  119.      tdescElem TYPEDESC
  120.      cDims     dw ?
  121.                dw ?
  122.      rgbounds  SAFEARRAYBOUND
  123. ends
  124.  
  125. struct TYPEATTR
  126.     guid             rb 16
  127.     lcid             dd ?
  128.     dwReserved       dd ?
  129.     memidConstructor dd ?
  130.     memidDestructor  dd ?
  131.     lpstrSchema      dd ?
  132.     cbSizeInstance   dd ?
  133.     typekind         dd ?
  134.     cFuncs           dw ?
  135.     cVars            dw ?
  136.     cImplTypes       dw ?
  137.     cbSizeVft        dw ?
  138.     cbAlignment      dw ?
  139.     wTypeFlags       dw ?
  140.     wMajorVerNum     dw ?
  141.     wMinorVerNum     dw ?
  142.     tdescAlias       TYPEDESC
  143.     idldescType      IDLDESC
  144. ends
  145.  
  146. struct VARDESC
  147.     memid          dd ?
  148.     lpstrSchema    dd ?
  149.     union
  150.         oInst      dd ?
  151.         lpvarValue dd ?
  152.     ends
  153.     elemdescVar    ELEMDESC
  154.     wVarFlags      dw ?
  155.                    dw ?
  156.     varkind        dd ?
  157. ends
  158.  
  159. struct FUNCDESC
  160.     memid             dd ?
  161.     lprgscode         dd ?
  162.     lprgelemdescParam dd ?
  163.     funckind          dd ?
  164.     invkind           dd ?
  165.     callconv          dd ?
  166.     cParams           dw ?
  167.     cParamsOpt        dw ?
  168.     oVft              dw ?
  169.     cScodes           dw ?
  170.     elemdescFunc      ELEMDESC
  171.     wFuncFlags        dw ?
  172.                       dw ?
  173. ends
  1. VT_EMPTY            = 0
  2. VT_NULL             = 1
  3. VT_I2               = 2
  4. VT_I4               = 3
  5. VT_R4               = 4
  6. VT_R8               = 5
  7. VT_CY               = 6
  8. VT_DATE             = 7
  9. VT_BSTR             = 8
  10. VT_DISPATCH         = 9
  11. VT_ERROR            = 10
  12. VT_BOOL             = 11
  13. VT_VARIANT          = 12
  14. VT_UNKNOWN          = 13
  15. VT_DECIMAL          = 14
  16. VT_I1               = 16
  17. VT_UI1              = 17
  18. VT_UI2              = 18
  19. VT_UI4              = 19
  20. VT_I8               = 20
  21. VT_UI8              = 21
  22. VT_INT              = 22
  23. VT_UINT             = 23
  24. VT_VOID             = 24
  25. VT_HRESULT          = 25
  26. VT_PTR              = 26
  27. VT_SAFEARRAY        = 27
  28. VT_CARRAY           = 28
  29. VT_USERDEFINED      = 29
  30. VT_LPSTR            = 30
  31. VT_LPWSTR           = 31
  32. VT_RECORD           = 36
  33. VT_INT_PTR          = 37
  34. VT_UINT_PTR         = 38
  35. VT_FILETIME         = 64
  36. VT_BLOB             = 65
  37. VT_STREAM           = 66
  38. VT_STORAGE          = 67
  39. VT_STREAMED_OBJECT  = 68
  40. VT_STORED_OBJECT    = 69
  41. VT_BLOB_OBJECT      = 70
  42. VT_CF               = 71
  43. VT_CLSID            = 72
  44. VT_VERSIONED_STREAM = 73
  45. VT_STORED_PROPSET   = 74
  46. VT_BLOB_PROPSET     = 75
  47. VT_VERBOSE_ENUM     = 76
  48. VT_BSTR_BLOB        = 0x0FFF
  49. VT_VECTOR           = 0x1000
  50. VT_ARRAY            = 0x2000
  51. VT_BYREF            = 0x4000
  52. VT_RESERVED         = 0x8000
  53. VT_ILLEGAL          = 0xFFFF
  54. VT_ILLEGALMASKED    = 0x0FFF
  55. VT_TYPEMASK         = 0x0FFF
  56.  
  57. MAX_NAMES = 64
  58.  
  59. REGKIND_NONE = 2
  60.  
  61. TKIND_ENUM      = 0
  62. TKIND_RECORD    = 1
  63. TKIND_MODULE    = 2
  64. TKIND_INTERFACE = 3
  65. TKIND_DISPATCH  = 4
  66. TKIND_COCLASS   = 5
  67. TKIND_ALIAS     = 6
  68. TKIND_UNION     = 7
  69.  
  70. INVOKE_FUNC           = 1
  71. INVOKE_PROPERTYGET    = 2
  72. INVOKE_PROPERTYPUT    = 4
  73. INVOKE_PROPERTYPUTREF = 8
  74.  
  75. VAR_PERINSTANCE = 0
  76. VAR_CONST = 2
  77.  
  78. MEMBERID_NIL = -1
  79.  
  80. PARAMFLAG_NONE    = 0
  81. PARAMFLAG_FIN     = 0x1
  82. PARAMFLAG_FOUT    = 0x2
  83. PARAMFLAG_FLCID   = 0x4
  84. PARAMFLAG_FRETVAL = 0x8
  85. PARAMFLAG_FOPT    = 0x10
Процесс анализа начинается с загрузки библиотеки типов при помощи функции LoadTypeLibEx. Я рекомендую использовать именно эту функцию, потому что в параметрах можно указать тип загрузки, а именно REGKIND_NONE, то есть загрузка без регистрации библиотеки в системном реестре. А еще в этом случае не придется освобождать ресурсы. В случае успешного выполнения функция вернет указатель на интерфейс ITypeLib, который станет отправной точкой для анализа всей библиотеки.

Первым делом получим название и описание библиотеки с помощью метода GetDocumentation, а также через метод GetTypeInfoCount узнаем количество записей, которые в ней содержатся.
  1.         ; Загрузить библиотеку без регистрации
  2.         invoke  LoadTypeLibEx,szFile,REGKIND_NONE,pptlib
  3.  
  4.         ; Получить имя и описание библиотеки
  5.         mov     eax, [pptlib]
  6.         mov     eax, [eax]
  7.         stdcall dword [eax+ITypeLib.GetDocumentation],[pptlib],\
  8.                 MEMBERID_NIL,strName,strDoc,NULL,NULL
  9.  
  10.         ; [strName] - указатель на строку названия библиотеки
  11.         ; [strDoc] - указатель на строку описания, может быть пустым
  12.  
  13.         ; Прибраться за собой
  14.         invoke  SysFreeString,[strName]
  15.         invoke  SysFreeString,[strDoc]
  16.  
  17.         ; Получить количество элементов
  18.         mov     eax, [pptlib]
  19.         mov     eax, [eax]
  20.         stdcall dword [eax+ITypeLib.GetTypeInfoCount],[pptlib]
  21.  
  22.         ; EAX - количество записей в библиотеке
Не забываем освобождать память, выделенную под строки названия и описания, система за нас это не сделает. Если количество записей в библиотеке нулевое, то ее дальнейшая обработка не требуется. Если данные есть, то запускаем основной цикл.
  1.         ; Обработать каждый элемент по очереди
  2.         mov     [num],0
  3. loc_loop:
  4.         ; Получить информацию о типе элемента
  5.         mov     eax, [pptlib]
  6.         mov     eax, [eax]
  7.         stdcall dword [eax+ITypeLib.GetTypeInfo],[pptlib],\
  8.                 [num],iInfo
  9.         ; Удалось определить тип?
  10.         or      eax,eax
  11.         ; Нет, переходим к следующему
  12.         jnz     loc_next
  13.  
  14.         ; Получить свойства элемента
  15.         mov     eax, [iInfo]
  16.         mov     eax, [eax]
  17.         stdcall dword [eax+ITypeInfo.GetTypeAttr],[iInfo],\
  18.                 pTypeAttr
  19.         ; Удалось получить свойства элемента?
  20.         or      eax,eax
  21.         ; Да, обрабатываем его
  22.         jz      @f
  23.  
  24.         ; Прибраться за собой
  25.         mov     eax, [iInfo]
  26.         mov     eax, [eax]
  27.         stdcall dword [eax+ITypeInfo.Release],[iInfo]
  28.         ; Переходим к следующему элементу
  29.         jmp     loc_next
  30. @@:
  31.         ;----------------------------------
  32.         ; Обработка элемента
  33.         ;----------------------------------
  34.  
  35. loc_clear:
  36.         ; Прибраться за собой
  37.         mov     eax, [iInfo]
  38.         mov     eax, [eax]
  39.         stdcall dword [eax+ITypeInfo.ReleaseTypeAttr],[iInfo],[pTypeAttr]
  40.  
  41.         mov     eax, [iInfo]
  42.         mov     eax, [eax]
  43.         stdcall dword [eax+ITypeInfo.Release],[iInfo]
  44.  
  45. loc_next:
  46.         ; Все элементы библиотеки обработаны?
  47.         inc     [num]
  48.         mov     eax,[num]
  49.         cmp     eax,[nCount]
  50.         jb      loc_loop
  51.  
  52.         ; Прибраться за собой
  53.         mov     eax, [pptlib]
  54.         mov     eax, [eax]
  55.         stdcall dword [eax+ITypeLib.Release],[pptlib]
Двумя последовательными вызовами методов GetTypeInfo и GetTypeAttr мы получаем, соответственно, указатель на интерфейс ITypeInfo, а затем через него указатель на структуру TYPEATTR в которой содержится информация об элементе. Если информацию получить не удалось, то освобождаем задействованные ресурсы и переходим к следующему элементу. Если все хорошо, то обрабатываем элемент.

C помощью уже знакомого нам метода GetDocumentation получаем название элемента и описание, если оно есть. Из структуры TYPEATTR извлекаем нужные поля, обязательно нужно значение typekind, а остальное на ваше усмотрение, в частности меня интересует еще поле GUID. Если это поле не пустое, то его можно преобразовать в строку при помощи штатной функции StringFromGUID2.
  1.         ; Получить имя и описание элемента
  2.         mov     eax, [pptlib]
  3.         mov     eax, [eax]
  4.         stdcall dword [eax+ITypeLib.GetDocumentation],[pptlib],\
  5.                 [num],strName,strDoc,NULL,NULL
  6.  
  7.         ; [strName] - указатель на строку названия элемента
  8.         ; [strDoc] - указатель на строку описания, может быть пустым
  9.  
  10.         ; Прибраться за собой
  11.         invoke  SysFreeString,[strName]
  12.         invoke  SysFreeString,[strDoc]
  13.  
  14.         ; Указатель на структуру TYPEATTR
  15.         mov     esi,[pTypeAttr]
  16.  
  17.         ; [esi+TYPEATTR.typekind] - тип элемента
  18.         ; [esi+TYPEATTR.guid] - GUID элемента, может быть пустым
  19.  
  20.         ;----------------------------------
  21.         ; Обработка элемента в зависимости
  22.         ; от его типа
  23.         ;----------------------------------
Дальнейшая обработка каждого элемента зависит от его типа. Начнем с наиболее простого - перечисление, ему соответствует значение тип элемента TKIND_ENUM. Для такого элемента важно проверить количество переменных, которые он содержит. Это значение хранится в поле cVars структуры TYPEATTR, если оно нулевое, то содержимое элемента обрабатывать дальше не надо.
  1.         ; Это перечисление?
  2.         cmp     [esi+TYPEATTR.typekind],TKIND_ENUM
  3.         jne     loc_not_enum
  4.  
  5.         ; Количество элементов в перечислении
  6.         movzx   eax,[esi+TYPEATTR.cVars]
  7.         or      eax,eax
  8.         jz      loc_not_enum
  9.  
  10.         mov     [num2],0
  11. loc_loop_enum:
  12.         ; Получить структуру VARDESC, описывающую переменную
  13.         mov     eax, [iInfo]
  14.         mov     eax, [eax]
  15.         stdcall dword [eax+ITypeInfo.GetVarDesc],[iInfo],\
  16.                 [num2],pVarDesc
  17.  
  18.         ; Указатель на структуру VARDESC
  19.         mov     edi,[pVarDesc]
  20.  
  21.         ; Получить название переменной
  22.         mov     eax, [iInfo]
  23.         mov     eax, [eax]
  24.         stdcall dword [eax+ITypeInfo.GetDocumentation],[iInfo],\
  25.                 [edi+VARDESC.memid],strName,NULL,NULL,NULL
  26.  
  27.         ; Получить значение переменной
  28.         mov     eax,[edi+VARDESC.lpvarValue]
  29.         mov     eax,[eax+VARIANT.lVal]
  30.  
  31.         ; [strName] - указатель на название переменной
  32.         ; EAX = значение переменной
  33.  
  34.         ; Прибраться за собой
  35.         invoke  SysFreeString,[strName]
  36.  
  37.         mov     eax, [iInfo]
  38.         mov     eax, [eax]
  39.         stdcall dword [eax+ITypeInfo.ReleaseVarDesc],[iInfo],[pVarDesc]
  40.  
  41.         ; Все значения в перечислении обработаны?
  42.         inc     [num2]
  43.         mov     eax,[num2]
  44.         cmp     ax,[esi+TYPEATTR.cVars]
  45.         jb      loc_loop_enum
Если какое-то количество переменных в перечислении имеется, то по очереди для каждой переменной при помощи метода GetVarDesc получаем ее описание в виде заполненной структуры VARDESC. Методом GetDocumentation узнаем название переменной, а из поля lVal структуры VARIANT получаем значение этой переменной. Не забываем освобождать ресурсы.

Элемент типа TKIND_ALIAS является псевдонимом для другого типа. Тут требуется узнать название этого самого другого типа. Для этого надо декодировать структуру TYPEDESC, которая содержится в поле tdescAlias структуры TYPEATTR. Функция декодирования будет описана ниже.
  1.         ; Это алиас?
  2.         cmp     [esi+TYPEATTR.typekind],TKIND_ALIAS
  3.         jne     loc_not_alias
  4.  
  5.         ; Тип алиаса
  6.         lea     eax,[esi+TYPEATTR.tdescAlias.lptdesc]
  7.         stdcall DecodeType,eax,strType
В элементе типа TKIND_COCLASS содержится информация об одном или нескольких интерфейсах. Перед обработкой надо проверить значение поля cImplTypes структуры TYPEATTR, если оно нулевое, то список интерфейсов пустой и обрабатывать его также не требуется. Для заполненного списка поочередно перебираем каждый его элемент и получаем информацию с помощью пары методов GetRefTypeOfImplType и GetRefTypeInfo. Название интерфейса получаем с помощью уже известного метода GetDocumentation.
  1.         ; Это класс?
  2.         cmp     [esi+TYPEATTR.typekind],TKIND_COCLASS
  3.         jne     loc_not_class
  4.  
  5.         ; Количество реализованных интерфейсов
  6.         movzx   eax,[esi+TYPEATTR.cImplTypes]
  7.         or      eax,eax
  8.         jz      loc_not_class
  9.  
  10.         mov     [num2],0
  11.  
  12. loc_loop_class:
  13.         ; Получить информацию об интерфейсе
  14.         mov     eax, [iInfo]
  15.         mov     eax, [eax]
  16.         stdcall dword [eax+ITypeInfo.GetRefTypeOfImplType],[iInfo],\
  17.                 [num2],pRefType
  18.  
  19.         mov     eax, [iInfo]
  20.         mov     eax, [eax]
  21.         stdcall dword [eax+ITypeInfo.GetRefTypeInfo],[iInfo],\
  22.                 [pRefType],pType2
  23.  
  24.         ; Получить название интерфейса
  25.         mov     eax,[pType2]
  26.         mov     eax,[eax]
  27.         stdcall dword [eax+ITypeInfo.GetDocumentation],[pType2],\
  28.                 MEMBERID_NIL,strRName,NULL,NULL,NULL
  29.  
  30.         ; [strRName] - название интерфейса
  31.  
  32.         ; Прибраться за собой
  33.         invoke  SysFreeString,[strRName]
  34.  
  35.         mov     eax,[pType2]
  36.         mov     eax,[eax]
  37.         stdcall dword [eax+ITypeInfo.Release],[pType2]
  38.  
  39.         ; Все интерфейсы обработаны?
  40.         inc     [num2]
  41.         mov     eax,[num2]
  42.         cmp     ax,[esi+TYPEATTR.cImplTypes]
  43.         jb      loc_loop_class
Самый объемный обработчик будет для элементов типов TKIND_INTERFACE, TKIND_DISPATCH, TKIND_RECORD и TKIND_UNION. При кажущейся разнице они, в принципе, имеют много общего по своей структуре. Интерфейсы могут иметь свойства и/или методы, тогда как структуры и объединения имеют только свойства. Чтобы упростить понимание, я разделю обработку свойств и методов. Начнем со свойств. Они очень похожи на переменные из перечислений, их количество также содержится в поле cVars структуры TYPEATTR. Если поле имеет нулевое значение, то свойств у элемента нет и можно переходить сразу к обработке методов. Обработка списка свойств очень напоминает обработку списка переменных перечисления. Разница только в том, что для переменной получается пара "имя = значение", а для свойств "имя : тип".
  1.         ; Это интерфейс?
  2.         cmp     [esi+TYPEATTR.typekind],TKIND_INTERFACE
  3.         je      @f
  4.         cmp     [esi+TYPEATTR.typekind],TKIND_DISPATCH
  5.         je      @f
  6.         ; Это структура или объединение?
  7.         cmp     [esi+TYPEATTR.typekind],TKIND_UNION
  8.         je      @f
  9.         cmp     [esi+TYPEATTR.typekind],TKIND_RECORD
  10.         jne     loc_not_func
  11. @@:
  12.         ; Количество свойств
  13.         movzx   eax,[esi+TYPEATTR.cVars]
  14.         or      eax,eax
  15.         jz      loc_no_prop
  16.  
  17.         mov     [num2],0
  18. loc_loop_prop:
  19.         ; Получить информацию о свойстве
  20.         mov     eax, [iInfo]
  21.         mov     eax, [eax]
  22.         stdcall dword [eax+ITypeInfo.GetVarDesc],[iInfo],\
  23.                 [num2],pVarDesc
  24.  
  25.         ; Указатель на структуру VARDESC
  26.         mov     edi,[pVarDesc]
  27.  
  28.         ; Название свойства и описание
  29.         mov     eax, [iInfo]
  30.         mov     eax, [eax]
  31.         stdcall dword [eax+ITypeInfo.GetDocumentation],[iInfo],\
  32.                 [edi+VARDESC.memid],strName,strDoc,NULL,NULL
  33.  
  34.         ; [strName] - указатель на строку названия свойства
  35.         ; [strDoc] - указатель на строку описания, может быть пустым
  36.  
  37.         ; Прибраться за собой
  38.         invoke  SysFreeString,[strName]
  39.         invoke  SysFreeString,[strDoc]
  40.  
  41.         ; Тип свойства
  42.         lea     eax,[edi+VARDESC.elemdescVar.tdesc]
  43.         stdcall DecodeType,eax,strType
  44.  
  45.         ; strType - строка с типом свойства
  46.  
  47.         mov     eax, [iInfo]
  48.         mov     eax, [eax]
  49.         stdcall dword [eax+ITypeInfo.ReleaseVarDesc],[iInfo],[pVarDesc]
  50.  
  51.         ; Все данные в перечислении обработаны?
  52.         inc     [num2]
  53.         mov     eax,[num2]
  54.         cmp     ax,[esi+TYPEATTR.cVars]
  55.         jb      loc_loop_prop
  56.  
  57. loc_no_prop:
Обработка методов выглядит следующим образом. Сперва проверяется поле cFuncs структуры TYPEATTR, если оно имеет нулевое значение, то методов нет и обработку элемента можно заканчивать. Иначе для каждого метода запрашиваем его свойства при помощи GetFuncDesc, в случае успешного выполнения мы получим заполненную структуру FUNCDESC, в которой содержится описание метода. Используем GetDocumentation для получения названия функции и ее описания (при наличии), поле invkind структуры FUNCDESC определяет тип вызова метода, а из поля elemdescFunc извлекаем тип возврата функции. Описание и возможные значения остальных полей смотрите в документации.
  1.         ; Количество методов
  2.         movzx   eax,[esi+TYPEATTR.cFuncs]
  3.         or      eax,eax
  4.         jz      loc_not_func
  5.  
  6.         mov     [num2],0
  7. loc_loop_func:
  8.         ; Получить информацию о методе
  9.         mov     eax, [iInfo]
  10.         mov     eax, [eax]
  11.         stdcall dword [eax+ITypeInfo.GetFuncDesc],[iInfo],\
  12.                 [num2],pFuncDesc
  13.  
  14.         ; Указатель на структуру FUNCDESC
  15.         mov     edi,[pFuncDesc]
  16.  
  17.         ; Получить имя метода и описание
  18.         mov     eax, [iInfo]
  19.         mov     eax, [eax]
  20.         stdcall dword [eax+ITypeInfo.GetDocumentation],[iInfo],\
  21.                 [edi+FUNCDESC.memid],strName,strDoc,NULL,NULL
  22.  
  23.         ; [strName] - указатель на строку названия метода
  24.         ; [strDoc] - указатель на строку описания, может быть пустым
  25.  
  26.         ; Прибраться за собой
  27.         invoke  SysFreeString,[strName]
  28.         invoke  SysFreeString,[strDoc]
  29.  
  30.         ; Тип возврата функции
  31.         lea     eax,[edi+FUNCDESC.elemdescFunc.tdesc]
  32.         stdcall DecodeType,eax,strType
  33.  
  34.         ; strType - строка с типом возврата
  35.  
  36.         ; У функции есть параметры?
  37.         cmp     [edi+FUNCDESC.cParams],0
  38.         je      func_no_params
  39.  
  40.         ; Получить названия всех параметров
  41.         mov     eax, [iInfo]
  42.         mov     eax, [eax]
  43.         stdcall dword [eax+ITypeInfo.GetNames],[iInfo],\
  44.                 [edi+FUNCDESC.memid],pnames,MAX_NAMES,tmp
  45.  
  46.         mov     [num3],0
  47. loc_loop_param:
  48.         ; Следующий параметр в массиве
  49.         mov     eax,[num3]
  50.         shl     eax,4
  51.         mov     ebx,[edi+FUNCDESC.lprgelemdescParam]
  52.         add     ebx,eax
  53.  
  54.         ; Какие-то флаги у параметра функции имеются?
  55.         cmp     word[ebx+ELEMDESC.paramdesc.wParamFlags],PARAMFLAG_NONE
  56.         je      loc_no_flags
  57.  
  58.         ;----------------------------------
  59.         ; Обработать флаги параметра
  60.         ;----------------------------------
  61.  
  62. loc_no_flags:
  63.         ; Получить название параметра из массива
  64.         mov     eax,[num3]
  65.         inc     eax
  66.         shl     eax,2
  67.         mov     eax,[pnames+eax]
  68.  
  69.         ; EAX - указатель на название параметра
  70.  
  71.         ; Получить тип параметра
  72.         stdcall DecodeType,ebx,strType
  73.  
  74.         ; strType - строка с типом параметра
  75.  
  76.         ; Все параметры функции обработаны?
  77.         inc     [num3]
  78.         mov     eax,[num3]
  79.         cmp     ax,[edi+FUNCDESC.cParams]
  80.         jb      loc_loop_param
  81.  
  82. func_no_params:
  83.         ; Прибраться за собой
  84.         mov     eax, [iInfo]
  85.         mov     eax, [eax]
  86.         stdcall dword [eax+ITypeInfo.ReleaseFuncDesc],[iInfo],[pFuncDesc]
  87.  
  88.         ; Все методы обработаны?
  89.         inc     [num2]
  90.         mov     eax,[num2]
  91.         cmp     ax,[esi+TYPEATTR.cFuncs]
  92.         jb      loc_loop_func
  93.  
  94. loc_not_func:
Как несложно догадаться, у метода скорее всего могут быть параметры. Их количество содержится в поле cParams структуры FUNCDESC. Если там ноль, то параметров нет и можно переходить к обработке следующей функции. Как ни странно, но названия параметров и их характеристики хранятся в разных местах. Для получения списка названий параметров надо воспользоваться методом GetNames, на выходе будет массив из указателей на строки с названиями. А характеристики параметров находятся в массиве из структур ELEMDESC, на который ссылается поле lprgelemdescParam структуры FUNCDESC. Для каждого элемента массива анализируется поле флагов wParamFlags, чтобы определить его тип.

Ну и наконец не раз упомянутая выше вспомогательная функция. Она декодирует данные, содержащиеся в структуре TYPEDESC, корректно обрабатываются частные случаи, такие как указатели на значение, указатели на указатели на значение, пользовательские типы данных и массивы. Для остальных значений типа VARIANT коды преобразовываются в их общепринятые текстовые названия. При появлении новых типов достаточно будет просто дополнить список кодов и их названий. В качестве параметров функции передаются указатель на структуру TYPEDESC и указатель на строку-приемник декодированных данных.
  1. ;------------------------------------------------------------
  2. ; Функция декодирования данных из структуры TYPEDESC
  3. ;------------------------------------------------------------
  4. proc  DecodeType ptdesc:DWORD,lpStr:DWORD
  5.         pusha
  6.         mov     esi,[ptdesc]
  7.         movzx   eax,[esi+TYPEDESC.vt]
  8.  
  9.         ; Это указатель?
  10.         cmp     eax,VT_INT_PTR
  11.         je      @f
  12.         cmp     eax,VT_UINT_PTR
  13.         je      @f
  14.         cmp     eax,VT_PTR
  15.         jne     .loc_no_ptr
  16. @@:
  17.         ; Рекурсивно вызвать функцию с указываемым типом данных
  18.         stdcall DecodeType,[esi+TYPEDESC.lptdesc],[lpStr]
  19.  
  20.         ; Добавить символ указателя
  21.         invoke  lstrcat,[lpStr],.ref
  22.         jmp     .loc_ret
  23.  
  24. .loc_no_ptr:
  25.         ; Это массив?
  26.         cmp     eax,VT_CARRAY
  27.         jne     .loc_no_carray
  28.  
  29.         ; ARRAYDESC
  30.         mov     esi,[esi+TYPEDESC.lpadesc]
  31.         stdcall DecodeType,esi,[lpStr]
  32.  
  33.         ; Перебрать строки массива
  34.         mov     [num4],0
  35.  
  36. .loc_loop_dim:
  37.         ; Количество элементов в строке массива
  38.         mov     eax,[num4]
  39.         shl     eax,4
  40.  
  41.         invoke  wsprintf,buff,szMask11,[esi+ARRAYDESC.rgbounds.cElements+eax]
  42.         add     esp,12
  43.  
  44.         invoke  lstrcat,[lpStr],buff
  45.  
  46.         ; Все строки массива обработали?
  47.         inc     [num4]
  48.         mov     eax,[num4]
  49.         cmp     ax,word [esi+ARRAYDESC.cDims]
  50.         jb      .loc_loop_dim
  51.  
  52.         jmp     .loc_ret
  53.  
  54. .loc_no_carray:
  55.         ; Это пользовательские данные?
  56.         cmp     eax,VT_USERDEFINED
  57.         jne     .loc_no_user
  58.  
  59.         ; Получить информацию о данных
  60.         mov     eax,[iInfo]
  61.         mov     eax,[eax]
  62.         stdcall dword [eax+ITypeInfo.GetRefTypeInfo],[iInfo],\
  63.                 [esi+TYPEDESC.hreftype],pRefType
  64.  
  65.         ; Получить название пользовательских данных
  66.         mov     eax,[pRefType]
  67.         mov     eax,[eax]
  68.         stdcall dword [eax+ITypeInfo.GetDocumentation],[pRefType],\
  69.                 MEMBERID_NIL,strRName,NULL,NULL,NULL
  70.  
  71.         invoke  lstrcat,[lpStr],[strRName]
  72.  
  73.         ; Прибраться за собой
  74.         invoke  SysFreeString,[strRName]
  75.  
  76.         mov     eax,[pRefType]
  77.         mov     eax,[eax]
  78.         stdcall dword [eax+ITypeInfo.Release],[pRefType]
  79.  
  80.         jmp     .loc_ret
  81.  
  82. .loc_no_user:
  83.         ; Преобразовать код VT_xx в текстовый вид
  84.         movzx   ebx,[esi+TYPEDESC.vt]
  85.         mov     esi,.vtable
  86. .loc_loop:
  87.         lodsd
  88.         cmp     eax,0xFFFFFFFF
  89.         je      @f
  90.         cmp     eax,ebx
  91.         je      .loc_set
  92.         invoke  lstrlen,esi
  93.         inc     eax
  94.         shl     eax,1
  95.         add     esi,eax
  96.         jmp     .loc_loop
  97. @@:
  98.         mov     esi,.error
  99. .loc_set:
  100.         invoke  lstrcat,[lpStr],esi
  101.  
  102. .loc_ret:
  103.         popa
  104.         ret
  105.  
  106. .ref    du '*',0
  107.  
  108. .error  du 'Unknown type',0
  109.  
  110. .vtable dd VT_EMPTY
  111.         du 'void',0
  112.         dd VT_NULL
  113.         du 'NULL',0
  114.         dd VT_I2
  115.         du 'short',0
  116.         dd VT_I4
  117.         du 'long',0
  118.         dd VT_R4
  119.         du 'single',0
  120.         dd VT_R8
  121.         du 'double',0
  122.         dd VT_CY
  123.         du 'CURRENCY',0
  124.         dd VT_DATE
  125.         du 'DATE',0
  126.         dd VT_BSTR
  127.         du 'BSTR',0
  128.         dd VT_DISPATCH
  129.         du 'IDispatch*',0
  130.         dd VT_ERROR
  131.         du 'SCODE',0
  132.         dd VT_BOOL
  133.         du 'BOOL',0
  134.         dd VT_VARIANT
  135.         du 'VARIANT',0
  136.         dd VT_UNKNOWN
  137.         du 'IUnknown*',0
  138.         dd VT_I1
  139.         du 'char',0
  140.         dd VT_UI1
  141.         du 'unsigned char',0
  142.         dd VT_UI2
  143.         du 'unsigned short',0
  144.         dd VT_UI4
  145.         du 'unsigned long',0
  146.         dd VT_I8
  147.         du 'int64',0
  148.         dd VT_UI8
  149.         du 'uint64',0
  150.         dd VT_INT
  151.         du 'int',0
  152.         dd VT_UINT
  153.         du 'unsigned int',0
  154.         dd VT_VOID
  155.         du 'void',0
  156.         dd VT_HRESULT
  157.         du 'HRESULT',0
  158.         dd VT_PTR
  159.         du 'void*',0
  160.         dd VT_SAFEARRAY
  161.         du 'SAFEARRAY',0
  162.         dd VT_CARRAY
  163.         du 'CARRAY',0
  164.         dd VT_USERDEFINED
  165.         du 'USERDEFINED',0
  166.         dd VT_LPSTR
  167.         du 'LPSTR',0
  168.         dd VT_LPWSTR
  169.         du 'LPWSTR',0
  170.         dd VT_FILETIME
  171.         du 'FILETIME',0
  172.         dd VT_BLOB
  173.         du 'BLOB',0
  174.         dd VT_STREAM
  175.         du 'STREAM',0
  176.         dd VT_STORAGE
  177.         du 'STORAGE',0
  178.         dd VT_STREAMED_OBJECT
  179.         du 'STREAMED_OBJECT',0
  180.         dd VT_STORED_OBJECT
  181.         du 'STORED_OBJECT',0
  182.         dd VT_BLOB_OBJECT
  183.         du 'BLOB_OBJECT',0
  184.         dd VT_CF
  185.         du 'CF',0
  186.         dd VT_CLSID
  187.         du 'CLSID',0
  188.         dd VT_VERSIONED_STREAM
  189.         du 'VERSIONED_STREAM',0
  190.         dd VT_STORED_PROPSET
  191.         du 'STORED_PROPSET',0
  192.         dd VT_BLOB_PROPSET
  193.         du 'BLOB_PROPSET',0
  194.         dd VT_VERBOSE_ENUM
  195.         du 'VERBOSE_ENUM',0
  196.         dd 0xFFFFFFFF
  197. endp
С помощью приведенного в статье кода спокойно разбираются библиотеки, на которых дают сбои даже именитые инструменты. Описаны методы получения данных всех возможных типов, этого должно хватить как для полноценной работы с библиотеками типов из своего приложения, так и для написания собственных анализаторов TypeLib. В зависимости от решаемой задачи, вы можете подкорректировать текстовые названия типов под целевой язык программирования, а также проанализировать другие поля описанных структур, чтобы получить из них более расширенную информацию об элементах библиотеки и их составляющих.

В приложении пример программы с исходным текстом, которая разбирает данные из библиотеки TypeLib и выводит их в текстовом виде.

Пример программы с исходным текстом (FASM)Пример программы с исходным текстом (FASM)

TypeLib.Demo.zip (245,192 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 214 | Комментариев: 0

Метки: Assembler, COM

Комментарии

Отзывы посетителей сайта о статье
Комментариeв нет

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2025
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.07 сек. / MySQL: 2 (0.0033 сек.) / Память: 4.5 Mb
Наверх