Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > ОС и железо
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 16.01.2017, 18:07
delphi-programmer-2007 delphi-programmer-2007 вне форума
Прохожий
 
Регистрация: 16.01.2017
Сообщения: 12
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию 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  
Старый 16.01.2017, 19:38
Аватар для NumLock
NumLock NumLock вне форума
Let Me Show You
 
Регистрация: 30.04.2010
Адрес: Северодвинск
Сообщения: 5,426
Версия Delphi: 7, XE5
Репутация: 59586
По умолчанию

Была уже подобная тема. Как раз про IDispatch и String у WMI. Проверяй типы до того как делать преобразование.
__________________
Пишу программы за еду.
__________________
Ответить с цитированием
  #3  
Старый 17.01.2017, 11:06
delphi-programmer-2007 delphi-programmer-2007 вне форума
Прохожий
 
Регистрация: 16.01.2017
Сообщения: 12
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от NumLock
Была уже подобная тема. Как раз про IDispatch и String у WMI. Проверяй типы до того как делать преобразование.

Так и так проверил, что тип-то varDispatch (в том числе в тех 99% случаях, когда все нормально работает). В 99% случаев он нормально преобразуется в String (причем использую в данном случае не свой код, а готовый из Интернета - и он на 99% компьютеров работает). А на 1% компьютеров возникает ошибка.

Даже непонятно, куда копать.

Что вообще с этим Dispatch делать.
Ответить с цитированием
  #4  
Старый 17.01.2017, 11:10
delphi-programmer-2007 delphi-programmer-2007 вне форума
Прохожий
 
Регистрация: 16.01.2017
Сообщения: 12
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от NumLock
Была уже подобная тема. Как раз про IDispatch и String у WMI. Проверяй типы до того как делать преобразование.

Честно, весь Интернет (весь Гугл и Яндекс) перерыл - да, есть что-то там про Dispatch и String - но применительно к данному случаю, совершенно не помогло.

И самое главное, непонятно, куда копать.

Что делать с этим Dispatch? Если не в string преобразовать - то во что еще можно преобразовать? Может, у Dispatch есть какие-то свойства или методы к которым можно обращаться (например, как с COM-объектом)?

И если у Dispatch есть методы - как узнать, какие у него есть методы?
Ответить с цитированием
  #5  
Старый 17.01.2017, 13:31
delphi-programmer-2007 delphi-programmer-2007 вне форума
Прохожий
 
Регистрация: 16.01.2017
Сообщения: 12
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Модуль 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  
Старый 17.01.2017, 20:30
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,004
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

IDispatch - это один из базовых интерфейсов, как TObject для обычных классов. Т.е. что за реальный объект "прячется" за этим интерфейсом заранее неизвестно.
Можно попробовать простой workaround. В случае, если у тебя не получается получить значение, ты все равно возвращаешь 'NULL'. Ну так перехвати исключение в верни этот 'NULL'. Типа:
Код:
try
  s3 := GetWMIstring('.', 'root\CIMV2', 'Win32_BaseBoard','SerialNumber')
except
  s3 := 'NULL'
end;
Ответить с цитированием
  #7  
Старый 18.01.2017, 11:54
delphi-programmer-2007 delphi-programmer-2007 вне форума
Прохожий
 
Регистрация: 16.01.2017
Сообщения: 12
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от lmikle
IDispatch - это один из базовых интерфейсов, как TObject для обычных классов. Т.е. что за реальный объект "прячется" за этим интерфейсом заранее неизвестно.
Можно попробовать простой workaround. В случае, если у тебя не получается получить значение, ты все равно возвращаешь 'NULL'. Ну так перехвати исключение в верни этот 'NULL'. Типа:
Код:
try
  s3 := GetWMIstring('.', 'root\CIMV2', 'Win32_BaseBoard','SerialNumber')
except
  s3 := 'NULL'
end;


Вообще говоря, временно так и пришлось сделать - от безысходности.

Правда, сделал немного не так, а вот так:

Код:
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  
Старый 18.01.2017, 21:41
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,004
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

ну, для начала, мне кажется, что ты там в коде пытаешься вызывать методы IDispatch, хотя у тебя IUnknown. Т.е. сначала надо хотя бы получить явно этот IDispatch.
Далее, я бы попробовал понять этот код и посмотреть в MSDN что за объект там получается и попробовать получить правильный интерфейс и вызывать его методы.

Кстати, вот тут дают немного другой вариант:
http://stackoverflow.com/questions/2...mber-in-delphi
Ответить с цитированием
  #9  
Старый 19.01.2017, 01:09
delphi-programmer-2007 delphi-programmer-2007 вне форума
Прохожий
 
Регистрация: 16.01.2017
Сообщения: 12
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от lmikle
ну, для начала, мне кажется, что ты там в коде пытаешься вызывать методы IDispatch, хотя у тебя IUnknown. Т.е. сначала надо хотя бы получить явно этот IDispatch.
Далее, я бы попробовал понять этот код и посмотреть в MSDN что за объект там получается и попробовать получить правильный интерфейс и вызывать его методы.

Кстати, вот тут дают немного другой вариант:
http://stackoverflow.com/questions/2...mber-in-delphi

Буду разбираться.

Сложность тут еще в том, что на моих компьютерах (на всех) все работает нормально. Проблема только на 1% компьютерах пользователей. На которые, конечно же, не будешь устанавливать среду разработчики Delphi 7 (пользователям не нужен всякий мусор чтобы захламлять компьютер) - и тем более пользователь не позволит разбираться с этой проблемой несколько дней по удаленному доступу (да и удаленный доступ такая штука, что он рвется периодически).

Вот бы научиться ошибку воспроизводить у себя ... понять, от чего она зависит и настроить окружение (с этой ошибкой) у себя как у этого пользователя.

Скопировал реестр (с помощью regedit) пользователя себе - на всякий случай - но что можно с этим реестром делать, чтобы у себя ошибку воспроизвести...
Ответить с цитированием
  #10  
Старый 19.01.2017, 22:02
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,004
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Ну, для отладки на таких машинах можно написать простенькую программку, делающую этот вызов и логирующую все, что тебе хочется. Соответсвенно, либо просишь пользователя ее запустить и прислать тебе лог, либо по удаленке сам это делаешь, т.е. устойчивость связи тут уже меньше играет.

По поводу, "а вдруг все вызовы не заработают". Ничто не мешает сделать простую защиту:
1. В реестре ищем ключ программы.
2. В нем смотрим, нет ли для запрашиваемого параметра соотв. записи.
3. Если запись есть, то возвращаем ее (обеспечиваем устойчивость).
4. Если записи нет, то пробуем получить данные из WMI.
5. Если получилось, то возвращаем результат.
6. Если не получилось, то генерим GUID (уникальность), пишем его в соотв. ключ в реестре и возвращаем его.

ЗЫ. А что с кодом по ссылке? Пробовал?
Ответить с цитированием
  #11  
Старый 23.01.2017, 14:54
delphi-programmer-2007 delphi-programmer-2007 вне форума
Прохожий
 
Регистрация: 16.01.2017
Сообщения: 12
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от lmikle
Ну, для отладки на таких машинах можно написать простенькую программку, делающую этот вызов и логирующую все, что тебе хочется.

Можно, конечно - только не совсем понятно, что в данном случае нужно логировать.

Если даже на своем компьютере воспроизвести ошибку пользователей не удается пока, притом, что пользователь предоставляет удаленный доступ и возможность что угодно делать с его компьютером - то об утилитке и речь идти не может на данном этапе, как я понимаю.


Цитата:
Сообщение от lmikle
Соответсвенно, либо просишь пользователя ее запустить и прислать тебе лог, либо по удаленке сам это делаешь, т.е. устойчивость связи тут уже меньше играет.

Собственно, тут как раз и проблема в том, чтобы знать то, что нужно логировать. А если знать - то написать такую программку не проблема.


Цитата:
Сообщение от lmikle
По поводу, "а вдруг все вызовы не заработают". Ничто не мешает сделать простую защиту:
1. В реестре ищем ключ программы.
2. В нем смотрим, нет ли для запрашиваемого параметра соотв. записи.
3. Если запись есть, то возвращаем ее (обеспечиваем устойчивость).
4. Если записи нет, то пробуем получить данные из WMI.
5. Если получилось, то возвращаем результат.
6. Если не получилось, то генерим GUID (уникальность), пишем его в соотв. ключ в реестре и возвращаем его.

Пользователи могут еще Windows переустанавливать. И не всегда перед установкой могут выполнить определенные действия - даже если написать инструкцию. Может сгореть жесткий диск или компьютер может быть заражен вирусами.

И привязка программы при этом не должна теряться.

А если дать возможность пользователю активировать программу ключом, не привязанным к железу - он сможет купив одну программу использовать программу бесплатно на 1000 компьютерах.



Цитата:
Сообщение от lmikle
ЗЫ. А что с кодом по ссылке? Пробовал?

Код рабочий - но только на моей машине.

Но проверить на проблемном компьютере нет возможности.

Дергать пользователя, у которого была проблема пытался, но он игнорирует (тк у него проблема временно решена ключом, привязанным к жесткому диску (к определенным параметрами диска - разделам и т. д.), который действует до переустановки Windows (но он этого не знает, тк не рискнули ему сказать это, опасаясь недовольства) - ему это неинтересно, его все устраивает). Соответственно, проблема с этим пользователем всплывет, когда он решит переустановить Windows (придется выдать ключ еще раз - если к тому времени не получится нормально решить проблему).
Ответить с цитированием
  #12  
Старый 23.01.2017, 20:04
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,004
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

А, так это ты защиту мастеришь...
Ну, первый совет тут - купить готовую. Лучше свое время потратить на разработку своей программы, чем на защиту, особенно с учетом того, что ты не специалист по защите. Почитай для общего образования блог Rouse. Он там постил несколько статей по противодействию взлому. Так вот, хотя я прекрсно понимаю то, о чем он там пишет, но повтрорять это для каждого своего проекта - увольте (ну, с учетом того, что у меня есть купленный внешний протектор).
Далее. По поводу оставления привязки. Есть 3 варианта.

Первый - забить. В случае проблемы, пользователь связывается с тобой и ты даешь ему новый ключ.

Второй - сделать что-то типа как в Винде. Т.е. если один-два из всех ключей не совпадает, то считать, что все ок, просто пользователь сменил часть железа. По желанию можно просто перегенерить, что бы следущее обновление железа не убило регистрацию. Единственное, можно таким образом "размножить" регистрацию путем постепенной замены железа с переносом старого в другой комп, но на практике это сильно геморойно.

Ну и третий вариант - он-лайн проверка. Если программа работает с интернетом, то ничего страшного, если она будет проверять лицензию через интернет.
Ответить с цитированием
  #13  
Старый 27.01.2017, 15:43
delphi-programmer-2007 delphi-programmer-2007 вне форума
Прохожий
 
Регистрация: 16.01.2017
Сообщения: 12
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от lmikle
А, так это ты защиту мастеришь...
Ну, первый совет тут - купить готовую. Лучше свое время потратить на разработку своей программы, чем на защиту, особенно с учетом того, что ты не специалист по защите. Почитай для общего образования блог Rouse. Он там постил несколько статей по противодействию взлому. Так вот, хотя я прекрсно понимаю то, о чем он там пишет,

Да - есть множество протекторов - вопрос в том, есть ли к ним доверие (да и разбираться с ними еще нужно). В случае чего клиент именно меня за яйца подвесит. Не охота зависеть непонятно от кого.



Цитата:
Сообщение от lmikle
но повтрорять это для каждого своего проекта - увольте (ну, с учетом того, что у меня есть купленный внешний протектор).

Ну если много мелких проектов - то да - видимо, тут вариантов нет. Хотя если все проекты разработаны в одной среде - в принципе, один раз написанный протектор можно применять во многих проектах слегка модифицируя.

Свое все-таки оно надежнее - пусть даже в чем-то менее эффективно - но по крайней мере, сам все контролируешь и некого винить если что не так. 100% защиты все равно нет - кому очень надо все равно взломает.



Цитата:
Сообщение от lmikle
Далее. По поводу оставления привязки. Есть 3 варианта.
Первый - забить. В случае проблемы, пользователь связывается с тобой и ты даешь ему новый ключ.

Собственно так и получается в этих случаях. Поэтому и пытаюсь решить вопрос. Но проблемы имеют свойство плодиться - и со временем общее количество случаев, когда "временно забил" будет расти и наступит момент, что придется выдавать по 1 ключу в сутки (клиентам кто купил в 2009, 2010, 2011, 2012 итд годы - у которых будут периодически слетать ключи).

А выдача ключей (с проверкой истории переписки - чтобы убедиться, что это действительно тот случай, где ранее "забил", а не попытка клиента получить бесплатно второй ключ) - это своего рода труд и труд бесплатный. Получается, чем больше программ продал - тем больше геморроя сделал себе на будущее.



Цитата:
Сообщение от lmikle
Второй - сделать что-то типа как в Винде. Т.е. если один-два из всех ключей не совпадает, то считать, что все ок, просто пользователь сменил часть железа. По желанию можно просто перегенерить, что бы следущее обновление железа не убило регистрацию. Единственное, можно таким образом "размножить" регистрацию путем постепенной замены железа с переносом старого в другой комп, но на практике это сильно геморойно.

Это бред - никто не будет этим заниматься из-за суммы меньше 30 долларов. А кто будет - ну и хрен с ними - все равно на них много не заработаешь (а те, кому продукт действительно нужен и приносит пользу - обычно из-за таких сумм не парятся). Это верно.



Цитата:
Сообщение от lmikle
Ну и третий вариант - он-лайн проверка. Если программа работает с интернетом, то ничего страшного, если она будет проверять лицензию через интернет.

Программа не требует Интернета. Но блочилку через Интернет и через обновления все же встроил - чтобы со временем у "хитрых" клиентов лишние ключи блокировались. Понятно, что 100% гарантий нет - но хоть что-то.
Ответить с цитированием
  #14  
Старый 28.01.2017, 02:31
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,004
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Ну не знаю.
Я пользовался в свое время ASProtect'ом. Никаких нареканий нет. Правда к железу привязывал только один проект. Остальные - без привязки только ключ.

ЗЫ. Кстати, если программа не сильно специфичная, то клиенты не очень то и бегут обновляться (по крайней мере если нет привязки к железу). За всю историю раз 20 такое было (8+ лет), когда клиент потерял ключ в результате паденя почтовой базы или чего подобного. Это при том, что для пары проектов я менял секретный ключ, т.е. все старые ключи переставали работать.
Но клиенты могут быть разными. У меня в основном End-User'а.
Ответить с цитированием
Этот пользователь сказал Спасибо lmikle за это полезное сообщение:
Admin (28.01.2017)
  #15  
Старый 28.01.2017, 08:52
delphi-programmer-2007 delphi-programmer-2007 вне форума
Прохожий
 
Регистрация: 16.01.2017
Сообщения: 12
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Цитата:
Сообщение от lmikle
Ну не знаю.
Я пользовался в свое время ASProtect'ом. Никаких нареканий нет. Правда к железу привязывал только один проект. Остальные - без привязки только ключ.

ЗЫ. Кстати, если программа не сильно специфичная, то клиенты не очень то и бегут обновляться (по крайней мере если нет привязки к железу).

Нет - обновления мы передаем бесплатно только в течение определенного срока после покупки программы.

Нам не надо этого чтобы клиент обновил программу через 5 лет, она у него упала и он обвинил в этом нас, что мы ему якобы сломали программу.

Прошел гарантийный срок - пользуйся программой и дальше если ничего не сломалось, но без обращений к нам, либо за отдельную плату. Таким образом, после окончания гарантийного срока в идеале клиент уже не должен нас бесплатно беспокоить (собственно и при покупке мобильного телефона также).

Исключение из правил одно - когда программа "сломалась" по причине того, что ключ слетел (из-за того, что клиент переустановил Windows, например) - тогда, как мы понимаем, нужно нянчиться с клиентом и после окончания гарантийного срока (если ключ слетел по причине нашего косяка, а не по причине смены железа клиентом).
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 00:36.


 

Сайт

Форум

FAQ

RSS лента

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2023

ВКонтакте   Facebook   Twitter