Hej igen,
Nu tänker jag sticka ut hakan och framföra lite konstruktiv kritik angående det API som Tellduscore (och nuvarande dll?) exponerar.
Hur många fler funktioner som lämnar över ägandeskapet av minne finns det? (
dokumentationen säger inget om detta) Utan tydlig information om detta är det ju 100% säkert att alla program som använder TelldusCore kommer att lida av minnesläckor!
Om det absolut är nödvändigt att lämna över minnet så är det mycket säkrare att delkarera metoden så här:
Code: Select all
int tdGetName( int intDeviceId, char *buffer, int bufferSize )
Fördelen med detta är att man för användaren/programmeraren verkligen belyser att ett överlämnande av data sker. Det är dessutom upp till användaren att allokera så mycket minne som han anser nödvändigt. Returvärdet skulle vid lyckad operation vara 0 och vid för liten buffer det antal chars som behövs för att hämta hela namnet, inklusive terminerande NULL-char samt negativa felkoder för intrerna fel. Som användare kan man då enkelt avgöra om operationen lyckades och, om man så önskar, få ut mer information om problemet.
Vidare har jag i och med mina funderingar på ovanstående insett att den exempel implementationen som finns för .Net
här, samt min egen implemementation antagligen inte gör rätt när det gäller P/Invoke.
Tydligen förutsätter v2 av CLR P/Invoke att det minne som returneras som en char * ska avallokeras med Marshal.FreeCoTaskMem, vilket förutsätter att det allokerats med CoTaskMemAlloc - gissningsvis använder ni
new eller malloc() för det? Tydligen fungerar det utan minnesläckr i .Net v2, men det är ju mot alla principer att inte använda rätt avallokeringsmetod; dvs new/delete, malloc/free och CoTaskMemAlloc/FreeCoTaskMem
Enligt
denna tråd kommer dessutom beteendet för P/Invoke att förändras när vad det gäller avallokeringen av minnet. I v4 av .Net kommer den inte längre att anropa FreeCoTaskMem vilket slutar i ytterligare minnesläckor (egentligen är ju detta beteende bättre eftersom inte alla metoder som returnerar en char * faktiskt släpper minnet). Det är alltså bara tur att det fungerar alls.
Med ovanstående föreslagna signatur på tdGetName() blir .Net implementationen så här:
Code: Select all
[DllImport( "TelldusCore.dll" )]
public static extern int tdGetName( int intDeviceId, System.Text.StringBuilder sb, int bufferSize );
och används så här:
Code: Select all
StringBuilder sb = new StringBuilder( 50 );
int result = tdGetName( 1, sb, sb.Capacity );
För att kunna vara kompatibla med både nuvarande och framtida .Net versioner men
främst för att tydligt visa att ett överlämnande av data sker föreslår jag därför att ni uppdaterar era publika APIer enl. ovanstående.
Ett annat alternativ är att TelldusCore tillhandahåller ett allokerings/avallokerings gränssnitt som användare av biblioteket kan använda. Det förhindrar alla eventuella problem med allokering/avallokering då man gömmer den problematiken för användaren.
Exempel:
Code: Select all
[DllImport( "TelldusCore.dll" )]
public static extern IntPtr tdGetName( int intDeviceId );
[DllImport( "TelldusCore.dll" )]
public static extern void tdDeallocate( IntPtr data );
IntPtr data = tdGetName( 1 );
string s = Marshal.PtrToStringBSTR( data );
deallocate( data );
Båda de föreslagna lösningarna bör fungera även i ren 64-bitars miljö utan några modifikationer (givetvis måste TelldusCore kompileras som 64bit)
// Per