Перейти к содержанию
Fire Monkey от А до Я
  • 0

Как получить версию APK-файла под Win32?


SerhioUser

Вопрос

Есть прога(c++) на сервере под Windows. К ней подключаются девайсы под андроид и проверяют обновление некого андроид-приложения. Прога на сервере должна узнать версию лежащего локально apk-файла и сообщить ее девайсу.

Теоретически можно с помощью aapt получить AndroidManifest.xml файл и вытащить версию от туда. А если ли какие стандартные средства, чтобы узнать версию APK под Windows?

Ссылка на комментарий

Рекомендуемые сообщения

  • 0

Ну какие под Windows могут быть стандартные средства для чтения файла из другой ОС? Конечно их нету.

Делается элементарно.

1. apk - это обычный zip файл. Распаковываете его (в Delphi встроенная поддержка zip - uses System.Zip > TZipFile ), и читаете  все что надо. В Total Commander в такие архивы проще заходить без переименования расширения - нажав Ctrl + PgDown 

2. Еще проще

aapt dump badging myapp.apk

Выведет детальную информацию об apk. 

aapt лежит где то в c:\Users\<user>\Documents\Embarcadero\Studio\18.0\PlatformSDKs\android-sdk-windows\build-tools\25.0.0\aapt.exe

Например чтобы сохранил результат в файл: aapt dump badging myapp.apk > mytextfile.txt

В первой строчке и будет искомое:

package: name='com.embarcadero.MyApp' versionCode='1' versionName='1.0.0' platformBuildVersionName=''

 

Изменено пользователем ENRGY
Ссылка на комментарий
  • 0
1 минуту назад, ENRGY сказал:

 

Ну какие под Windows могут быть стандартные средства для чтения файла из другой ОС? Конечно их нету.

 

Имелись в виду стандартные средства эмбаркадеро, т.е Delphi/Builder, а не самой windows.

5 минут назад, ENRGY сказал:

1. apk - это обычный zip файл.

Не совсем. Где-то читал, что он может быть шифрован и тогда такой вариант не подойдет.

 

7 минут назад, ENRGY сказал:

2. Еще проще

 

8 минут назад, ENRGY сказал:

Например чтобы сохранил результат в файл: aapt dump badging myapp.apk > mytextfile.txt

Я так пробовал с самого начала. Ни ShellExecute ни WinExec у меня не смогли сохранить результат в файл. Все выводилось в консоль. :(

Ссылка на комментарий
  • 0

aapt dump badging myapp.apk > mytextfile.txt

Вот эта команда "> mytextfile.txt" 100% сохранит весь вывод консоли в файл.

Сначала протестируйте свою command line cтрочку в cmd.exe, потом вставьте ее в ShellExecute  

 

Ссылка на комментарий
  • 0
4 часа назад, SerhioUser сказал:

.

4 часа назад, ENRGY сказал:

apk - это обычный zip файл.

Не совсем. Где-то читал, что он может быть шифрован и тогда такой вариант не подойдет.

 

где это вы читали?

Ссылка на комментарий
  • 0
13 часов назад, Major сказал:

где это вы читали?

Вот здесь человек не мог открыть apk архиватором. Конечно возможно он что-то делал не так.

Мои apk-шки архиваторы открывают нормально, и это обнадеживает.

14 часов назад, ENRGY сказал:

Вот эта команда "> mytextfile.txt" 100% сохранит весь вывод консоли в файл.

Я ж написал, что я так пробовал с самого начала. Только не написал, что в консоли отрабатывает корректно.

А из ShellExecute и из WinExec в файл не сохраняет (это я писал). Одна и та-же команда. Просто выводит на экран, как будто "> mytextfile.txt" не прописано.

Сейчас буду разбираться, что умеет TZipFile.

Ссылка на комментарий
  • 0
9 минут назад, SerhioUser сказал:

cmd.exe не принимает параметры.

не может быть такого

весь смысл cmd в параметрах. сам он ничего не делает, так что ошибка на стороне программиста..

Ссылка на комментарий
  • 0
6 минут назад, krapotkin сказал:

не может быть такого

весь смысл cmd в параметрах. сам он ничего не делает, так что ошибка на стороне программиста..

Пуск->Выполнить

Дальше пишем "cmd.exe dir"

Открывается консоль и фСё! :) Сам в шоке... (У меня win 8.1)

1 минуту назад, Rusland сказал:

Про зашифрованный apk ни разу не слышал...

Я, честно говоря, тоже... возможно тот чел. что-то не так делал...

Ссылка на комментарий
  • 0

Попытался извлечь из APK-файла файл AndroidManifest.xml с помощью 7zip и winrar. Как архив apk открывается, а вот содержимое действительно похоже на зашифрованное. У AndroidManifest.xml даже размер не соответствует оригинальному AndroidManifest.xml, который туда помещала студия. Кто-то может у себя это проверить?

TZipFile вернул аналогичное шифрованное содержимое.

Походу  aapt dump badging единственный вариант...

Ссылка на комментарий
  • 0
  • Модераторы
1 час назад, SerhioUser сказал:

Открывается консоль и фСё! :) Сам в шоке... (У меня win 8.1)

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

что в этом странного?

Ссылка на комментарий
  • 0

В общем работает так:

WinExec("cmd /c aapt.exe dump badging path\\file.apk > text.txt", SW_HIDE);

Запускается по тихому и создает text.txt, из которого можно выудить информацию о версии. Жаль, что нельзя этот файл получать напрямую в память, а не на диск - лишние операции, создающие тормоза.

Ссылка на комментарий
  • 0

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

Ссылка на комментарий
  • 0
10 часов назад, SerhioUser сказал:

В общем работает так:

WinExec("cmd /c aapt.exe dump badging path\\file.apk > text.txt", SW_HIDE);

Запускается по тихому и создает text.txt, из которого можно выудить информацию о версии. Жаль, что нельзя этот файл получать напрямую в память, а не на диск - лишние операции, создающие тормоза.

Вот так напрямую в память:

function ShellExecuteMy(CommandLine: string; AWorkDir: string = 'C:\') : String;
var
  SA: TSecurityAttributes;
  SI: TStartupInfo;
  PI: TProcessInformation;
  StdOutPipeRead, StdOutPipeWrite: THandle;
  WasOK: Boolean;
  Buffer: array[0..255] of AnsiChar;
  BytesRead: Cardinal;
  WorkDir: string;
  Handle: Boolean;
  AOutputLine : String;
begin
  with SA do begin
    nLength := SizeOf(SA);
    bInheritHandle := True;
    lpSecurityDescriptor := nil;
  end;
  CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 0);
  try
    with SI do
    begin
      FillChar(SI, SizeOf(SI), 0);
      cb := SizeOf(SI);
      dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
      wShowWindow := SW_HIDE;
//      wShowWindow := SW_MINIMIZE;
//      wShowWindow := SW_NORMAL;
      hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin
      hStdOutput := StdOutPipeWrite;
      hStdError := StdOutPipeWrite;
    end;
    WorkDir := AWorkDir;
//    Handle := CreateProcess(nil, PChar('cmd.exe /C ' + CommandLine),
    Handle := CreateProcess(nil, PChar(CommandLine),
                            nil, nil, True, 0, nil,
                            PChar(WorkDir), SI, PI);
//    Result:=PI;
    CloseHandle(StdOutPipeWrite);
  finally
    CloseHandle(StdOutPipeRead);
  end;

    if Handle then
      try
        repeat
//          WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
          WasOK := ReadFile(StdOutPipeRead, Buffer, 50, BytesRead, nil);
          if BytesRead > 0 then
          begin
            Buffer[BytesRead] := #0;
            AOutputLine:=StrOemToAnsi(Buffer);
            Result :=Result + AOutputLine;
          end;
        until not WasOK or (BytesRead = 0);
        WaitForSingleObject(PI.hProcess, INFINITE);
      finally
        CloseHandle(PI.hThread);
        CloseHandle(PI.hProcess);
      end;
  finally
    CloseHandle(StdOutPipeRead);
  end;
end;

Функция читает пайп вывода и помещает в строку. 

P.S. uses Winapi.Windows;

Изменено пользователем Евгений Корепов
Ссылка на комментарий
  • 0

Благодарю, Евгений Корепов!

Вот функция GetApkVersion() на C++ для получения версии APK-файла, и слегка подкорректированная вышеописанная функция запуска:

String __fastcall ShellExecutePipe(String CommandLine, String AWorkDir = "C:\\", UINT Flag = SW_HIDE)
{
	TSecurityAttributes SA;
	TStartupInfo SI;
	TProcessInformation PI;
	HANDLE StdOutPipeRead, StdOutPipeWrite;
	bool WasOK;
	char Buffer[256];
	unsigned long BytesRead;
	String WorkDir;
	bool Handle;
	String AOutputLine;
	String Result;

  SA.nLength = sizeof(SA);
  SA.bInheritHandle = true;
  SA.lpSecurityDescriptor = NULL;
  CreatePipe(&StdOutPipeRead, &StdOutPipeWrite, &SA, 0);
  try
  {
    setmem(&SI, sizeof(SI), 0);
    SI.cb = sizeof(SI);
    SI.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    SI.wShowWindow = Flag;
    SI.hStdInput = (unsigned)GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin
    SI.hStdOutput = (unsigned int)StdOutPipeWrite;
    SI.hStdError = (unsigned int)StdOutPipeWrite;
    WorkDir = AWorkDir;
    Handle = CreateProcess(NULL, CommandLine.c_str(),
                            NULL, NULL, true, 0, NULL,
                            WorkDir.c_str(), (STARTUPINFO*)&SI, &PI);
  }
  catch(Exception &e)
  {
  }

  CloseHandle(StdOutPipeWrite);

  if (Handle)
  {
    try
    {
      do
      {
        WasOK = ReadFile(StdOutPipeRead, Buffer, 50, &BytesRead, NULL);
        if(BytesRead > 0)
        {
          Buffer[BytesRead] = 0;
          AOutputLine = Buffer;
          Result = Result + AOutputLine;
        }
      }while(WasOK && (BytesRead != 0));
      WaitForSingleObject(PI.hProcess, INFINITE);
	}
    __finally
    {
      CloseHandle(PI.hThread);
      CloseHandle(PI.hProcess);
    }
  }

  CloseHandle(StdOutPipeRead);
  return Result;
}

//---------------------------------------------------------------------------
String __fastcall GetShortName(String sLongName)
{
	String ret;
	String sShortName;
	int nShortNameLen;

	sShortName.SetLength(MAX_PATH);
	nShortNameLen = GetShortPathName(
		sLongName.c_str(), sShortName.c_str(),
		MAX_PATH - 1);
	if (nShortNameLen != 0)
	{
		sShortName.SetLength(nShortNameLen);
		ret = sShortName;
	}
	return ret;
}

//---------------------------------------------------------------------------
String __fastcall GetApkVersion(String FileName)
{
	String ret;
	String AppPath = GetShortName(ExtractFileDir(Application->ExeName));
	FileName = GetShortName(FileName);
	if(FileName != "")
	{
	  String cmd = (String)"cmd /c \""+ AppPath + "\\aapt.exe\"" + " dump badging " + FileName;
	  String res = ShellExecutePipe(cmd, AppPath);
	  TStringList *sl = new TStringList;
	  sl->Delimiter = ' ';
	  sl->DelimitedText = res;
	  ret = AnsiDequotedStr(sl->Values["versionName"], '\'');
	  if(ret != "")
		ret += ".";
      ret += AnsiDequotedStr(sl->Values["versionCode"], '\'');
      delete sl;
    }
    return ret;
}

//---------------------------------------------------------------------------

Всем спасибо за участие.

Ссылка на комментарий

Присоединяйтесь к обсуждению

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

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...