Marko Apfel - Afghanistan/Belgium/Germany

Management, Architecture, Programming, QA, Coach, GIS, EAI

  Home  |   Contact  |   Syndication    |   Login
  168 Posts | 2 Stories | 133 Comments | 4 Trackbacks

News



Twitter | LinkedIn | Xing

Article Categories

Archives

Post Categories

BizTalk

C#

Enterprise Library

SAP

SQL Server

Technologie

Problemkontext

So schön auch das automatische “cleanup code refactoring” von ReSharper ist, im Zusammenhang mit Strukturdefinitionen, die als Übergabe-Parameter für unmanaged Code Aufrufe benötigt werden, lauert eine große Gefahr.

Standardmäßig ist im Profil “Full Cleanup” die Option “Reorder type members” auf “Yes” gestellt.

Und dann wird aus der originalen Definition:

public struct TreeViewItem
{
    public int mask;
    public IntPtr hItem;
    public int state;
    public int stateMask;
}

ein mit alphebetisch geordneten Type Membern:

public struct TreeViewItem
{
    public IntPtr hItem;
    public int mask;
    public int state;
    public int stateMask;
}

Im eigenen Scope ist das natürlich erstmal nicht problematisch – allerdings wird dies ein möglicherweise schwer zu findende Problemursache wenn eine Instanz dieser Struktur als Parameter in einem unmanaged code Aufruf (z.B. WinAPI-Aufruf) verwendet wird.

Konkret hatten wir dieses Problem mit folgendem Codefragment:

tvi = new TreeViewItem();
..
IntPtr lparam = Marshal.AllocHGlobal(Marshal.SizeOf(tvi));
Marshal.StructureToPtr(tvi, lparam, false);
SendMessage(Handle, (uint)TVM_SETITEM, IntPtr.Zero, lparam);

Dann wird es seitens Windows nämlich interessant an welcher Position der Struktur welche Informationen stehen. Und der Tausch der ersten beiden Felder wird meistens zu Problemen führen.

Lösungsansätze

Um die Position der Informationen fest vorzugeben – unabhängig von der Reihenfolge im Quellcode – gibt es das [StructLayout]-Attribut.

Mit der Konstruktor-Variante [StructLayout(LayoutKind.Explicit)] lässt sich jeder Type Member über das [FieldOffset]–Attribut exakt ausrichten.

So kann sichergestellt werden, dass beim (automatischen) Refactoring durch das Umordnen von Type Membern nicht versehentlich die Semantik einer Struktur geändert wird.

Diese Gefahr wurde seitens ReSharper schon länger gebannt, indem die Reihenfolge von Type Membern in Strukturen, welche mit dem [StructLayout]-Attribut ausgezeichnet sind, nicht angefasst wird.

Damit kann bei ausschließlichem Einsatz von “cleveren” Refactoring-Werkzeuge wie ReSharper die Auszeichnungs-Variante [StructLayout(LayoutKind.Sequential)] genutzt werden, welche quasi die Standardeinstellung ist und die Position der  Informationen so annimmt, wie im Quellcode die Type Member in der Struktur angegeben sind.

Nichtsdestotrotz bleibt dann ein Risiko wenn zukünftig mal neue Refactoring-Werkzeuge zum Einsatz kommen, die dieses Attribut nicht berücksichtigen.

Einen derartigen Fehler dann zu finden kann eine unglaubliche Sisyphosarbeit sein, wie wir selber schon schmerzlich erfahren mussten. Deshalb sollte der clean code developer lieber gleich Nägel mit Köpfen machen und die Ausrichtung selber spezifizieren.

Links

ReSharper - Disable Naming Style checks for types with StructLayout
http://youtrack.jetbrains.net/issue/RSRP-110430

Mastering structs in C#
http://stackoverflow.com/questions/1182782/c-structlayout-explicit-question

MSDN - LayoutKind Enumeration
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.layoutkind.aspx

posted on Wednesday, April 28, 2010 9:40 AM