[英]Remote Debug in VS 2015 to Windows Server 2012 doesn't work
[英]Why doesn't the VS 2015 debug visualizer work on this unsafe struct?
我必須對很多東西進行哈希處理...並且我將哈希保留為一種內容標識。 我到處都用這些東西。 哈希是20字節的數組,最近我在具有ToString()
方法的ac#項目中將它們更改為一個(看似)簡單的unsafe struct
。 但是,在運行時,可視化始終是默認值(全為零)-即使在內容更改后也是如此。
結構中唯一的實例數據是固定字節數組,它可以通過許多方法寫入。 如果沒有ToString()
方法,可視化工具將顯示該值的一些la腳表示-但這是(我認為)固定數組的地址。
沒有一種排列方法會導致可視化器與默認可視化發生變化。
例如:
即使ToString()方法產生以下結果:
...這是期望值(和期望的可視化效果)。
我已經嘗試過[DebuggerDisplay( "{ToString()}" )]
並使該東西可序列化,但是仍然得到相同的結果。 因此,這是否意味着我對不安全的結構不走運,還是我做錯了我沒有發現的錯誤?
編輯 :
很抱歉沒有提供完整的,可驗證的樣本。 這是演示該問題的完整控制台應用程序。 只需用此代碼替換Class1.cs的內容,您就會看到問題。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void Main( string[ ] args )
{
// sample data...
var bytes = new byte[ 20 ] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 };
var hash1 = Hash.FromBytes( bytes ); //deserailize a hash from array
Debug.WriteLine( hash1 );
var hash2 = new Hash( bytes, 0, 20 ); //computes a hash over array
Debug.WriteLine( hash2 );
using ( var stream = new MemoryStream( bytes ) )
{
var hash3 = new Hash( );// empty hash
hash3.Read( stream ); // deserialize a hash from stream
Debug.WriteLine( hash3 );
stream.Position = 0;
var hash4 = new Hash( stream ); //compute hash over stream
Debug.WriteLine( hash4 );
var hash5 = new Hash( "Compute the hash of a string" );
Debug.WriteLine( hash5 );
Debug.Assert( hash1 == hash3, "Oops!" );
Debug.Assert( hash2 == hash4, "Nope!" );
Debug.Assert( hash1 != hash2, "Golly!" );
Debug.Assert( hash3 != hash4, "Shucks!" );
}
}
}
/// <summary>Represents a hash of a string or byte array</summary>
[StructLayout( LayoutKind.Sequential )]
public unsafe struct Hash: IComparable<Hash>
{
#region statics and constants
/// <summary>Character map for byte array to string</summary>
readonly static char[ ] hex = new char[ ] {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
'c', 'd', 'e', 'f' };
/// <summary>Synchronization primitive</summary>
readonly static object sync = new object( );
/// <summary>Buffer for reading hashes from streams, strings, and arrays</summary>
readonly static byte[ ] buffer = new byte[ 20 ];
/// <summary>ToString workspace</summary>
static char[ ] hexChars = new char[ Length * 2 ];
/// <summary>Returns a hash that has no value</summary>
public readonly static Hash EmptyHash = new Hash( );
/// <summary>Retruns the length of any <see cref="Hash"/></summary>
public const int Length = 20;
/// <summary>Returns a <see cref="HashAlgorithm"/> that the system uses to compute hashes</summary>
public static HashAlgorithm GetHasher( )
{
return new SHA1Managed( );
}
#endregion
#region private data
/// <summary>A pointer to the underlying data</summary>
fixed byte value[ 20 ];
#endregion
#region construction
/// <summary>Creates a hash from a string</summary>
public Hash( string hashable )
{
fixed ( byte* bytes = value, sourceBytes = GetHasher( ).ComputeHash( Encoding.Unicode.GetBytes( hashable ) ) )
{
NativeMethods.CopyMemory( bytes, sourceBytes, Length );
}
}
/// <summary>Creates a hash from a byte array</summary>
public Hash( byte[ ] source, int index, int length )
{
fixed ( byte* bytes = value, sourceBytes = GetHasher( ).ComputeHash( source, index, length ) )
{
NativeMethods.CopyMemory( bytes, sourceBytes, Length );
}
}
/// <summary>Creates a hash from a series of hashes</summary>
public Hash( IEnumerable<Hash> hashes )
{
var hasher = GetHasher( );
var buffer = new byte[ Length ];
hashes.Do( key =>
{
key.CopyTo( buffer );
hasher.TransformBlock( buffer, 0, Length, buffer, 0 );
} );
hasher.TransformFinalBlock( buffer, 0, 0 );
fixed ( byte* bytes = value, source = hasher.Hash )
{
NativeMethods.CopyMemory( bytes, source, Length );
}
}
/// <summary>Creates a hash over a stream from current position to end</summary>
public Hash( Stream stream )
{
const int bufferSize = 4096;
var hasher = GetHasher( );
var bytesRead = 0;
var buffer = new byte[ bufferSize ];
while ( true )
{
bytesRead = stream.Read( buffer, 0, bufferSize );
if ( bytesRead == 0 )
{
hasher.TransformFinalBlock( buffer, 0, 0 );
break;
}
else
{
hasher.TransformBlock( buffer, 0, bytesRead, buffer, 0 );
}
}
fixed ( byte* bytes = value, source = hasher.Hash )
{
NativeMethods.CopyMemory( bytes, source, Length );
}
}
#endregion
#region methods
/// <summary>Copies the hash to the start of a byte array</summary>
public void CopyTo( byte[ ] buffer )
{
CopyTo( buffer, 0 );
}
/// <summary>Copies the hash to a byte array</summary>
public void CopyTo( byte[ ] buffer, int offset )
{
if ( buffer == null ) throw new ArgumentNullException( nameof( buffer ) );
if ( buffer.Length < ( offset + Length ) ) throw new ArgumentOutOfRangeException( nameof( buffer ) );
fixed ( byte* bytes = value, dest = buffer )
{
NativeMethods.CopyMemory( dest + offset, bytes, Length );
}
}
/// <summary>Returns a byte-array representation of the <see cref="Hash"/></summary>
/// <remarks>The returned value is a copy</remarks>
public byte[ ] GetBytes( )
{
var results = new byte[ Length ];
fixed ( byte* bytes = value, target = results )
{
NativeMethods.CopyMemory( target, bytes, Length );
}
return results;
}
/// <summary>Compares this hash to another</summary>
public int CompareTo( Hash other )
{
var comparedByte = 0;
fixed ( byte* bytes = value )
{
for ( int i = 0; i < Length; i++ )
{
comparedByte = ( *( bytes + i ) ).CompareTo( other.value[ i ] );
if ( comparedByte != 0 ) break;
}
return comparedByte;
}
}
/// <summary>Returns true if <paramref name="obj"/> is a <see cref="Hash"/> and it's value exactly matches</summary>
/// <param name="obj">The <see cref="Hash"/> to compare to this one</param>
/// <returns>true if the values match</returns>
public override bool Equals( object obj )
{
if ( obj == null || !( obj is Hash ) ) return false;
var other = ( Hash ) obj;
return CompareTo( other ) == 0;
}
/// <summary>Returns a .Net hash code for this <see cref="Hash"/></summary>
public override int GetHashCode( )
{
unchecked
{
int hashCode = 17;
fixed ( byte* bytes = value )
{
for ( int i = 0; i < Length; i++ )
{
hashCode = hashCode * 31 + *( bytes + i );
}
return hashCode;
}
}
}
/// <summary>Returns a hex string representation of the hash</summary>
public override string ToString( )
{
lock ( sync )
{
fixed ( char* hexFixed = hex, hexCharsFixed = hexChars )
{
fixed ( byte* bytes = value )
{
for ( int i = 0; i < Length; i++ )
{
*( hexCharsFixed + ( i * 2 ) ) = *( hexFixed + ( *( bytes + i ) >> 4 ) );
*( hexCharsFixed + ( 1 + ( i * 2 ) ) ) = *( hexFixed + ( *( bytes + i ) & 0xf ) );
}
return new string( hexChars );
}
}
}
}
/// <summary>Reads a <see cref="Hash"/> from the provided stream</summary>
public void Read( Stream stream )
{
lock ( sync )
{
var retryCount = 0;
var bytesRead = ReadStream( stream, buffer, 0, Length, ref retryCount );
if ( bytesRead == Length )
{
fixed ( byte* bytes = value, sourceBytes = buffer )
{
NativeMethods.CopyMemory( bytes, sourceBytes, Length );
}
}
}
}
/// <summary>Tries hard to populate a <see cref="Hash"/> from a stream - across multiple reads if necessary - up to a point</summary>
int ReadStream( Stream stream, byte[ ] buffer, int offset, int length, ref int retryCount )
{
const int maxStreamReadRetries = 3;
var bytesRead = stream.Read( buffer, offset, length );
var done = bytesRead == 0 || bytesRead == length; // eos, timeout, or success
if ( !done )
{
if ( retryCount++ >= maxStreamReadRetries ) return 0;
bytesRead += ReadStream( stream, buffer, bytesRead, length - bytesRead, ref retryCount );
}
return bytesRead;
}
/// <summary>Writes the hash to a stream</summary>
public void Write( Stream stream )
{
lock ( sync )
{
fixed ( byte* bytes = value, targetBytes = buffer )
{
NativeMethods.CopyMemory( targetBytes, bytes, Length );
}
stream.Write( buffer, 0, Length );
}
}
/// <summary>Returns true if the hash has no value</summary>
public bool IsEmpty( )
{
return Equals( EmptyHash );
}
/// <summary>Returns the result of XORing two <see cref="Hash"/>es</summary>
public static Hash Combine( Hash a, Hash b )
{
var results = new Hash( );
for ( int i = 0; i < Length; i++ )
{
*( results.value + i ) = ( byte ) ( *( a.value + i ) ^ *( b.value + i ) );
}
return results;
}
/// <summary>Returns the first non-empty hash from a list</summary>
public static Hash FirstNotEmpty( params Hash[ ] hashes )
{
foreach ( var hash in hashes ) if ( !hash.IsEmpty( ) ) return hash;
throw new ArgumentOutOfRangeException( nameof( hashes ) );
}
/// <summary>Implements == operator</summary>
public static bool operator ==( Hash a, Hash b )
{
return a.Equals( b );
}
/// <summary>Implements != operator</summary>
public static bool operator !=( Hash a, Hash b )
{
return !a.Equals( b );
}
/// <summary>Converts a byte array to a <see cref="Hash"/></summary>
public static Hash FromBytes( byte[ ] hashBytes, int offset = 0 )
{
if ( hashBytes == null ) throw new ArgumentNullException( nameof( hashBytes ) );
if ( ( hashBytes.Length + offset ) < Length ) throw new ArgumentOutOfRangeException( nameof( hashBytes ) );
var hash = new Hash( );
fixed ( byte* sourceBytes = hashBytes )
NativeMethods.CopyMemory( hash.value, sourceBytes + offset, Length );
return hash;
}
#endregion
}
class NativeMethods
{
[DllImport( "Kernel32", SetLastError = true, EntryPoint = "CopyMemory" )]
internal unsafe static extern void CopyMemory( void* destination, void* source, uint length );
}
static class Extensions
{
/// <summary>Applies action to each element of the collection.</summary>
public static void Do<T>( this IEnumerable<T> enumerable, Action<T> action )
{
if ( enumerable == null ) throw new ArgumentNullException( "enumerable" );
if ( action == null ) throw new ArgumentNullException( "action" );
foreach ( var item in enumerable ) action( item );
}
}
}
在main方法的末尾附近設置一個斷點,並在創建hash5變量之后將光標懸停在任何hash1到hash5上。
注意 :您必須在項目屬性中設置允許不安全代碼 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.