在windows环境下,qt从mtp设备中获取文件

需求描述


1.在window环境下 
2.使用qt 
3.从mtp设备中获取文件。

很显然这么奇葩的需求,肯定不是我自己想做的,没错的,你猜对了。是一个项目的需求。首先分析这个需求,我认为最难的一点就是基于window来做。(当然那些专业搞c#的人,整天研究大微软的人例外)。这个东西放在linux下简直太easy了。正因为在window下来做,所以难点就来了:首先你需要能获取到mtp设备下的文件,你要明白mtp设备是没有盘符的,这就意味着你指望qt中的方法调用肯定是没法实现了,所以这就成为了第一个难点,第二个难点:就是你得集成进qt里边去。当然这个第二个难点取决于你是怎么来解决第一个难点的。下面我就来说说我是如何一步一步干掉这些个问题的。


1.第一个坑:从mtp设备中获取文件

1.1 第一个大坑中的第一个小坑:采取何种方式获取mtp设备中的文件。

首先,必须得着手解决掉这个问题,于是我开始我的万里长征第一步,开始漫无目的的查资料。其实在网上翻过一遍之后,方法无非就是俩种:一种就是微软的那一套,直接去调用微软的API,各种C#,不过我一想到VISUAL STUDIO安装包就是几个G,而且作为一个平时只使用Ubuntu的码农来说,这种方法一开始在我的脑海中就被毙掉了。第二种就是基于libusb实现的libmtp。说实话,我一开始的时候计划按照这种方法来做的,不过资料全是基于linux的,我再看这个资料的过程中,无意中发现了一个从来没有听说过的东西Powershell.
一看到这个神奇的东西中带了shell,我就心里莫名的开心,因为我知道它和linux中的shell肯定多多少少会有联系。只要有联系,让我去搞这个东西,我就有把握。果断决定采取powershell这种方法。
powershell这种东西就是微软搞得一套自己的shell.(个人感觉是因为微软自己感觉Linux的shell太好用了,所以眼红了,就模仿人家搞了一套,不过我想说微软的这个powershell简直就是辣鸡!!!)。而且有一点很重要win7之后的windows都内置了powershell。看到这一点我就决定了要用这一套,因为它在windows上起码不用做多余的配置。

1.2 第一个大坑中的第二个小坑:利用Powershell去复制mtp设备中的文件。

写到这儿,(我必须吐槽一下:windows的powershell.既然你要模仿,你就给我好好地模仿…)作为一个习惯穿梭在linux下shell脚本的码农来说,powershell的语法我用晦涩难懂,简直就是辣鸡,来形容一点儿也不为过。接下来进入正题吧,下面是我调试通的从mtp设备中拷贝文件的shell脚本代码。

function Get-ShellProxy
{
  if( -not $global:ShellProxy) { 
  $global:ShellProxy = new-object -com Shell.Application
  }
  $global:ShellProxy
}
function Get-ShellItem
{
 param($Path=17)
 $shell=Get-ShellProxy
 trap [System.Management.Automation.MethodInvocationException]
 {
  $Path = $Path.ToString()
  $dir = $Path.Substring( 0 ,$Path.LastIndexOf('\') )
  return $shell.NameSpace($dir).items()  | 
   where { (-not $_.IsFolder ) -and  $_.path -eq $Path} |
   select -First 1
  continue
 }
 $shell.NameSpace($Path).self
}
function Get-ChildShellItem
{
 param(
  $Path=17,
  [switch]$Recurse,
  $Filter=$null)
 Filter myFilter
 {
   if($Filter){
   $_ | where { $_.Name -match $Filter }
   }
   else{
   $_
   }
 }
 
 $shellItem = Get-ShellItem $Path
 $shellItem | myFilter
 if( $shellItem.IsFolder ){ 
  if($Recurse) {
   $shellItem | myFilter
   $stack=New-Object System.Collections.Stack
   $stack.Push($shellItem)
   while($stack.Count -gt 0)
   {
     $top = $stack.Pop()
     $top | myFilter
     $top.GetFolder.items() | foreach {
       if( $_.IsFolder )
       { $stack.Push($_) }
       else { $_ | myFilter }
     }
   }
  }
  else{
  $shellItem.GetFolder.items() | myFilter }
  }
}
function Copy-ShellItem
{
 param($Path,$Destination)
 $shell=Get-ShellProxy
 $shell.NameSpace($Destination).Copyhere($Path,16)
}
$des = '你需要拷贝到PC中的位置'
$phone = Get-ChildShellItem | where { $_.name -eq '你的设备名称' }
Get-ChildShellItem -Path "$($phone.Path)\sdcard" -Filter '(.json)|(.js)$' | foreach {
 echo $_
 Copy-ShellItem -Path $_ -Destination $des
}

第二个坑:集成进qt中

当我成功的跳过第一个坑之后,在powershell中执行完脚本之后,看到文件成功复制到了我pc上的文件夹之后,别提我有多高兴了。根据我对linux调用脚本的easy程度,我以为在windos进行集成也很容易。结果我又一次的错了。第二个坑丝毫不亚于第一个坑。我只能硬着头皮开始了跨越第二坑的过程。

2.1第二个坑中的第一个小坑:采用何种方法进行集成

我开始了各种尝试在qt中去运行powershell脚本,不料我认为我都能用的方法,结果调试都没通了。最后的最后,想了一个大招,因为知道,exe可执行文件是可以在qt中被跑起来的,所以我就想到把脚本编成可执行文件的形式(其实这一步走弯路了,不过实在是没有办法)。下面是我调试可用的powershell脚本是用来生成可执行文件的。

function Convert-PS1ToExe
{
    param(
    [Parameter(Mandatory=$true)]
    [ValidateScript({$true})]
    [ValidateNotNullOrEmpty()]   
    [IO.FileInfo]$ScriptFile
    )
    if( -not $ScriptFile.Exists)
    {
        Write-Warning "$ScriptFile not exits."
        return
    }
 
    [string]$csharpCode = @'
    using System;
    using System.IO;
    using System.Reflection;
    using System.Diagnostics;
    namespace LoadXmlTestConsole
    {
        public class ConsoleWriter
        {
            private static void Proc_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
            {
                Process pro = sender as Process;
                Console.WriteLine(e.Data);
            }
            static void Main(string[] args)
            {
                // Set title of console
                Console.Title = "Powered by PSTips.Net";
 
                // read script from resource
                Assembly ase = Assembly.GetExecutingAssembly();
                string scriptName = ase.GetManifestResourceNames()[0];
                string scriptContent = string.Empty;
                using (Stream stream = ase.GetManifestResourceStream(scriptName))
                using (StreamReader reader = new StreamReader(stream))
                {
                    scriptContent = reader.ReadToEnd();
                }
 
                string scriptFile = Environment.ExpandEnvironmentVariables(string.Format("%temp%\\{0}", scriptName));
                try
                {
                    // output script file to temp path
                    File.WriteAllText(scriptFile, scriptContent);
 
                    ProcessStartInfo proInfo = new ProcessStartInfo();
                    proInfo.FileName = "PowerShell.exe";
                    proInfo.CreateNoWindow = true;
                    proInfo.RedirectStandardOutput = true;
                    proInfo.UseShellExecute = false;
                    proInfo.Arguments = string.Format(" -File {0}",scriptFile);
 
                    var proc = Process.Start(proInfo);
                    proc.OutputDataReceived += Proc_OutputDataReceived;
                    proc.BeginOutputReadLine();
                    proc.WaitForExit();
                    Console.WriteLine("Hit any key to continue...");
                    Console.ReadKey();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Hit Exception: {0}", ex.Message);
                }
                finally
                {
                    // delete temp file
                    if (File.Exists(scriptFile))
                    {
                        File.Delete(scriptFile);
                    }
                }
 
            }
 
        }
    }
'@
 
    # $providerDict
    $providerDict = New-Object 'System.Collections.Generic.Dictionary[[string],[string]]'
    $providerDict.Add('CompilerVersion','v4.0')
    $codeCompiler = [Microsoft.CSharp.CSharpCodeProvider]$providerDict
 
    # Create the optional compiler parameters
    $compilerParameters = New-Object 'System.CodeDom.Compiler.CompilerParameters'
    $compilerParameters.GenerateExecutable = $true
    $compilerParameters.GenerateInMemory = $true
    $compilerParameters.WarningLevel = 3
    $compilerParameters.TreatWarningsAsErrors = $false
    $compilerParameters.CompilerOptions = '/optimize'
    $outputExe = Join-Path $ScriptFile.Directory "$($ScriptFile.BaseName).exe"
    $compilerParameters.OutputAssembly =  $outputExe
    $compilerParameters.EmbeddedResources.Add($ScriptFile.FullName) > $null
    $compilerParameters.ReferencedAssemblies.Add( [System.Diagnostics.Process].Assembly.Location ) > $null
 
    # Compile Assembly
    $compilerResult = $codeCompiler.CompileAssemblyFromSource($compilerParameters,$csharpCode)
 
    # Print compiler errors
    if($compilerResult.Errors.HasErrors)
    {
        Write-Host 'Compile faield. See error message as below:' -ForegroundColor Red
        $compilerResult.Errors | foreach {
            Write-Warning ('{0},[{1},{2}],{3}' -f $_.ErrorNumber,$_.Line,$_.Column,$_.ErrorText )
        }
    }
    else
    {
         Write-Host 'Compile succeed.' -ForegroundColor Green
         "Output executable file to '$outputExe'"
    }
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值