CustomPage для серийного номера в Inno Setup

Как создать CustomPage в Inno Setup с полями редактирования для серийного номера? Например, 6x5 символов или 7x5 символов?

Скрипт должен проверить, все ли поля заполнены до того, как кнопка "Далее" станет доступной.

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

3 ответа

Решение

Вот один подход, который использует пользовательскую страницу, где создаются отдельные поля редактирования. Вам нужно только указать значение для SC_EDITCOUNT константа, где определяется количество полей редактирования и SC_CHARCOUNT какое количество символов можно ввести в эти поля редактирования. Если вы находитесь в первом окне редактирования, вы можете вставить весь серийный номер, если он имеет формат, с помощью шаблона, разделенного - чар (TryPasteSerialNumber функция здесь). Чтобы получить серийный номер из ящиков для редактирования достаточно позвонить GetSerialNumber где вы можете указать также разделитель для выходного формата (при необходимости).

[Setup]
AppName=Serial number project
AppVersion=1.0
DefaultDirName={pf}\Serial number project

[code]
function SetFocus(hWnd: HWND): HWND;
  external 'SetFocus@user32.dll stdcall';
function OpenClipboard(hWndNewOwner: HWND): BOOL;
  external 'OpenClipboard@user32.dll stdcall';
function GetClipboardData(uFormat: UINT): THandle;
  external 'GetClipboardData@user32.dll stdcall';
function CloseClipboard: BOOL;
  external 'CloseClipboard@user32.dll stdcall';
function GlobalLock(hMem: THandle): PAnsiChar;
  external 'GlobalLock@kernel32.dll stdcall';
function GlobalUnlock(hMem: THandle): BOOL;
  external 'GlobalUnlock@kernel32.dll stdcall';

var
  SerialPage: TWizardPage;
  SerialEdits: array of TEdit;

const
  CF_TEXT = 1;
  VK_BACK = 8;
  SC_EDITCOUNT = 6;
  SC_CHARCOUNT = 5;
  SC_DELIMITER = '-';

function IsValidInput: Boolean;
var
  I: Integer;
begin
  Result := True;
  for I := 0 to GetArrayLength(SerialEdits) - 1 do
    if Length(SerialEdits[I].Text) < SC_CHARCOUNT then
    begin
      Result := False;
      Break;
    end;
end;

function GetClipboardText: string;
var
  Data: THandle;
begin
  Result := '';
  if OpenClipboard(0) then
  try
    Data := GetClipboardData(CF_TEXT);
    if Data <> 0 then
      Result := String(GlobalLock(Data));
  finally
    if Data <> 0 then
      GlobalUnlock(Data);
    CloseClipboard;
  end;
end;

function GetSerialNumber(ADelimiter: Char): string;
var
  I: Integer;
begin
  Result := '';
  for I := 0 to GetArrayLength(SerialEdits) - 1 do
    Result := Result + SerialEdits[I].Text + ADelimiter;
  Delete(Result, Length(Result), 1);
end;

function TrySetSerialNumber(const ASerialNumber: string; ADelimiter: Char): Boolean;
var
  I: Integer;
  J: Integer;
begin
  Result := False;

  if Length(ASerialNumber) = ((SC_EDITCOUNT * SC_CHARCOUNT) + (SC_EDITCOUNT - 1)) then
  begin
    for I := 1 to SC_EDITCOUNT - 1 do
      if ASerialNumber[(I * SC_CHARCOUNT) + I] <> ADelimiter then
        Exit;

    for I := 0 to GetArrayLength(SerialEdits) - 1 do
    begin
      J := (I * SC_CHARCOUNT) + I + 1;
      SerialEdits[I].Text := Copy(ASerialNumber, J, SC_CHARCOUNT);
    end;

    Result := True;
  end;
end;

function TryPasteSerialNumber: Boolean;
begin
  Result := TrySetSerialNumber(GetClipboardText, SC_DELIMITER);
end;

procedure OnSerialEditChange(Sender: TObject);
begin
  WizardForm.NextButton.Enabled := IsValidInput;
end;

procedure OnSerialEditKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  Edit: TEdit;
  EditIndex: Integer;
begin
  Edit := TEdit(Sender);
  EditIndex := Edit.TabOrder - SerialEdits[0].TabOrder;
  if (EditIndex = 0) and (Key = Ord('V')) and (Shift = [ssCtrl]) then
  begin
    if TryPasteSerialNumber then
      Key := 0;
  end
  else
  if (Key >= 32) and (Key <= 255) then
  begin
    if Length(Edit.Text) = SC_CHARCOUNT - 1 then
    begin
      if EditIndex < GetArrayLength(SerialEdits) - 1 then
        SetFocus(SerialEdits[EditIndex + 1].Handle)
      else
        SetFocus(WizardForm.NextButton.Handle);
    end;
  end
  else
  if Key = VK_BACK then
    if (EditIndex > 0) and (Edit.Text = '') and (Edit.SelStart = 0) then
      SetFocus(SerialEdits[EditIndex - 1].Handle);
end;

procedure CreateSerialNumberPage;
var
  I: Integer;
  Edit: TEdit;
  DescLabel: TLabel;
  EditWidth: Integer;
begin
  SerialPage := CreateCustomPage(wpWelcome, 'Serial number validation',
    'Enter the valid serial number');

  DescLabel := TLabel.Create(SerialPage);
  DescLabel.Top := 16;
  DescLabel.Left := 0;
  DescLabel.Parent := SerialPage.Surface;
  DescLabel.Caption := 'Enter valid serial number and continue the installation...';
  DescLabel.Font.Style := [fsBold];

  SetArrayLength(SerialEdits, SC_EDITCOUNT);
  EditWidth := (SerialPage.SurfaceWidth - ((SC_EDITCOUNT - 1) * 8)) div SC_EDITCOUNT;

  for I := 0 to SC_EDITCOUNT - 1 do
  begin
    Edit := TEdit.Create(SerialPage);
    Edit.Top := 40;
    Edit.Left := I * (EditWidth + 8);
    Edit.Width := EditWidth;
    Edit.CharCase := ecUpperCase;
    Edit.MaxLength := SC_CHARCOUNT;
    Edit.Parent := SerialPage.Surface;
    Edit.OnChange := @OnSerialEditChange;
    Edit.OnKeyDown := @OnSerialEditKeyDown;
    SerialEdits[I] := Edit;
  end;
end;

procedure CurPageChanged(CurPageID: Integer);
begin
  if CurPageID = SerialPage.ID then
    WizardForm.NextButton.Enabled := IsValidInput;  
end;

procedure InitializeWizard;
begin
  CreateSerialNumberPage;
end;

А вот как это выглядит:

Вы можете заставить Inno запрашивать у пользователя серийный ключ, добавив CheckSerial() функция события.

Если вы хотите больше контроля над страницей, вы можете использовать одну из стандартных страниц (CreateInput...Page) или пользовательскую страницу в мастере установки, используя CreateCustomPage() и добавление элементов управления по мере необходимости.

См. Пример codedlg.iss, включенный в настройку Inno.

Самый простой способ добавить поле "Серийный ключ" под текстовыми полями "Имя" и "Организация" - это добавить что-то вроде следующего в ваш файл iss.

[Code]

  function CheckSerial(Serial: String): Boolean;
  begin
  // serial format is XXXX-XXXX-XXXX-XXXX
  Serial := Trim(Serial);
  if Length(Serial) = 19 then
    result := true;
 end;

Это может быть полезно в сочетании с

[Setup]
DefaultUserInfoSerial={param:Serial}

который заполнит серийный номер, если он был введен ранее для установки.

Другие вопросы по тегам