Unicode版本的ClipboardAsString

时间:2016-06-07 21:18:38

标签: delphi unicode

我已经使用Peter Below的APIClipboard部门多年,但它不再适用于Unicode Delphi。

ClipboardAsString返回gobbledegook:

[HttpPost]
public async Task<IActionResult> Upload(IList<IFormFile> files)
{
  foreach (IFormFile source in files)
  {
    string filename = ContentDispositionHeaderValue.Parse(source.ContentDisposition).FileName.Trim('"');

    filename = this.EnsureCorrectFilename(filename);

    using (FileStream output = System.IO.File.Create(this.GetPathAndFilename(filename)))
      await source.CopyToAsync(output);
  }

  return this.RedirectToAction("Index");
}

private string EnsureCorrectFilename(string filename)
{
  if (filename.Contains("\\"))
    filename = filename.Substring(filename.LastIndexOf("\\") + 1);

  return filename;
}

private string GetPathAndFilename(string filename)
{
  return this.HostingEnvironment.WebRootPath + "\\files\\" + filename;
}

StringToClipboard仅复制第一个字符:

Procedure DataFromClipboard( fmt: DWORD; S: TStream );
  Var
    hMem: THandle;
    pMem: Pointer;
    datasize: DWORD;
  Begin { DataFromClipboard }
    Assert( Assigned( S ));
    hMem := GetClipboardData( fmt );
    If hMem <> 0 Then Begin
      datasize := GlobalSize( hMem );
      If datasize > 0 Then Begin
        pMem := GlobalLock( hMem );
        If pMem = Nil Then
          raise EclipboardError.Create( eLockFailed );
        try
          S.WriteBuffer( pMem^, datasize );
        finally
          GlobalUnlock( hMem );
        end;
      End;
    End;
  End;



Procedure CopyDataFromClipboard( fmt: DWORD; S: TStream );
  Begin { CopyDataFromClipboard }
    Assert( Assigned( S ));
    If OpenClipboard( 0 ) Then
      try
        DataFromClipboard( fmt , S );
      finally
        CloseClipboard;
      end
    Else
      raise EclipboardError.Create( eCannotOpenClipboard );
  End; 


Function ClipboardAsString: String;
  Const
    nullchar: Char = #0;
  Var
    ms: TMemoryStream;
  Begin { ClipboardAsString }
    If not IsClipboardFormatAvailable( CF_TEXT ) Then
      Result := EmptyStr
    Else Begin
      ms:= TMemoryStream.Create;
      try
        CopyDataFromClipboard( CF_TEXT , ms );
        ms.Seek( 0, soFromEnd );
        ms.WriteBuffer( nullChar, Sizeof( nullchar ));
        Result := Pchar( ms.Memory );
      finally
        ms.Free;
      end;
    End;
  End; 

我已搜索但无法找到此单元的更新版本。有更多Unicode字符串经验的人是否知道解决此问题的最佳方法?

由于

1 个答案:

答案 0 :(得分:5)

CF_TEXT是Ansi,CF_UNICODETEXT是Unicode。需要根据string是Ansi还是Unicode来更新代码以使用适当的格式,例如:

Const
  CFTextFmt = {$IFDEF UNICODE}CF_UNICODETEXT{$ELSE}CF_TEXT{$ENDIF};

Function ClipboardAsString: String;
  Var
    ms: TMemoryStream;
  Begin { ClipboardAsString }
    If not IsClipboardFormatAvailable( CFTextFmt ) Then
      Result := EmptyStr
    Else Begin
      ms := TMemoryStream.Create;
      try
        CopyDataFromClipboard( CFTextFmt, ms );
        SetString(Result, PChar(ms.Memory), ms.Size);
      finally
        ms.Free;
      end;
    End;
  End; 

Procedure StringToClipboard( Const S: String );
  Begin
    CopyDataToClipboard( CFTextFmt, PChar(S)^, (Length(S) + 1) * SizeOf(Char));
  End;

或者,你可以使用VCL自己的TClipboard.AsText属性,它会为你处理这些细节:

uses
  Clipbrd;

Function ClipboardAsString: String;
  Begin
    Result := Clipboard.AsText;
  End; 

Procedure StringToClipboard( Const S: String );
  Begin
    Clipboard.AsText := S;
  End;

据说,在旁注中,DataToClipboard()有一些错误。它应该允许datasize为0并且不要忽略它,否则无法存储空白数据(这是可取的)。它不需要使用GMEM_ZEROINIT(不是错误,但是浪费了开销)。如果HGLOBAL失败,则需要释放SetClipboardData()

Procedure DataToClipboard( fmt: DWORD; Const data; datasize: Integer );
  Var
    hMem: THandle;
    pMem: Pointer;
  Begin { DataToClipboard }
    If datasize < 0 Then datasize := 0;
    hMem := GlobalAlloc( GMEM_MOVEABLE or GMEM_SHARE, datasize );
    If hMem = 0 Then
      raise EclipboardError.Create( eSystemOutOfMemory );
    Try
      If datasize > 0 Then 
      Begin
        pMem := GlobalLock( hMem );
        If pMem = Nil Then
          raise EclipboardError.Create( eLockFailed );
        Try
          Move( data, pMem^, datasize );
        Finally
          GlobalUnlock( hMem );
        End;
      End;
      If SetClipboardData( fmt, hMem ) = 0 Then
        raise EClipboarderror( eSetDataFailed );
    Except
      GlobalFree( hMem );
      raise;
    End;
  End; { DataToClipboard }

CopyDataToClipboard()为True时,emptyClipboardFirst中还有一个错误:

  

如果应用程序在hwnd设置为NULL的情况下调用OpenClipboard,则EmptyClipboard会将剪贴板所有者设置为NULL; 导致SetClipboardData失败

因此,在清空剪贴板然后在其上放置新数据时,您必须将有效的非零HWND传递给OpenClipboard()