* Статья с блога Крейга Бойда ч.2 (в моем переводе)
* 25 сентября 2007 г.
* www.sweetpotatosoftware.com
*
* Файлы PE, UAC, Reg-free COM, и Другой Сумасшедший Материал - Часть 2
* В прошлый Раз
В части 1 этой статьи я показал в общих чертах, что я вообще собираюсь показать, а также дал некоторую краткую
информацию относительно формата PE. В конце я привел некоторый код, который позволяет разработчику Visual FoxPro
проходить по некоторым из структур PE и данным. В этой же записи в блоге, я собираюсь показать вам, как вы можете
отредактировать manifest в Файле PE, и как это может облегчить установку разрешений в Vista UAC и Reg-Free COM.
* Manifest-ы в PE-файлах Visual FoxPro
Некоторое время назад Visual FoxPro Team начала добавлять информацию о конфигурации в качестве ресурса в Visual
FoxPro DLL и EXE-файлах. Эта информация о конфигурации - фактически Манифест Приложения (Application Manifest).
Давайте бросим беглый взгляд на один пример. Загрузите нижеприведенный код, и укажите в качестве выбора любой
файл VFP - EXE или DLL.
Declare Long LoadLibrary In WIN32API String
Declare Long FindResource In WIN32API Long, Long, Long
Declare Long LoadResource In WIN32API Long, Long
Declare Long SizeofResource In WIN32API Long, Long
Declare Long FreeLibrary In WIN32API Long
Declare Long FreeResource In WIN32API Long
Local lcModule, hModule
m.lcModule = Getfile("EXE|DLL")
m.hModule = LoadLibrary(m.lcModule)
m.lnRsrc = FindResource(m.hModule, 1, 24)
m.lnMem = LoadResource(m.hModule, m.lnRsrc)
m.lnSize = SizeofResource(m.hModule, m.lnRsrc)
m.lcManifest = Sys(2600, m.lnMem, m.lnSize)
FreeResource(m.lnMem)
FreeLibrary(m.hModule)
?m.lcManifest
Вы должны увидеть что-то наподобие такого:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
type="win32"
name="Microsoft.VisualFoxPro"
processorArchitecture="x86"
/>
<description>Visual FoxPro</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
language="*"
processorArchitecture="x86"
publicKeyToken="6595b64144ccf1df"
/>
</dependentAssembly>
</dependency>
</assembly>
* Visual FoxPro 9.0 SP2
Теперь, те из вас, кто выбрали EXE или DLL, построенный в VFP9 SP2, отметят, что ваш manifest xml -
не то же самое, что я показал выше. В VFP9 SP2 он имеет дополнительный узел trustinfo, который подобен:
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="asInvoker"
uiAccess="false"/>
</requestedPrivileges>
</security>
Этот trustinfo-узел используется для безопасного связывания, и определяет способ, которым контроль за
учетной записью пользователя на Vista реагирует на начало вашего PE-файла, и что ему дозволяется делать, когда он
загружается. Отметьте узел requestedExecutionLevel, и особенно атрибут уровня, который он содержит: "asInvoker" -
и означает в основном - загружать ли ваше приложение с наименьшим количеством возможных привелегий (его фактически
назвали когда-то leastPrivelege - если не изменяет память).
Т.е., если ваши пользователи не щелкают правой кнопкой мыши на вашем приложении, выбирая "Загрузить от имени
Администратора", или создавая ярлык вашего приложения, который позволяет Vista всегда запускать его от имени
Администратора, то ваше приложение предполагает быть очень ограниченным в том, что оно может сделать в Vista.
Фактически это вроде бы и хорошо, но... чуть больше об этом - через мгновение.
Ведь по большей части именно это и становится реальным геммороем и одной из главных жалоб, одинаковых и от
разработчиков и от пользователей в Vista.
* Так мы можем Изменить manifest?
Да, конечно же, ведь Microsoft встроил в Visual Foxpro Project Info или через диалоги Project Build возможность
изменения этих настроек уровня выполнения, ведь так?
Нет. Не так. До самой последней Беты SP2 я так и не увидел ничего подобного. А теперь это им уже и не обязательно,
тем более, что они забросили нас (хотя соглашусь, что неплохо было бы иметь такую настройку). Вероятная причина,
по которой они не сделали этого, состоит в том, что Microsoft постановил, что Сертификат для Windows Vista требует,
чтобы приложения были помечены asInvoker. Если вы должны выполнить задачи, которые требуют более высокого уровня
привелегий, то вам необходимо запросить эти привелегии - как в примерах класса по установке разрешений VFP COM
сервера, который вы создавали. Более подробно о требованиях для UAC, можно прочитать в документе Word, загружаемом
с Microsoft.com:
download.microsoft.com
Ну так что же, означает ли все это, что мы никак не можем поднять уровень выполнения, изменяя manifest? Нет.
Вы все-таки можете сделать это. Только это не тот способ, которым Microsoft хочет, чтобы вы делали это, и даже
не молитесь о получении одной из тех миленьких Эмблем Vista для вашего приложения, если поступите так.
Да и вообще не молитесь об этом, если только вы не один из тех немногих счастливчиков, которым предоставляется
специальное исключение от Microsoft, потому что вы в состоянии обсудить с ними свой случай достаточно хорошо.
Тут я отступаю...
Если же вы решите изменить уровень, тогда у вас есть две возможности: highestAvailable и requireAdministrator.
Но проблема состоит в том, что, даже если вы знаете их, в настоящее время нет никакого средства в Visual FoxPro,
чтобы изменить их. Давайте рассмотрим решение, которое исследовал Кэлвин Хсиа (Calvin Hsia) в одной из записей
своего блога:
[url]http://blogs.msdn.com/b/calvin_hsia/archive/2007/04/13/add-a-manifest-to-control-your-application-vista-uac-behavior.aspx[/url]
* Изменение манифеста
Если бы вы почитали блог Кэлвина, то отметили бы, что между прочим он в основном использует вызов UpdateResource
API и делает какие-то странные махинации с концом манифеста FoxPro. UpdateResource API как раз и делает то, что и
подразумевает ее имя. Она обновляет ресурс в файле PE, а так как манифест - ресурс в приложении Visual FoxPro 9.0,
то все это вполне может использоваться, чтобы обновить манифест.
Только что же такое проделывает Кэлвин, когда идет до конца файла, выделяя пары 14-байтовых структур?
Хорошо, что рядом Bo Durban, и я могу сделать вывод, что те две структуры в конце файла PE Visual FoxPro используются
для указания на компиляцию приложения VFP и некоторую информацию typelib/registration в пределах выполнения.
Вы, возможно, слышали когда-то, что VFP PE отличается от нормального PE тем, что в нем реально выполняется
только рантайм библиотека VFP, которая и загружает внутренние FXP или APP.
Так что похоже есть некоторая правда в данном коде Кэлвина. В любом случае, он разбирает на составляющие собственно
материал VFP, чтобы защитить его, затем когда UpdateResource произведен, завершая свой замысел, он возвращает их
назад (хотя я полагаю, он делает маленькую ошибку, откладывая материал назад). Довольно гладко, не так ли?
Так, при использовании техники, которую обрисовывал в общих чертах Кэлвин, мы можем заменить манифест в приложении
Visual FoxPro, и таким образом изменить способ, которым наше приложение ведет себя под UAC в Vista.
Эта техника работает и на VFP9 SP1, так же как на SP2, и в этом смысле все работало бы и на предыдущих версиях
Visual FoxPro также, но все же лучше, если вы модернизируетесь до VFP9 и тогда отправитесь вместе с нами в эту
поездку.
[attachment 12122 staiya_3_pic.jpg]
Выше (слева-направо):
1) Скриншот PE-файла Visual FoxPro на Vista Ultimate с уровнем выполнения, помеченным как "asInvoker".
2) Скриншот PE-файла Visual FoxPro на Vista Ultimate с уровнем выполнения, помеченным как "requireAdministrator"
путем модификации манифеста, вложенного в exe. Обратите внимание на "значок щита", который Vista добавляет к
значку exe, чтобы обозначить, что он требует администраторских привелегий.
3) Автоматический диалог, который Vista Ultimate показывает, когда exe отмечен с "requireAdministrator"
уровнем выполнения. Пользователю больше не нужно щелкать правой кнопкой мыши на exe, чтобы загружать его от имени
Администратора. Повышенные разрешения будут затребованы, как только exe начнет выполняться.
* Reg-Free COM
В дополнение к изменению уровня выполнения вашего приложения на Vista UAC Вы можете также использовать эту
технику, чтобы облегчить Reg-free COM в Windows XP и Vista. Reg-free COM разработан, чтобы облегчить DLL-Hell
определенных версий и выполнение на местах. Кроме того, если вы собираетесь использовать развертывание
ClickOnce в будущем, это поможет, так как ClickOnce не регистрирует компоненты COM. Об этом я буду говорить
на сессии по ClickOnce на Southwest Fox в этом году:
www.sweetpotatosoftware.com
Я был бы более, чем счастлив, более подробно побеседовать некоторое время с любым, кто изучает этот материал,
(блог не всегда способствует этому в должной мере), так, если вы предполагаете быть свободны 18-ого октября,
заскакивайте и регистрируйтесь на конференции:
www.sweetpotatosoftware.com
и я рад буду видеть вас там.
Так вот, как я и говорил прежде, бесстыдно заявляя о "супер-удивительном, которое не будет пропущено" на
конференции Southwest Fox, мы можем фактически использовать манифест приложения для размещения в наших приложениях
Visual FoxPro информации, которую обычно необходимо прописывать в реестр, регистрируя наши компоненты COM
через regsvr32. На что это похоже? Ну, точно так же, как проблемы безопасности были решены, добавлением узла
"trustInfo", Reg-free материал COM становится возможным, добавлением узла "file".
Давайте посмотрим, на что это похоже...
<file name="regfree.dll">
<comClass description="regfree.adder"
clsid="{99A4ADD8-5A6A-40C1-89FF-50B5E8267B69}"
progid="regfree.adder"
threadingModel="Both"/>
</file>
Как Вы можете видеть, в этом XML есть признак имени узла файла, в данном случае это "regfree.dll", в котором
есть вложенный (дочерний) узел, названный comClass, где находятся описание (description), clsid, progid, и
информация threadingModel для класса OLEPublic. Это - все, что есть там. Так что, когда все это добавляется к
предыдущим модификациям безопасности, вы должны получить манифест, который несколько походит на следующий...
<?xml version="1.0" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0" type="win32" name="Microsoft.VisualFoxPro" processorArchitecture="x86"/>
<description>Visual FoxPro</description>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" language="*" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df"/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<file name="regfreedll.dll">
<comClass description="regfreedll.adder" clsid="{99A4ADD8-5A6A-40C1-89FF-50B5E8267B69}" progid="regfreedll.adder" threadingModel="Both"/>
</file>
</assembly>
Немного грязновато, но вы видите основную идею, а я съэкономлю сколько-то времени, не прибегая к необходимости
форматировать этот XML, который я вытащил из одного из моих VFP exe-шников. Теперь, с манифестом в приложении
мне не нужно регистрировать некую regfree.dll ни на XP, ни в Vista. Ну только, если религия запрещает пользоваться
таким, запуская непременно под Windows 2000 или Windows 98, тогда мой dll конечно придется регистрировать.
Regfree.dll содержит только один класс, класс Adder, но если бы он содержал больше, тогда я вставил бы
дополнительные comClass узлы для каждого из классов, которые использовал бы в своем Exe, а если бы у меня было
больше, чем один DLL или OCX, тогда я добавил бы больше узлов file. Таким образом, у вас и получается Reg-free COM.
* Манифест приложения Модификации Класса
Так как у нас нет способа проделать все это непосредственно в Visual FoxPro, думаю, я создам класс, чтобы было
проще обращаться со всем этим. По-видимому, класс может быть расширен в соединении с hook-ами проекта и гладким
интерфейсом, чтобы реально дать нам профессиональный способ обработки всего нужного в пределах Visual FoxPro IDE.
Кроме того, этот класс будет работать с другими Файлами PE, у него есть некоторый специфический код VFP для этого,
но он выполнится, только если файл PE, который вы изменяете, был собран в VFP.
Например, вы можете применять его для vfp9.exe, чтобы заставить всегда загружаться от имени Администратора под
Vista... Что было бы ну уже совсем против соглашения EULA, так что, пожалуйста, не делайте такого.
Заметьте, я лишь говорю: вот если бы это не было против EULA, то вы могли бы делать что-то подобное.
* Далее
В моей следующей записи в блоге я покажу вам, как управлять строковой таблицей ресурсов в Файле PE, используя
Visual FoxPro, а также покажу, как вы можете использовать новое именование повышения COM и CoGetObject, чтобы
облегчить приведенные классы, которые требуют повышенных разрешений для приложения Visual FoxPro, загруженного
asInvoker. Это позволит разработчикам Visual FoxPro запускать приложения, как это для них нужно, на Windows
Vista, и лишь повышать разрешения в меру необходимости. Давая больше совместимости с Vista и власти разработчику.
* Выше головы!
Скоро я собираюсь показать минифильтр, который я написал в C с дополнительным DLL, который я написал в C ++,
он позволит разработчикам Visual FoxPro полностью шифровать и расшифровывать данные Visual FoxPro на лету.
Он будет несколько походить на Cryptor, делая это, как я полагаю более безопасно, ну и конечно же, он будет
свободно распространяемым. После этого мы будем рассматривать использование Элементов ActiveX, таких как MSChart,
непосредственно в отчете, используя FLL, который я написал с Bo Durban, который один из моих клиентов, достаточно
хорошо относящийся к моим акциям, и затем мы создадим и ещё некоторый другой сумасшедший материал.
Но прямо сейчас я собираюсь посходить с ума некоторое время на материале Vista. У нас есть много другого материала,
чтобы покрыть в этом материале UAC прежде, чем я покажу вам все части, как они соединяются, и код, который мы
должны использовать затем.
Так что, я завершаю и предлагаю вам код прикладного класса модификатора манифеста (достаточно скопировать-
вставить его в prg, загрузить, и выбрав приложение VFP, в котором вы хотите изменить манифест - если собираетесь
добавить сервер COM, затем убедиться, что изменили путь и имя в вызове метода AddCOMModule для вашего фактического
файла).
* Модификация манифеста приложения и код примера
*!* пример использования
LOCAL loModuleResourceEditor
m.loModuleResourceEditor = CREATEOBJECT("ModuleResourceEditor")
m.loModuleResourceEditor.Module = GETFILE("EXE|DLL|OCX|CPL")
m.loModuleResourceEditor.ExecutionLevel = 3 && требование Администратора
?m.loModuleResourceEditor.AddCOMModule("D:\Documents and Settings\Craig Boyd\Desktop\RegFreeComTest\regfreedll.dll")
?m.loModuleResourceEditor.CreateNewModuleManifest()
?m.loModuleResourceEditor.ApplyNewManifest()
?m.loModuleResourceEditor.AddStringToStringTable("RegFreeDll Adder",101)
DEFINE CLASS ModuleResourceEditor AS CUSTOM
DIMENSION aCOMFiles(1) && массив, проводящий добавление COM модулей в манифест для REG-Free COM
COMFileCount = 0 && используется внутренне объявление aCOMFiles
ExecutionLevel = 1 && asInvoker по умолчанию
Module = "" && EXE/OCX/DLL/и т.п. те, чьи манифесты будут модифицированы
Manifest = "" && новый манифест
DefaultManifest = "" && манифест, используемый, если манифест не найден в модуле
LastError = 0 && номер ошибки, возвращаемый GetLastError()
*******************************
FUNCTION Init()
*******************************
this.aCOMFiles(1) = .F.
ENDFUNC
*******************************
FUNCTION AddCOMModule(tcCOMModuleName)
*******************************
IF FILE(m.tcCOMModuleName)
this.COMFileCount = this.COMFileCount + 1
DIMENSION this.aCOMFiles(this.COMFileCount)
this.aCOMFiles(this.COMFileCount) = m.tcCOMModuleName
=ASORT(This.aCOMFiles,1,-1,0,1)
RETURN .T.
ELSE
RETURN .F.
ENDIF
ENDFUNC
*******************************
FUNCTION RemoveCOMModule(tcCOMModuleName)
*******************************
LOCAL lnFoundAt
m.lnFoundAt = ASCAN(this.aCOMFiles, m.tcCOMModuleName)
IF m.lnFoundAt > 0
this.aCOMFiles(m.lnFoundAt) = .F.
ENDIF
ENDFUNC
*******************************
FUNCTION ApplyNewManifest()
*******************************
#DEFINE RT_MANIFEST 24
LOCAL hFile, lnPosition, lnSize, hModule, lcStruct, lnCounter, ;
llVFPModule, lnResult, llReturn, lcManifestXML
LOCAL ARRAY laVFPSections[2]
m.llReturn = .F.
IF !FILE(This.Module)
*!* свойства модуля не содержатся в файле или не были установлены.
RETURN m.llReturn
endif
IF Empty(This.Manifest)
*!* свойства манифеста не были установлены.
RETURN m.llReturn
endif
SET STEP ON
DECLARE INTEGER BeginUpdateResource IN WIN32API STRING, INTEGER
DECLARE INTEGER EndUpdateResource IN WIN32API INTEGER, INTEGER
DECLARE INTEGER UpdateResource IN WIN32API INTEGER, INTEGER, INTEGER, INTEGER, STRING @, INTEGER
DECLARE INTEGER GetLastError IN WIN32API
m.llVFPModule = .T. && принять этот модуль как VFPModule
*!* защищенная часть APP и инфа Typelib/Registration
m.hFile = FOPEN(This.Module)
m.lnPosition = FSEEK(m.hFile,0,2) && Go EOF and Get Size
FOR m.lnCounter = 1 TO 2
=FSEEK(m.hFile, m.lnPosition - 14, 0)
m.lcStruct = FREAD(m.hFile, 14)
m.lnSize = CTOBIN(SUBSTR(m.lcStruct, 11, 4), "4sr") && последние 4 байта содержат размер
IF m.lnSize > m.lnPosition OR m.lnSize < 0 OR (m.lnCounter = 1 AND m.lnSize = 0)
m.llVFPModule = .F.
EXIT
ENDIF
IF m.lnSize != 0
=FSEEK(m.hFile, m.lnPosition - m.lnSize, 0)
m.laVFPSections[m.lnCounter] = FREAD(m.hFile, m.lnSize)
IF m.lnCounter = 1 AND LEFT(m.laVFPSections[1],3) != 0hFEF2FF
m.llVFPModule = .F.
EXIT
ENDIF
ELSE
m.laVFPSections[m.lnCounter] = m.lcStruct
ENDIF
m.lnPosition = m.lnPosition - m.lnSize
ENDFOR
=FCLOSE(m.hFile)
*!* обновление манифеста в модуле
m.hModule = BeginUpdateResource(This.Module, 0)
m.lcManifestXML = This.Manifest
m.lnResult = UpdateResource(m.hModule, RT_MANIFEST, 1, 1033, @m.lcManifestXML, LEN(m.lcManifestXML))
IF EndUpdateResource(m.hModule, 0) = 0
this.LastError = TRANSFORM(GetLastError())
ELSE
m.llReturn = .T.
ENDIF
IF m.llVFPModule
*!* восстановлдение части APP и Typelib
m.hFile = FOPEN(This.Module, 2)
m.lnPosition = FSEEK(m.hFile, 0, 2)
FOR m.lnCounter = 2 TO 1 STEP -1
=FWRITE(m.hFile, m.laVFPSections[m.lnCounter])
ENDFOR
=FCLOSE(m.hFile)
ENDIF
RETURN m.llReturn
ENDFUNC
***********************
FUNCTION AddStringToStringTable(tcString, tnStringIndex) && проход -1 для tnStringIndex добавить в конец строковой таблицы
***********************
#DEFINE RT_STRING 6
LOCAL hFile, lnPosition, lnSize, hModule, lcStruct, ;
lnCounter, llVFPModule, lnResult, lnReturn, lcBaseStringTable, lcNewStringTable
LOCAL ARRAY laVFPSections[2]
m.lnReturn = -1
IF !FILE(This.Module)
*!* свойства модуля не появились в файле или не были установлены.
RETURN m.lnReturn
endif
IF Empty(m.tcString)
*!* параметры не были установлены.
RETURN m.lnReturn
endif
DECLARE INTEGER BeginUpdateResource IN WIN32API STRING, INTEGER
DECLARE INTEGER EndUpdateResource IN WIN32API INTEGER, INTEGER
DECLARE INTEGER UpdateResource IN WIN32API INTEGER, INTEGER, INTEGER, INTEGER, STRING @, INTEGER
DECLARE INTEGER GetLastError IN WIN32API
m.llVFPModule = .T. && принять этот модуль как VFPModule
*!* защищенная часть APP и Typelib
m.hFile = FOPEN(This.Module)
m.lnPosition = FSEEK(m.hFile,0,2) && Go EOF and Get Size
FOR m.lnCounter = 1 TO 2
=FSEEK(m.hFile, m.lnPosition - 14, 0)
m.lcStruct = FREAD(m.hFile, 14)
m.lnSize = CTOBIN(SUBSTR(m.lcStruct, 11, 4), "4sr") && последние 4 байта содержат размер
IF m.lnSize > m.lnPosition OR m.lnSize < 0 OR (m.lnCounter = 1 AND m.lnSize = 0)
m.llVFPModule = .F.
EXIT
ENDIF
IF m.lnSize != 0
=FSEEK(m.hFile, m.lnPosition - m.lnSize, 0)
m.laVFPSections[m.lnCounter] = FREAD(m.hFile, m.lnSize)
IF m.lnCounter = 1 AND LEFT(m.laVFPSections[1],3) != 0hFEF2FF
m.llVFPModule = .F.
EXIT
ENDIF
ELSE
m.laVFPSections[m.lnCounter] = m.lcStruct
ENDIF
m.lnPosition = m.lnPosition - m.lnSize
ENDFOR
=FCLOSE(m.hFile)
SET STEP ON
m.lcBaseStringTable = This.GetStringResource(1, RT_STRING)
m.lnLastStringIndex = This.GetStringTableCount(m.lcBaseStringTable)
DO case
CASE m.tnStringIndex < 0
m.tcString = This.GetFormattedStringForStringTable(m.tcString)
m.lcNewStringTable = m.lcBaseStringTable + m.tcString
CASE m.tnStringIndex > m.lnLastStringIndex
m.tcString = This.GetFormattedStringForStringTable(m.tcString)
m.lcNewStringTable = m.lcBaseStringTable + REPLICATE(0h0000, m.tnStringIndex - m.lnLastStringIndex) + m.tcString
OTHERWISE
m.lcNewStringTable = This.ReplaceStringTableString(m.lcBaseStringTable, m.tnStringIndex, m.tcString)
ENDCASE
*!* обновить строковую таблицу в модуле
m.hModule = BeginUpdateResource(This.Module, 0)
m.lnResult = UpdateResource(m.hModule, RT_STRING, 1, 1033, @m.lcNewStringTable, LEN(m.lcNewStringTable))
IF EndUpdateResource(m.hModule, 0) = 0
this.LastError = TRANSFORM(GetLastError())
ELSE
m.lnReturn = IIF(m.tnStringIndex < 0, m.lnLastStringIndex + 1, m.tnStringIndex)
ENDIF
IF m.llVFPModule
*!* восстановить часть APP и Typelib
m.hFile = FOPEN(This.Module, 2)
m.lnPosition = FSEEK(m.hFile, 0, 2)
FOR m.lnCounter = 2 TO 1 STEP -1
=FWRITE(m.hFile, m.laVFPSections[m.lnCounter])
ENDFOR
=FCLOSE(m.hFile)
ENDIF
RETURN m.lnReturn
ENDFUNC
***********************
FUNCTION CreateNewModuleManifest()
***********************
#DEFINE RT_MANIFEST 24
LOCAL lcBaseManifest, lcLevelXML, lcModuleName, lnCounter, ;
lcProgID, lcNameSpace, lcFileXML, lccomClassXML, lnClassCounter, ;
loDOM AS MSXML2.DOMDocument, loParentNode AS MSXML2.IXMLDOMELEMENT, ;
loNode AS MSXML2.IXMLDOMELEMENT, loNewLevelNode AS MSXML2.IXMLDOMELEMENT, ;
loTempNode AS MSXML2.IXMLDOMELEMENT, loTypeLib AS TLI.TypeLibInfo
IF !FILE(This.Module)
*!* свойства модуля не появились в файле или не были установлены.
RETURN .F.
ENDIF
This.Manifest = ""
*!* взять манифест из модуля или создать один
m.lcBaseManifest = This.GetStringResource(1, RT_MANIFEST)
IF EMPTY(m.lcBaseManifest)
IF EMPTY(this.DefaultManifest)
m.lcBaseManifest = This.GetDefaultManifest()
ELSE
m.lcBaseManifest = this.DefaultManifest
ENDIF
ENDIF
m.loDOM = CREATEOBJECT("MSXML2.DOMDocument.4.0")
IF m.loDOM.LOADXML(m.lcBaseManifest)
m.lcNameSpace = m.loDOM.firstChild.NEXTSIBLING.getAttribute("xmlns")
*!* добавить или заменить уровень выполнения
IF TYPE("This.ExecutionLevel") = "N"
m.loNode = m.loDOM.selectSingleNode('//*[local-name()="trustInfo"]')
IF !ISNULL(m.loNode)
m.loParentNode = m.loNode.parentNode
m.loParentNode.removeChild(m.loNode)
ENDIF
m.lcLevelXML = This.GetExecutionLevelXML()
m.loNewLevelNode = This.GetNodeFromXML(m.lcLevelXML, m.lcNameSpace)
IF !ISNULL(m.loNewLevelNode)
m.loDOM.selectSingleNode('//*[local-name()="assembly"]').appendChild(m.loNewLevelNode)
ENDIF
ENDIF
m.loDOM.setProperty("SelectionNamespaces", "xmlns:pe='" + m.lcNameSpace + "'")
*!* добавить или заменить Reg-Free COM
IF TYPE("This.aCOMFiles",1) = "A"
m.loTypeLib = CREATEOBJECT("TLI.TypeLibInfo")
IF TYPE("m.loTypeLib") = "O"
FOR m.lnCounter = 1 TO ALEN(This.aCOMFiles,1)
IF TYPE("This.aCOMFiles(m.lnCounter)") = "C" AND FILE(This.aCOMFiles(m.lnCounter))
m.loTypeLib.ContainingFile = This.aCOMFiles(m.lnCounter)
FOR m.lnClassCounter = 1 TO m.loTypeLib.TypeInfoCount
m.loTypeInfo = m.loTypeLib.TypeInfos(m.lnClassCounter)
IF m.loTypeInfo.TypeKind = 5
m.lcModuleName = JUSTFNAME(m.loTypeLib.ContainingFile)
m.lcProgID = m.loTypeLib.NAME + "." + m.loTypeLib.TypeInfos(m.lnClassCounter).NAME
m.loNode = m.loDOM.selectSingleNode('//*[local-name()="file"][translate(@name,"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")="' + LOWER(m.lcModuleName) + '"]')
IF ISNULL(m.loNode)
m.lcFileXML = This.GetCOMFileXML(m.lcModuleName)
m.loNewLevelNode = This.GetNodeFromXML(m.lcFileXML, m.lcNameSpace)
IF !ISNULL(m.loNewLevelNode)
m.loNode = m.loDOM.selectSingleNode('//*[local-name()="assembly"]').appendChild(m.loNewLevelNode)
ENDIF
ELSE
FOR EACH loTempNode IN m.loNode.childNodes
IF m.loTempNode.nodeName = "comClass"
IF LOWER(m.loTempNode.getAttribute("progid")) = LOWER(m.lcProgID)
m.loTempNode.parentNode.removeChild(m.loTempNode)
EXIT
ENDIF
ENDIF
NEXT
ENDIF
m.lccomClassXML = This.GetcomClassXML(m.lcModuleName, m.lcProgID, m.loTypeInfo.GUID, m.loTypeInfo.HELPSTRING)
m.loNewLevelNode = This.GetNodeFromXML(m.lccomClassXML, m.lcNameSpace)
IF !ISNULL(m.loNewLevelNode)
m.loNode.appendChild(m.loNewLevelNode)
ENDIF
ENDIF
ENDFOR
ENDIF
ENDFOR
ENDIF
ENDIF
This.Manifest = m.loDOM.XML
ENDIF
RETURN .T.
ENDFUNC
***********************
FUNCTION GetDefaultManifest()
***********************
LOCAL lcReturn
TEXT TO m.lcReturn textmerge noshow
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
type="win32"
name="<<tcModuleName>>"
processorArchitecture="x86"/>
<description><<tcModuleName>></description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
language="*"
processorArchitecture="x86"
publicKeyToken="6595b64144ccf1df"/>
</dependentAssembly>
</dependency>
</assembly>
ENDTEXT
RETURN m.lcReturn
ENDFUNC
***********************
FUNCTION GetStringTableCount(tcStringTable)
***********************
LOCAL lnReturn, lnCounter, lnStringSize
m.lnReturn = 0
FOR m.lnCounter = 1 TO LEN(m.tcStringTable) STEP 2
m.lnStringSize = CTOBIN(SUBSTR(m.tcStringTable,m.lnCounter,2),"2RS") * 2
m.lnReturn = m.lnReturn + 1
m.lnCounter = m.lnCounter + m.lnStringSize
ENDFOR
RETURN m.lnReturn
ENDFUNC
***********************
FUNCTION GetStringTableString(tcStringTable, tnIndex)
***********************
LOCAL lnIndex, lnCounter, lnStringSize, lcReturn
m.lnIndex = 0
m.lcReturn = ""
FOR m.lnCounter = 1 TO LEN(tcStringTable) STEP 2
m.lnStringSize = CTOBIN(SUBSTR(tcStringTable,m.lnCounter,2),"2RS") * 2
m.lnIndex = m.lnIndex + 1
IF m.lnIndex = m.tnIndex
m.lcReturn = STRCONV(SUBSTR(m.tcStringTable, m.lnCounter + 2, m.lnStringSize),6)
EXIT
ENDIF
m.lnCounter = m.lnCounter + m.lnStringSize
ENDFOR
RETURN m.lcReturn
ENDFUNC
***********************
FUNCTION GetFormattedStringForStringTable(tcString)
***********************
LOCAL lcReturn, lnLen
m.lnLen = LEN(m.tcString)
m.lcReturn = BINTOC(m.lnLen,"2RS") + STRCONV(m.tcString,5) && + IIF(MOD(m.lnLen,2) = 1,0h0000,"")
RETURN m.lcReturn
ENDFUNC
***********************
FUNCTION ReplaceStringTableString(tcStringTable, tnIndex, tcReplacementString)
***********************
LOCAL lnIndex, lnCounter, lnStringSize, lcReturn
m.lnIndex = 0
m.lcReturn = ""
m.tnIndex = m.tnIndex + 1
FOR m.lnCounter = 1 TO LEN(tcStringTable) STEP 2
m.lnStringSize = CTOBIN(SUBSTR(tcStringTable,m.lnCounter,2),"2RS") * 2
m.lnIndex = m.lnIndex + 1
IF m.lnIndex = m.tnIndex
m.lcReturn = STUFF(m.tcStringTable, m.lnCounter, m.lnStringSize + 2, This.GetFormattedStringForStringTable(tcReplacementString))
EXIT
ENDIF
m.lnCounter = m.lnCounter + m.lnStringSize
ENDFOR
RETURN m.lcReturn
ENDFUNC
***********************
FUNCTION GetStringResource(tnName, tnType)
***********************
LOCAL lcModule, hModule, lcReturn, lnResource, lnSize, hGlobal
DECLARE LONG LoadLibrary IN WIN32API STRING
DECLARE LONG FindResource IN WIN32API LONG, LONG, LONG
DECLARE LONG LoadResource IN WIN32API LONG, LONG
*!* DECLARE LONG LockResource IN WIN32API LONG
DECLARE LONG SizeofResource IN WIN32API LONG, LONG
DECLARE LONG FreeLibrary IN WIN32API LONG
DECLARE LONG FreeResource IN WIN32API LONG
m.hModule = LoadLibrary(This.Module)
m.lnResource = FindResource(m.hModule, tnName, tnType)
m.hGlobal = LoadResource(m.hModule, m.lnResource)
m.lnSize = SizeofResource(m.hModule, m.lnResource)
*!* LockResource(m.lnMem)
m.lcReturn = SYS(2600, m.hGlobal, m.lnSize)
FreeResource(m.hGlobal)
FreeLibrary(m.hModule)
RETURN m.lcReturn
ENDFUNC
***********************
FUNCTION GetNodeFromXML(tcXML, tcNameSpace)
***********************
LOCAL loDOMTemp AS MSXML2.DOMDocument, loNode AS MSXML2.IXMLDOMELEMENT
m.loNode = NULL
m.loDOMTemp = CREATEOBJECT("MSXML2.DOMDocument.4.0")
IF m.loDOMTemp.LOADXML([<dummy xmlns="] + m.tcNameSpace + [">] + m.tcXML + [</dummy>])
m.loNode = m.loDOMTemp.firstChild.firstChild
ENDIF
RETURN m.loNode
ENDFUNC
*****************************
FUNCTION GetExecutionLevelXML()
*****************************
*!*
*!* This.ExecutionLevel = 1 - asInvoker, 2 - highestAvailable, 3 - requireAdministrator
*!*
*!* asInvoker — модуль выполняется с теми же разрешенийми, что и его родительский процесс (прежде известный как leastPrivilege)
*!* highestAvailable — модуль выполняет с самыми высокими привилегиями, которые может получить текущий пользователь
*!* requireAdministrator — модуль выполняется только для администраторов (включается оверлей щита для символа модуля)
*!*
*****************************
LOCAL lcLevel, lcReturn
m.lcLevel = ICASE(This.ExecutionLevel = 2, "highestAvailable", This.ExecutionLevel = 3, "requireAdministrator", "asInvoker")
TEXT TO m.lcReturn TEXTMERGE NOSHOW
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="<<m.lcLevel>>" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
ENDTEXT
RETURN m.lcReturn
ENDFUNC
*****************************
FUNCTION GetCOMFileXML(tcModuleName)
*****************************
LOCAL lcReturn
TEXT TO m.lcReturn TEXTMERGE NOSHOW
<file name="<<LOWER(JUSTFNAME(tcModuleName))>>">
</file>
ENDTEXT
RETURN m.lcReturn
ENDFUNC
*****************************
FUNCTION GetcomClassXML(tcModuleName, tcProgID, tcCLSID, tcDescription)
*****************************
LOCAL lcReturn && tcCLSID
*!* m.lcCLSID = CLSIDFromProgIDEx(m.tcProgID)
IF PCOUNT() < 4
m.tcDescription = m.tcProgID
ENDIF
TEXT TO m.lcReturn TEXTMERGE NOSHOW
<comClass description="<<m.tcDescription>>"
clsid="<<m.tcCLSID>>"
progid="<<m.tcProgID>>"
threadingModel="Both"/>
ENDTEXT
RETURN m.lcReturn
ENDFUNC
*****************************
FUNCTION GetLastErrorMessage(tnError)
*****************************
#DEFINE FORMAT_MESSAGE_FROM_SYSTEM 0x00001000
LOCAL lcBuffer
DECLARE INTEGER GetLastError IN win32api
DECLARE INTEGER FormatMessage IN kernel32.DLL ;
INTEGER dwFlags, ;
STRING @lpSource, ;
INTEGER dwMessageId, ;
INTEGER dwLanguageId, ;
STRING @lpBuffer, ;
INTEGER nSize, ;
INTEGER Arguments
m.lcBuffer = SPACE(128)
=FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 'WINERROR.H', m.tnError, 0, @m.lcBuffer, 128 , 0)
RETURN m.lcBuffer
ENDFUNC
ENDDEFINE