|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
|||
|
|||
Could not convert variant of type Dispatch into type(String) - ошибка
Имеется программа с привязкой к материнской плате и процессору, все нормально в целом работает.
Привязка выполняется на основе функции GetWMIString (код на Delphi 7 приведен ниже), В целом все нормально работает на 99% компьютерах. Но есть одна проблема, которая проявляется на 1% компьютеров и совсем непонятно, что с этим делать. Выскакивает ошибка could not convert variant of type Dispatch into type(String) - причем выскакивает она в строке, которая в коде функции помечена соответствующим комментарием. Что было проверено: Пытался копать - проверил следующее: ShowMessage(inttostr(VarType(colItem.Properties_.I tem(wmiProperty, 0)))) - выдет 9 - то есть тип проблемного выражения, которое не присваивается переменной result, varDispatch Там точно не NULL. Вместо VarIsNull(colItem.Properties_.Item(wmiProperty, 0)) пробовал еще VarIsClear(colItem.Properties_.Item(wmiProperty, 0)) - то же самое - попадает на 2-ю ветку. Дальше куда копать - непонятно Что интересно, функция GetWMIstring иногда работает (даже на этих проблемных 1% компьютерах) - например, s1 := GetWMIstring('.', 'root\CIMV2', 'Win32_BaseBoard','Product') //работает s2 := GetWMIstring('.', 'root\CIMV2', 'Win32_BaseBoard','Manufacturer') //работает s3 := GetWMIstring('.', 'root\CIMV2', 'Win32_BaseBoard','SerialNumber') //НЕ РАБОТАЕТ В чем может быть причина? Код функции: Код:
function GetWMIstring0(wmiHost, root, wmiClass, wmiProperty: string): string; var objWMIService: OLEVariant; colItems: OLEVariant; colItem: OLEVariant; oEnum: IEnumvariant; iValue: LongWord; function GetWMIObject(const objectName: String): IDispatch; var chEaten: Integer; BindCtx: IBindCtx; Moniker: IMoniker; begin OleCheck(CreateBindCtx(0, bindCtx)); OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker)); //здесь ошибка OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result)); end; begin objWMIService := GetWMIObject(Format('winmgmts:\\%s\%s',[wmiHost,root])); colItems := objWMIService.ExecQuery(Format('SELECT * FROM %s',[wmiClass]),'WQL',0); oEnum := IUnknown(colItems._NewEnum) as IEnumVariant; while oEnum.Next(1, colItem, iValue) = 0 do begin if VarIsNull(colItem.Properties_.Item(wmiProperty, 0)) then Result := 'NULL' else begin //при выполнении следующей команды будет ошибка "could not convert variant of type Dispatch into type(String)" Result := colItem.Properties_.Item(wmiProperty, 0) end end; end; |
#2
|
||||
|
||||
Была уже подобная тема. Как раз про IDispatch и String у WMI. Проверяй типы до того как делать преобразование.
Пишу программы за еду. __________________ |
#3
|
|||
|
|||
Цитата:
Так и так проверил, что тип-то varDispatch (в том числе в тех 99% случаях, когда все нормально работает). В 99% случаев он нормально преобразуется в String (причем использую в данном случае не свой код, а готовый из Интернета - и он на 99% компьютеров работает). А на 1% компьютеров возникает ошибка. Даже непонятно, куда копать. Что вообще с этим Dispatch делать. |
#4
|
|||
|
|||
Цитата:
Честно, весь Интернет (весь Гугл и Яндекс) перерыл - да, есть что-то там про Dispatch и String - но применительно к данному случаю, совершенно не помогло. И самое главное, непонятно, куда копать. Что делать с этим Dispatch? Если не в string преобразовать - то во что еще можно преобразовать? Может, у Dispatch есть какие-то свойства или методы к которым можно обращаться (например, как с COM-объектом)? И если у Dispatch есть методы - как узнать, какие у него есть методы? |
#5
|
|||
|
|||
Модуль System смотрел - нашел там
Код:
varDispatch = $0009; { vt_dispatch 9 } IDispatch = interface(IUnknown) ['{00020400-0000-0000-C000-000000000046}'] function GetTypeInfoCount(out Count: Integer): HResult; stdcall; function GetTypeInfo(Index, LocaleID: Integer; out TypeInfo): HResult; stdcall; function GetIDsOfNames(const IID: TGUID; Names: Pointer; NameCount, LocaleID: Integer; DispIDs: Pointer): HResult; stdcall; function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult; stdcall; end; Пробовал обращаться к colItem.Properties_.Item(wmiProperty, 0) через какой-нибудь метод Dispatch - например, пробовал Код:
var Count: integer colItem.Properties_.Item(wmiProperty, 0).GetTypeInfoCount(Count) - думал, это поможет определить количество методов или свойств, доступных для обращения к Dispatch - не помогло (ошибку выдает и все) - похоже, этих методов что в System описаны нет у Dispatch |
#6
|
|||
|
|||
IDispatch - это один из базовых интерфейсов, как TObject для обычных классов. Т.е. что за реальный объект "прячется" за этим интерфейсом заранее неизвестно.
Можно попробовать простой workaround. В случае, если у тебя не получается получить значение, ты все равно возвращаешь 'NULL'. Ну так перехвати исключение в верни этот 'NULL'. Типа: Код:
try s3 := GetWMIstring('.', 'root\CIMV2', 'Win32_BaseBoard','SerialNumber') except s3 := 'NULL' end; |
#7
|
|||
|
|||
Цитата:
Вообще говоря, временно так и пришлось сделать - от безысходности. Правда, сделал немного не так, а вот так: Код:
try s3 := GetWMIstring('.', 'root\CIMV2', 'Win32_BaseBoard','SerialNumber') except on E:Exception do s3 := E.Message end; чтобы было более более информативно, чем в случае с 'NULL'. Но это плохое решение. Вполне может получиться, что на некоторых компьютерах все четыре GetWMIString будут выдавать значение NULL и таким образом, теряется вообще смысл привязки программы к материнской плате и процессору. Кроме того, пользователь может поменять операционную систему (например, поменять Windows 7 на Windows 10) и может так получиться, что GetWMISTring будет уже другим для другой операционной системы ... Хотелось бы все-таки по-нормальному решить проблему или хотя бы понять причину ошибки. И понять, как вытащить этот SerialNumber, либо узнать, что он действительно NULL и его вытащить невозможно. Ведь Dispatch-то какой-то есть и он явно не пустой. И явно что-то с ним можно сделать. Только понять нужно, что с ним делать и что можно сделать. Понять, куда копать. А так получается, только гадать можно (методом тыка, например) - какие методы могут быть у этого Dispatch - никак не узнать? Последний раз редактировалось delphi-programmer-2007, 18.01.2017 в 12:00. |
#8
|
|||
|
|||
ну, для начала, мне кажется, что ты там в коде пытаешься вызывать методы IDispatch, хотя у тебя IUnknown. Т.е. сначала надо хотя бы получить явно этот IDispatch.
Далее, я бы попробовал понять этот код и посмотреть в MSDN что за объект там получается и попробовать получить правильный интерфейс и вызывать его методы. Кстати, вот тут дают немного другой вариант: http://stackoverflow.com/questions/2...mber-in-delphi |
#9
|
|||
|
|||
Цитата:
Буду разбираться. Сложность тут еще в том, что на моих компьютерах (на всех) все работает нормально. Проблема только на 1% компьютерах пользователей. На которые, конечно же, не будешь устанавливать среду разработчики Delphi 7 (пользователям не нужен всякий мусор чтобы захламлять компьютер) - и тем более пользователь не позволит разбираться с этой проблемой несколько дней по удаленному доступу (да и удаленный доступ такая штука, что он рвется периодически). Вот бы научиться ошибку воспроизводить у себя ... понять, от чего она зависит и настроить окружение (с этой ошибкой) у себя как у этого пользователя. Скопировал реестр (с помощью regedit) пользователя себе - на всякий случай - но что можно с этим реестром делать, чтобы у себя ошибку воспроизвести... |
#10
|
|||
|
|||
Ну, для отладки на таких машинах можно написать простенькую программку, делающую этот вызов и логирующую все, что тебе хочется. Соответсвенно, либо просишь пользователя ее запустить и прислать тебе лог, либо по удаленке сам это делаешь, т.е. устойчивость связи тут уже меньше играет.
По поводу, "а вдруг все вызовы не заработают". Ничто не мешает сделать простую защиту: 1. В реестре ищем ключ программы. 2. В нем смотрим, нет ли для запрашиваемого параметра соотв. записи. 3. Если запись есть, то возвращаем ее (обеспечиваем устойчивость). 4. Если записи нет, то пробуем получить данные из WMI. 5. Если получилось, то возвращаем результат. 6. Если не получилось, то генерим GUID (уникальность), пишем его в соотв. ключ в реестре и возвращаем его. ЗЫ. А что с кодом по ссылке? Пробовал? |
#11
|
||||
|
||||
Цитата:
Можно, конечно - только не совсем понятно, что в данном случае нужно логировать. Если даже на своем компьютере воспроизвести ошибку пользователей не удается пока, притом, что пользователь предоставляет удаленный доступ и возможность что угодно делать с его компьютером - то об утилитке и речь идти не может на данном этапе, как я понимаю. Цитата:
Собственно, тут как раз и проблема в том, чтобы знать то, что нужно логировать. А если знать - то написать такую программку не проблема. Цитата:
Пользователи могут еще Windows переустанавливать. И не всегда перед установкой могут выполнить определенные действия - даже если написать инструкцию. Может сгореть жесткий диск или компьютер может быть заражен вирусами. И привязка программы при этом не должна теряться. А если дать возможность пользователю активировать программу ключом, не привязанным к железу - он сможет купив одну программу использовать программу бесплатно на 1000 компьютерах. Цитата:
Код рабочий - но только на моей машине. Но проверить на проблемном компьютере нет возможности. Дергать пользователя, у которого была проблема пытался, но он игнорирует (тк у него проблема временно решена ключом, привязанным к жесткому диску (к определенным параметрами диска - разделам и т. д.), который действует до переустановки Windows (но он этого не знает, тк не рискнули ему сказать это, опасаясь недовольства) - ему это неинтересно, его все устраивает). Соответственно, проблема с этим пользователем всплывет, когда он решит переустановить Windows (придется выдать ключ еще раз - если к тому времени не получится нормально решить проблему). |
#12
|
|||
|
|||
А, так это ты защиту мастеришь...
Ну, первый совет тут - купить готовую. Лучше свое время потратить на разработку своей программы, чем на защиту, особенно с учетом того, что ты не специалист по защите. Почитай для общего образования блог Rouse. Он там постил несколько статей по противодействию взлому. Так вот, хотя я прекрсно понимаю то, о чем он там пишет, но повтрорять это для каждого своего проекта - увольте (ну, с учетом того, что у меня есть купленный внешний протектор). Далее. По поводу оставления привязки. Есть 3 варианта. Первый - забить. В случае проблемы, пользователь связывается с тобой и ты даешь ему новый ключ. Второй - сделать что-то типа как в Винде. Т.е. если один-два из всех ключей не совпадает, то считать, что все ок, просто пользователь сменил часть железа. По желанию можно просто перегенерить, что бы следущее обновление железа не убило регистрацию. Единственное, можно таким образом "размножить" регистрацию путем постепенной замены железа с переносом старого в другой комп, но на практике это сильно геморойно. Ну и третий вариант - он-лайн проверка. Если программа работает с интернетом, то ничего страшного, если она будет проверять лицензию через интернет. |
#13
|
|||||
|
|||||
Цитата:
Да - есть множество протекторов - вопрос в том, есть ли к ним доверие (да и разбираться с ними еще нужно). В случае чего клиент именно меня за яйца подвесит. Не охота зависеть непонятно от кого. Цитата:
Ну если много мелких проектов - то да - видимо, тут вариантов нет. Хотя если все проекты разработаны в одной среде - в принципе, один раз написанный протектор можно применять во многих проектах слегка модифицируя. Свое все-таки оно надежнее - пусть даже в чем-то менее эффективно - но по крайней мере, сам все контролируешь и некого винить если что не так. 100% защиты все равно нет - кому очень надо все равно взломает. Цитата:
Собственно так и получается в этих случаях. Поэтому и пытаюсь решить вопрос. Но проблемы имеют свойство плодиться - и со временем общее количество случаев, когда "временно забил" будет расти и наступит момент, что придется выдавать по 1 ключу в сутки (клиентам кто купил в 2009, 2010, 2011, 2012 итд годы - у которых будут периодически слетать ключи). А выдача ключей (с проверкой истории переписки - чтобы убедиться, что это действительно тот случай, где ранее "забил", а не попытка клиента получить бесплатно второй ключ) - это своего рода труд и труд бесплатный. Получается, чем больше программ продал - тем больше геморроя сделал себе на будущее. Цитата:
Это бред - никто не будет этим заниматься из-за суммы меньше 30 долларов. А кто будет - ну и хрен с ними - все равно на них много не заработаешь (а те, кому продукт действительно нужен и приносит пользу - обычно из-за таких сумм не парятся). Это верно. Цитата:
Программа не требует Интернета. Но блочилку через Интернет и через обновления все же встроил - чтобы со временем у "хитрых" клиентов лишние ключи блокировались. Понятно, что 100% гарантий нет - но хоть что-то. |
#14
|
|||
|
|||
Ну не знаю.
Я пользовался в свое время ASProtect'ом. Никаких нареканий нет. Правда к железу привязывал только один проект. Остальные - без привязки только ключ. ЗЫ. Кстати, если программа не сильно специфичная, то клиенты не очень то и бегут обновляться (по крайней мере если нет привязки к железу). За всю историю раз 20 такое было (8+ лет), когда клиент потерял ключ в результате паденя почтовой базы или чего подобного. Это при том, что для пары проектов я менял секретный ключ, т.е. все старые ключи переставали работать. Но клиенты могут быть разными. У меня в основном End-User'а. |
Этот пользователь сказал Спасибо lmikle за это полезное сообщение: | ||
Admin (28.01.2017)
|
#15
|
|||
|
|||
Цитата:
Нет - обновления мы передаем бесплатно только в течение определенного срока после покупки программы. Нам не надо этого чтобы клиент обновил программу через 5 лет, она у него упала и он обвинил в этом нас, что мы ему якобы сломали программу. Прошел гарантийный срок - пользуйся программой и дальше если ничего не сломалось, но без обращений к нам, либо за отдельную плату. Таким образом, после окончания гарантийного срока в идеале клиент уже не должен нас бесплатно беспокоить (собственно и при покупке мобильного телефона также). Исключение из правил одно - когда программа "сломалась" по причине того, что ключ слетел (из-за того, что клиент переустановил Windows, например) - тогда, как мы понимаем, нужно нянчиться с клиентом и после окончания гарантийного срока (если ключ слетел по причине нашего косяка, а не по причине смены железа клиентом). |