Problem creating Packages using Automation API after upgrading to version 16.1.8.4404

We have our own wrapper that we were using from the VBA to create projects and packages. After recent update to 16.1.8.4404 it stopped working.

In version 16.1.5.4270 is everything working fine.

Tested also with another tool/autmation kit STraSAK that originaly developed Mr. Mazanek without luck. https://github.com/EvzenP/STraSAK

Thank you for your help.

Description of error>

Error: Could not load file or assembly 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.
System.IO.FileNotFoundException: Could not load file or assembly 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.

File name: 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
   at System.Span`1..ctor(T[] array)
   at SharpCompress.Writers.Zip.ZipWriter.WriteEndRecord(UInt64 size)
   at SharpCompress.Writers.Zip.ZipWriter.Dispose(Boolean isDisposing)
   at SharpCompress.Writers.AbstractWriter.Dispose()
   at Sdl.ProjectApi.Implementation.ZipCompress.ZipDirectory(String packageFilepath, String localDataFolder)
   at Sdl.ProjectApi.Implementation.PackageProjectArchiver.ZipDirectory(String packageFilepath, String localDataFolder)
   at Sdl.ProjectApi.Implementation.PackageProject.CreatePackage(String packageFilePath, Boolean saveProject)
   at Sdl.ProjectApi.Implementation.AbstractProjectPackageCreation.StartImpl()
   at Sdl.ProjectApi.Implementation.PackageOperation.Start()

Parents
  • Hi

    Can you confirm if this issue occurs after you have successfully recompiled your code against the assemblies that are installed with SR2?

  • Unfortunately it is not working. The same error is displayed.

    I had to donwgrade to previous functioning trados version on all computers where new versions were installed.. it is a bit annoying because we use trados mostly in automated workflow.

    Analyses and project creation is functioning normally but package are NOT even in SR2.

    Please advice.

    Marian

  • Hi

    Binding redirect in the app config : This is used in visual studio only but I don't use this tool to develop our application
    Register to the GAC : Which DLL ? Which version ? How to find it ? this is not a workable solution because it would be necessary to register in the GAC for each computer that uses the API, and GAC commands are installed with visual studio so it would also be necessary to install visual studio on each computer

  • The binding redirect in SDL TradosStudio.exe.config is ok (see bellow) and when this is changed to 4.0.4.1 it obviously throws same error as 4.0.4.1 is no longer installed in the system.

    This is not exactly my filed of expertize but  It seems like that some of the dlls used in API calls (like Sdl.ProjectAutomation.Core.dll or Sdl.ProjectAutomation.FileBased.dll ) still refers to previous version of System.Runtime.CompilerServices.Unsafe  Version=4.0.4.1.  that is no longer avaiable??

    For me also make more sense to fix it there and not to try to find complicated workarounds.

    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
          <dependentAssembly>
            <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
            <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
          </dependentAssembly>
        </assemblyBinding>

  • Hi!

    The binding redirect should be added to your app, not in Trados, as your app seems to have a reference to an older version of the DLL in question.

  • We did not have any reference to System.Runtime.CompilerServices.Unsafe.dll before so I could not have possiblity to update or change what never existed....this dll seems to part of trados folder after recent updates..

    I attach one example part of a powershel code that can be used for package creation. Where exactly it should be changed?   

    param([String]$StudioVersion = "Studio5")
    
    if ("${Env:ProgramFiles(x86)}") {
        $ProgramFilesDir = "${Env:ProgramFiles(x86)}"
    }
    else {
        $ProgramFilesDir = "${Env:ProgramFiles}"
    }
    
    Add-Type -Path "$ProgramFilesDir\SDL\SDL Trados Studio\$StudioVersion\Sdl.ProjectAutomation.FileBased.dll"
    Add-Type -Path "$ProgramFilesDir\SDL\SDL Trados Studio\$StudioVersion\Sdl.ProjectAutomation.Core.dll"
    
    
    $LanguagesSeparator = "\s+|;\s*|,\s*"
    
    $ProjectPackageExtension = ".sdlppx"
    $ReturnPackageExtension = ".sdlrpx"
    
    function Export-Package {
    <#
    .SYNOPSIS
    Exports Trados Studio project packages from project.
    .DESCRIPTION
    Exports Trados Studio project packages from project to a specified location, allowing to define specific export options.
    Packages location is created automatically. Separate package is created for every target language.
    .EXAMPLE
    Export-Package -ProjectLocation "D:\Project" -PackageLocation "D:\Packages"
    
    Creates translation packages for all target languages defined in project located in "D:\Project" folder;
    packages will be created in "D:\Packages" folder;
    no project TMs, main TMs, termbases, etc. will be included in the packages.
    .EXAMPLE
    Export-Package -ProjectLocation "D:\Project" -PackageLocation "D:\Packages" -TargetLanguages "fi-FI,sv-SE" -IncTM -IncTB
    
    Creates translation package for Finnish and Swedish target languages from project located in "D:\Project" folder;
    package will be created in "D:\Packages" folder;
    project TM will not be included in package, main TM and termbase will be included in package.
    #>
    	[CmdletBinding()]
    
    	param(
    		# Path to directory where the project is located.
    		[Parameter (Mandatory = $true)]
    		[Alias("Location","PrjLoc")]
    		[String] $ProjectLocation,
    
    		# Path to directory where the package will be created.
    		[Parameter (Mandatory = $true)]
    		[Alias("PkgLoc")]
    		[String] $PackageLocation,
    		
    		# Space-, comma- or semicolon-separated list of locale codes of project target languages.
    		# For locale codes, see https://msdn.microsoft.com/en-us/goglobal/bb896001.aspx
    		# If this parameter is omitted, packages for all project target languages are created.
    		[Alias("TrgLng")]
    		[String] $TargetLanguages,
    
    		# Name of manual task which will be associated with files in the package
    		[ValidateSet("Translate","Review")]
    		[String] $Task = "Translate",
    
    		# Option for project translation memory to be included in the package
    		# None - do not include any project TM
    		# UseExisting - include existing project TM
    		# CreateNew - create new project TM
    		[ValidateSet("None","UseExisting","CreateNew")]
    		[Alias("PrjTM","ProjectTMs")]
    		[String] $ProjectTM = "None",
    
    		# Optional short comment to be included in package
    		[Alias("PkgCmt")]
    		[String] $PackageComment = "",
    
    		# Include AutoSuggest dictionaries in package
    		[Alias("IncAS","IncludeAutoSuggest")]
    		[Switch] $IncludeAutoSuggestDictionaries,
    
    		# Include main translation memories in package
    		[Alias("IncTM","IncludeMainTM")]
    		[Switch] $IncludeMainTMs,
    
    		# Include termbases in package
    		[Alias("IncTB","IncludeTermbase")]
    		[Switch] $IncludeTermbases,
    
    		# Recompute wordcount and analysis to update cross-file repetition counts
    		# and include the recomputed reports in package
    		[Alias("RecAna","Recompute","RecomputeAnalyse","RecomputeAnalyze")]
    		[Switch] $RecomputeAnalysis,
    
    		# Include existing wordcount reports in package
    		[Alias("IncRep", "IncludeExisting","IncludeExistingReport","IncludeReports","IncludeReport")]
    		[Switch] $IncludeExistingReports,
    
    		# Keep automated translation providers information in package
    		[Alias("KeepAT","KeepATProviders","KeepATProvider")]
    		[Switch] $KeepAutomatedTranslationProviders,
    
    		# Remove links to server-based translation memories from package
    		[Alias("RmvSrvTM","RemoveServerTMs","RemoveServerTM")]
    		[Switch] $RemoveServerBasedTMs
    	)
    
    	# If package location does not exist, create it
    	if (!(Test-Path $PackageLocation)) 	{
    		New-Item -Path $PackageLocation -Force -ItemType Directory | Out-Null
    	}
    
    	# Initialize default package creation options
    	$PackageOptions = [Sdl.ProjectAutomation.Core.ProjectPackageCreationOptions] (New-DefaultPackageOptions)
    
    	# Cast ProjectTM string value to corresponding enumeration value
    	$PackageOptions.ProjectTranslationMemoryOptions = [Sdl.ProjectAutomation.Core.ProjectTranslationMemoryPackageOptions] $ProjectTM
    
    	# Workaround for "create new project TM" option not actually working unless "include reports" is also set
    	if ($ProjectTM -eq "CreateNew") {
    		# if the IncludeReports property exists, set it to true
    		# (this property was introduced only in Studio 2015 SR2 CU7)
    		if (Get-Member -InputObject $PackageOptions -Name "IncludeReports" -MemberType Properties) {
    			$PackageOptions.IncludeReports = $true
    		}
    	}
    
    	# Set options according to provided switches
    	if ($IncludeAutoSuggestDictionaries) {
    		$PackageOptions.IncludeAutoSuggestDictionaries = $true
    	}
    	if ($IncludeMainTMs) {
    		$PackageOptions.IncludeMainTranslationMemories = $true
    	}
    	if ($IncludeTermbases) {
    		$PackageOptions.IncludeTermbases = $true
    	}
    	if ($KeepAutomatedTranslationProviders) {
    		$PackageOptions.RemoveAutomatedTranslationProviders = $false
    	}
    	if ($RemoveServerBasedTMs) {
    		$PackageOptions.RemoveServerBasedTranslationMemories = $true
    	}
    	if ($IncludeExistingReports) {
    		# if the IncludeExistingReports property exists, set it to true
    		# (this property was introduced only in Studio 2015 SR2 CU7)
    		if (Get-Member -InputObject $PackageOptions -Name "IncludeExistingReports" -MemberType Properties) {
    			$PackageOptions.IncludeExistingReports = $true
    		}
    		# if the IncludeReports property exists, set it to true
    		# (this property was introduced only in Studio 2015 SR2 CU7)
    		if (Get-Member -InputObject $PackageOptions -Name "IncludeReports" -MemberType Properties) {
    			$PackageOptions.IncludeReports = $true
    		}
    	}
    	if ($RecomputeAnalysis) {
    		$PackageOptions.RecomputeAnalysisStatistics = $true
    		# if the IncludeReports property exists, set it to true
    		# (this property was introduced only in Studio 2015 SR2 CU7)
    		if (Get-Member -InputObject $PackageOptions -Name "IncludeReports" -MemberType Properties) {
    			$PackageOptions.IncludeReports = $true
    		}
    	}
    	
    	# According to info from SDL developer forum, using [DateTime]::MaxValue sets "no package due date"
    	$PackageDueDate = [DateTime]::MaxValue
    
    	$Project = Get-Project (Resolve-Path -LiteralPath $ProjectLocation).ProviderPath
    
    	if ($TargetLanguages -ne $null -and $TargetLanguages -ne "") {
    		# Parse target languages from provided parameter
    		$TargetLanguagesList = $TargetLanguages -Split $LanguagesSeparator
    	}
    	else {
    		# Get project languages
    		$TargetLanguagesList = @($Project.GetProjectInfo().TargetLanguages.IsoAbbreviation)
    	}
    
    	Write-Host "`nCreating packages..." -ForegroundColor White
    
    	# Loop through target languages and create package for each one
    	Get-Languages $TargetLanguagesList | ForEach {
    
    		$Language = $_
    		$User = "$($Language.IsoAbbreviation) translator"
    		# Set package name to project name with target language ISO code suffix
    		$PackageName = "$($Project.GetProjectInfo().Name)_$($Language.IsoAbbreviation)"
    		
    		Write-Host "$PackageName$ProjectPackageExtension"
    		
    		# Get TaskFileInfo (files list) data for the target language's project files
    		[Sdl.ProjectAutomation.Core.TaskFileInfo[]] $TaskFiles = Get-TaskFileInfoFiles $Project $Language
    		# Create the manual task which will be associated with files being included in the package
    		[Sdl.ProjectAutomation.Core.ManualTask] $ManualTask = $Project.CreateManualTask($Task, $User, $PackageDueDate, $TaskFiles)
    
    		# Create package containing the manual task
    		[Sdl.ProjectAutomation.Core.ProjectPackageCreation] $Package = $Project.CreateProjectPackage($ManualTask.Id, $PackageName, $PackageComment, $PackageOptions, ${function:Write-PackageProgress}, ${function:Write-PackageMessage})
    
    		# Save the package to file in specified location
    		if ($Package.Status -eq [Sdl.ProjectAutomation.Core.PackageStatus]::Completed) {
    			$Project.SavePackageAs($Package.PackageId, "$PackageLocation\$PackageName$ProjectPackageExtension")
    		}
    		else {
    			Write-Host "Package creation failed, cannot save it!"
    		}
    
    		Remove-Variable TaskFiles, ManualTask, Package
    	}
    }
    
    function Import-Package {
    <#
    .SYNOPSIS
    Imports Trados Studio return packages in project.
    .DESCRIPTION
    Imports Trados Studio return packages from specified location into a project stored in specified location.
    .EXAMPLE
    Import-Package -ProjectLocation "D:\Project" -PackageLocation "D:\Packages"
    
    Imports all translation packages found in "D:\Packages" directory (and all its eventual subdirectories) into a project located in "D:\Project" folder.
    .EXAMPLE
    Import-Package -ProjectLocation "D:\Project" -PackageLocation "D:\Packages\Handback_en-US_fi-FI.sdlrpx"
    
    Imports single  translation package from "D:\Packages\Handback_en-US_fi-FI.sdlrpx" folder  into a project located in "D:\Project" folder.
    #>
    	[CmdletBinding()]
    
    	param(
    		# Path to directory where the project is located.
    		[Parameter (Mandatory = $true)]
    		[Alias("Location","PrjLoc")]
    		[String] $ProjectLocation,
    
    		# Path to either a single package, or directory where multiple packages are located.
    		[Parameter (Mandatory = $true)]
    		[Alias("PkgLoc")]
    		[String] $PackageLocation,
    
    		# Imports also all return packages found in subdirectories of the specified path.
    		[Alias("r")]
    		[switch] $Recurse
    	)
    
    	$Project = Get-Project (Resolve-Path -LiteralPath $ProjectLocation).ProviderPath
    
    	Write-Host "`nImporting packages..." -ForegroundColor White
    
    	Get-ChildItem $PackageLocation *.sdlrpx -File -Recurse:$Recurse | ForEach {
    		Write-Host "$($_.Name)"
    		$PackageImport = $Project.ImportReturnPackage($_.FullName, ${function:Write-PackageProgress}, ${function:Write-PackageMessage})
    	}
    }
    
    function Write-PackageProgress {
    	param(
    	$Caller,
    	$ProgressEventArgs
    	)
    
    	$Cancel = $ProgressEventArgs.Cancel
    	$Message = $ProgressEventArgs.StatusMessage
    
    	if ($Message -ne $null -and $Message -ne "") {
    		$Percent = $ProgressEventArgs.PercentComplete
    		if ($Percent -eq 100) {
    			$Message = "Completed"
    		}
    
    		# write textual progress percentage in console
    		if ($host.name -eq 'ConsoleHost') {
    			Write-Host "$($Percent.ToString().PadLeft(5))%	$Message"
    			Start-Sleep -Seconds 1
    		}
    		# use PowerShell progress bar in PowerShell environment since it does not support writing on the same line using `r
    		else {
    			Write-Progress -Activity "Processing task" -PercentComplete $Percent -Status $Message
    			# when all is done, remove the progress bar
    			if ($Percent -eq 100 -and $Message -eq "Completed") {
    				Write-Progress -Activity "Processing task" -Completed
    			}
    		}
    	}
    }
    
    function Write-PackageMessage {
    	param(
    	$Caller,
    	$MessageEventArgs
    	)
    
    	$Message = $MessageEventArgs.Message
    
    	# do not pollute output with potentially unnecessary lines
    	if ($Message.Source -ne "Package import") {
    		Write-Host "$($Message.Source)" -ForegroundColor DarkYellow
    	}
    	Write-Host "$($Message.Level): $($Message.Message)" -ForegroundColor Magenta
    	if ($Message.Exception) {
    		Write-Host "$($Message.Exception)" -ForegroundColor Magenta
    	}
    }
    
    function New-PackageOptions {
    	[Sdl.ProjectAutomation.Core.ProjectPackageCreationOptions] $PackageOptions = New-Object Sdl.ProjectAutomation.Core.ProjectPackageCreationOptions
    	return $PackageOptions
    }
    
    function New-DefaultPackageOptions {
    	[Sdl.ProjectAutomation.Core.ProjectPackageCreationOptions] $PackageOptions = New-Object Sdl.ProjectAutomation.Core.ProjectPackageCreationOptions
    	$PackageOptions.IncludeAutoSuggestDictionaries = $false
    	$PackageOptions.IncludeMainTranslationMemories = $false
    	$PackageOptions.IncludeTermbases = $false
    	$PackageOptions.ProjectTranslationMemoryOptions = [Sdl.ProjectAutomation.Core.ProjectTranslationMemoryPackageOptions]::None
    	if ($PackageOptions.IncludeReports) {$PackageOptions.IncludeReports = $false}
    	if ($PackageOptions.IncludeExistingReports) {$PackageOptions.IncludeExistingReports = $false}
    	$PackageOptions.RecomputeAnalysisStatistics = $false
    	$PackageOptions.RemoveAutomatedTranslationProviders = $true
    	$PackageOptions.RemoveServerBasedTranslationMemories = $false
    	return $PackageOptions
    }
    
    Export-ModuleMember Export-Package
    Export-ModuleMember Import-Package
    #Export-ModuleMember New-PackageOptions
    #Export-ModuleMember New-DefaultPackageOptions
    

  • That's the problem, in my app there is no reference to any version of the DLL, the DLL v 4.0.4.1 don't exist in my PC,

    I suggest that you do an update of Trados so that the API has the right binding automatically

  • I don't know powershell that well but wasn't this about Studio 2021 (16.1.5.4270 and 16.1.8.4404)? I'm asking this because in the code you provided, you make reference to "Studio5" folder in ProgramFiles, which is Studio 2017's folder... 

  • Yes we are talking about Studio 2021. This code was running like a charm in Studo 2021 (16.1.5.4270) but after update it is NOT.

    Regarding your comment about reference to Studio5. The correct version is defined in main batch file so it is not relevant..see bellow

    Thank you.

    <# : ----------------- begin batch part -----------------
    @echo off
    
    :: get Documents folder location from registry
    for /f "tokens=2*" %%A in ('reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" /v "Personal" 2^>nul') do call set "DocumentsFolder=%%B"
    :: add Modules location to PSModulePath variable
    set "PSModulePath=%DocumentsFolder%\WindowsPowerShell\Modules\;%PSModulePath%"
    
    :: PowerShell location
    set POWERSHELL=%windir%\system32\WindowsPowerShell\v1.0\powershell.exe
    :: use 32-bit version on 64-bit systems!
    if exist "%windir%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe" (set POWERSHELL=%windir%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe)
    
    :: from http://www.dostips.com/forum/viewtopic.php?f=3&t=5526&start=15#p45502
    :: invoke embedded PowerShell code + code specified on command line
    setlocal enabledelayedexpansion
    set _args=%*
    rem this is to prevent PS errors if launched with empty command line
    if not defined _args set "_args= "
    set _args=!_args:'=''!
    set _args=!_args:"="""!
    type "%~f0" | %POWERSHELL% -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "%POWERSHELL% -NoLogo -NoProfile -ExecutionPolicy Bypass -Command ([ScriptBlock]::Create([Console]::In.ReadToEnd()+';!_args!'))"
    endlocal
    
    exit /b 0
    ----------------- end batch part ----------------- #>
    
    [cultureinfo]::DefaultThreadCurrentCulture = 'en-GB'
    
    # import SDL Powershell Toolkit modules
    $StudioVersion = "Studio16"
    $Modules = @("TMHelper","ProjectHelper","GetGuids","PackageHelper")
    ForEach ($Module in $Modules) {Import-Module -Name $Module -ArgumentList $StudioVersion}

  • Hi , ,

    Trados Studio installs version 5 of that assembly with the product.

    I'm trying to understand why it's asking for version 4.0.4.1; this is a puzzle, given the input so far.

    What I would try to understand next is where this reference is creeping in from:

    • Is this problematic only when you are executing the automation via the powershell script?
    • What is executing/calling the script?  I want to exclude that this version isn't loaded from the application context that is executing the script... TBC

    In the meantime I will run some tests on my side to determine if I can reproduce; concentrating on Package export/import automation, standalone and directly as a script from your example above.

  • Hi 

    I was successful in reproducing this issue while running the powershell script that you have made note of above. The previous version of that assembly is being resolved because of a dependency in one of the references (System.Memory).

    To fix, simply include a binding redirect in the script to ensure the correct version of that assembly is resolved in the current App Domain during execution, as follows:

    $compileServicesAssembly = [System.Reflection.Assembly]::LoadFrom("$ProgramFilesDir\SDL\SDL Trados Studio\$StudioVersion\System.Runtime.CompilerServices.Unsafe.dll")
    $onAssemblyResolveEventHandler = [System.ResolveEventHandler] {
      param($sender, $e) 
      if ($e.Name.StartsWith("System.Runtime.CompilerServices.Unsafe")) {
        return $compileServicesAssembly
      }
      foreach($assembly in [System.AppDomain]::CurrentDomain.GetAssemblies()) {
        if ($assembly.FullName -eq $e.Name) {
          return $assembly
        }
      }
      return $null
    }
    [System.AppDomain]::CurrentDomain.add_AssemblyResolve($onAssemblyResolveEventHandler)

    Note: Add this code, before you load the modules and remember to remove the reference when exiting the PS

    Example:

    cls
    
    if ("${Env:ProgramFiles(x86)}") {
        $ProgramFilesDir = "${Env:ProgramFiles(x86)}"
    }
    else {
        $ProgramFilesDir = "${Env:ProgramFiles}"
    }
    
    $StudioVersion = "Studio16"
    
    $Modules = @("TMHelper","ProjectHelper","GetGuids","PackageHelper")
    
    
    Write-Host "Add binding redirect to 'System.Runtime.CompilerServices.Unsafe.dll'";
    
    $compileServicesAssembly = [System.Reflection.Assembly]::LoadFrom("$ProgramFilesDir\SDL\SDL Trados Studio\$StudioVersion\System.Runtime.CompilerServices.Unsafe.dll")
    $onAssemblyResolveEventHandler = [System.ResolveEventHandler] {
      param($sender, $e) 
      if ($e.Name.StartsWith("System.Runtime.CompilerServices.Unsafe")) {
        return $compileServicesAssembly
      }
      foreach($assembly in [System.AppDomain]::CurrentDomain.GetAssemblies()) {
        if ($assembly.FullName -eq $e.Name) {
          return $assembly
        }
      }
      return $null
    }
    [System.AppDomain]::CurrentDomain.add_AssemblyResolve($onAssemblyResolveEventHandler)
    
    Write-Host "Load PowerShell Toolkit modules.";
    
    ForEach ($Module in $Modules) {Import-Module -Name $Module -ArgumentList $StudioVersion}
    
    
    Write-Host "Test: Export packages.";
    
    Export-Package -ProjectLocation "C:\MyProjects\Project 1\Project 1.sdlproj" -PackageLocation "C:\Packages"
    
    Write-Host "Completed.";
    
    Remove-Module -Name "TMHelper";
    Remove-Module -Name "ProjectHelper";
    Remove-Module -name "GetGuids";
    Remove-Module -name "PackageHelper";
    [System.AppDomain]::CurrentDomain.remove_AssemblyResolve($onAssemblyResolveEventHandler)

    Try this and let me know how it goes...

  • Dear Patrick,

    Your workaround seems to be working. We will implement this fix into the production system.

    But for any future Trados releases it would be much better when any necessary assamblies would be resolved directly in trados libraries during an API call.

    Thank you.

Reply Children