RSS
czwartek, 18 lipca 2013
Using .NET class with PowerBuilder 12.5 Classic (version 12.5.1 Build 4595)

 

Below are the steps one has to do in order to use a .NET class in a PowerBuilder classic application. I’ll be using the below .NET C# class to explain the approach:

 

using System;
using System.Runtime.InteropServices;

namespace SHARP
{
    [Guid("A08260C7-6F2D-400B-82BD-5B768CD2C1A6")]
    [ComVisible(true)]
    public class SharpClass
    {
        public int Sum(int a, int b) {
            return a + b;
        }
    }
}

 

1. IMPORTANT! The class/method should be COM visible.

This can be set up on the assembly level by editing AssemblyInfo.cs:

AssemblyInfo.cs

//Setting ComVisible to false makes the types in this assembly not visible
//to COM components.  If you need to access a type in this assembly from
//COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(true)]


// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c814a8fa-c35f-4306-b5e5-5b30f72ab21a")]


The advantage here is we do not have to generate GUID for our class manually.

Another way is to set this up in the class code, again COM visibility is set on the assembly level:

[assembly: System.Runtime.InteropServices.ComVisible(true)]
namespace SHARP
{
   ...
}



As before, we do not have to create our own GUID in this case. The one from AssemblyInfo.cs will be used.

NOTE! We can only set the assembly level ComVisible property once either in the AssemblyInfo.cs file or a class annotation. Trying to use it twice will cause “Duplicate 'ComVisible' attribute” error.



Finally we can expose a single class/method from our assembly as shown in the very first example. In this case we should create our own GUID using Visual Studio Tools/Create GUID function:


Visual Studio - Create GUID

Visual Studio - Create GUID

 

2. Project properties need to be updated. In project’s Properties/Build/Output we have to check Register for COM interop:



Visual Studio - Register for COM interop

 

Now, if you try to build you class without exposing anything as COM visible the following warnings appear:

warning MSB3391: " SHARP.dll" does not contain any types that can be unregistered for COM Interop.



3. Now we have to register our assembly:

c:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /codebase c:\Temp\SHARP\SHARP\bin\Debug\SHARP.dll /regfile:SHARP.reg

We’re using the full path to RegAsm program to avoid “Failed to load .dll because it is not a valid .NET assembly” error:

c:\Temp\SHARP\SHARP\bin\Debug>REGASM SHARP.dll /regfile:SHARP.reg
Microsoft (R) .NET Framework Assembly Registration Utility 2.0.50727.5420
Copyright (C) Microsoft Corporation 1998-2004.  All rights reserved.

RegAsm : error RA0000 : Failed to load 'c:\Temp\SHARP\SHARP\bin\Debug\SHARP.dll' because it is not a valid .NET assembly

c:\Temp\SHARP\SHARP\bin\Debug>

 
4. After we run the created .reg file we can call our .NET function from PowerBuilder:

 

integer            li_rc
oleobject          sharp

sharp = CREATE oleobject
li_rc = sharp.ConnectToNewObject ( "SHARP.SharpClass" )
li_rc = sharp.Sum(5, 1)
sharp.DisconnectObject()
DESTROY sharp

 

Downside: If PowerBuilder is open after an assembly was used the .dll stays locked even after you disconnect from the object.

wtorek, 16 lipca 2013
Programowanie iPhone - Sortowanie tablicy obiektów

 

Do sortowania tablicy obiektów możemy podejść na kilka sposobów.
Wszystkie przykłady wykorzystają poniższą klasę Trip:


@interface Trip : NSObject {
	/* trip details */
	NSInteger ID;
	NSDate *date;
	NSNumber *odometer;
NSNumber *distance;
/* ... */
}

@property (nonatomic, assign) NSInteger ID;
@property (nonatomic, retain) NSDate *date;
@property (nonatomic, retain) NSNumber *odometer;
@property (nonatomic, retain) NSNumber *distance;

/* ... */

@end

 

Najprostszym sposobem jest implementacja i wykorzystanie metody compare:

 

kod
-(NSComparisonResult) compare:(Trip *) tripObject {
	return [self.date compare:tripObject.date];
}

-(NSArray *) sortA:(NSArray *) trips {
   
   NSArray *sortedArray;
   sortedArray = [trips sortedArrayUsingSelector:@selector(compare:)];
   
   return sortedArray;
}

 

Bardzo eleganckim, lecz nieco mniej czytelnym, szczególnie dla początkujących, rozwiązaniem, jest wykorzystanie bloków:

 

kod
-(NSArray *) sortB1:(NSArray *) trips {
   
   NSArray *sortedArray;
   sortedArray = [trips sortedArrayUsingComparator:^(id a, id b) {
      NSDate *first = [(Trip *)a date];
      NSDate *second = [(Trip *)b date];
      return [first compare:second];
   }];
   
   return sortedArray;
   
}

 

Kod powyższy możemy jeszcze bardziej skondensować:

 

kod
-(NSArray *) sortB2:(NSArray *) trips {
   
   return [trips sortedArrayUsingComparator:^(id a, id b) {
      NSDate *first = [(Trip *)a date];
      NSDate *second = [(Trip *)b date];
      return [first compare:second];
   }];

}

 

Podobnym do „blokowego” rozwiązaniem jest kod oparty o NSComparisonResult, to podejście może wydać się bardziej intuicyjne i czytelniejsze dla osób mających wcześniejsze doświadczenia z językami C / Java. Poniżej dwa przykłady ilustrujące tą technikę, jeden wykorzystujący metodę sortUsingComparator: operującej bezpośrednio na tablicy wejściowej i drugi metodę sortedArrayUsingComparator: sortującą tablicę wejściową i zwracającą posortowana jej kopie:

 

-(NSArray *) sortC1:(NSMutableArray *) trips {
   
   [trips sortUsingComparator:^NSComparisonResult(id a, id b){
      
      NSDate *first = [(Trip *)a date];
      NSDate *second = [(Trip *)b date];
      
      if (first > second) {
        return (NSComparisonResult)NSOrderedDescending;
      }
      if (first < second) {
         return (NSComparisonResult)NSOrderedAscending;
      }
         return (NSComparisonResult)NSOrderedSame;
      }];

   return trips;
   
}

 

-(NSArray *) sortC2:(NSArray *) trips {
   
   NSArray *sortedArray;
   sortedArray = [trips sortedArrayUsingComparator:^NSComparisonResult(id a, id b){
      
      NSDate *first = [(Trip *)a date];
      NSDate *second = [(Trip *)b date];
      
      if (first > second) {
         return (NSComparisonResult)NSOrderedDescending;
      }
      if (first < second) {
         return (NSComparisonResult)NSOrderedAscending;
      }
      return (NSComparisonResult)NSOrderedSame;
   }];
   
   return sortedArray;
   
}

 

Jednak najprostszym, najbardziej przejrzystym i wykorzystywanym najczęściej przeze mnie sposobem sortowania jest wykorzystanie klasy NSSortDescriptor:

 

kod
-(NSArray *) sortD1:(NSArray *) trips {
   
   NSSortDescriptor *sortDescriptor;
   sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES];
   NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptor, nil];
   
   NSArray *sortedArray;
   sortedArray = [trips sortedArrayUsingDescriptors:sortDescriptors];
   
   [sortDescriptor release];		//not needed with ARC

   return sortedArray;
   
}

 

I na koniec nieco bardziej skomplikowany przykład sortujący w kolejności malejącej najpierw po dacie, a później po identyfikatorze ID:

 

kod
-(NSArray *) sortD2:(NSArray *) trips {
   
   NSSortDescriptor *sortDescriptorDateDesc, *sortDescriptorIDDesc;
   
   sortDescriptorDateDesc = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO];
   sortDescriptorIDDesc = [[NSSortDescriptor alloc] initWithKey:@"ID" ascending:NO];
   
   NSArray *sortDescriptors = [NSArray arrayWithObjects:sortDescriptorDateDesc, sortDescriptorIDDesc, nil];
   
   NSArray *sortedArray;
   sortedArray = [trips sortedArrayUsingDescriptors:sortDescriptors];
   
	[sortDescriptorDateDesc release];		//not needed with ARC
	[sortDescriptorIDDesc release];			//not needed with ARC
   
	return sortedArray;
   
}
czwartek, 11 lipca 2013
Programowanie iPhone – UITableiew blokowanie wierszy tabeli

 

Jednym ze sposobów uniemożliwienia użytkownikom naszej aplikacji wyboru jednego lub więcej wierszy tabeli (UITableView) jest implementacja w delegacie UITableViewDelegate metody tableview:willSelectRowAtIndexPath:. Wiersze, dla których metoda zwróci wartości nil będą „niewybieralne”.

 

-(NSIndexPath*)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {

   BOOL isRowSelectable = TRUE;  //domyslnie mozna wybrac wiersz
   //sprawdz czy wiersz moze byc wybrany czy nie
   
   //...
   
   if (isRowSelectable) {
      return indexPath;
   }
   
   return nil;
   
}

 

Warto też w metodzie (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath ustawić, dla wierszy których nie można wybrać parametr selectionStyle na UITableViewCellSelectionStyleNone:

 

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
   
   //...

   cell.selectionStyle = UITableViewCellSelectionStyleNone;
   return cell;
}

 

Opcjonalnie można również ustawić właściwość wiersza userInteractionEnabled na NO;

 

cell.userInteractionEnabled = NO;

 

Dzięki temu unikniemy chwilowego zmiany koloru komórki na niebieski w momencie gdy użytkownik na niej „tapnie”. Można tez pokusić się o stworzenie kategorii rozszerzającej możliwości klasy UITableViewCell.

wtorek, 09 lipca 2013
MacBook Pro Boot Camp i Print Screen

 

Przygotowując jeden z ostatnich wpisów musiałem wykonać print screena okna. Oczywiście na klawiaturze Maca klawisz Print Scrn nie istnieje.

Po krótkim poszukiwaniu znalazłem stronę Appla wyjaśniającą jak to zrobić:

 

Print Screen - cały ekran: Shift + Fn + F11
Print Screen - aktywne okno: Alt + Shift + Fn + F11

 

Nie jest to generalnie problem Boot Camp, ale ogólnie ograniczenie klawiatury MacBooka. Po podłączeniu do laptopa standardowej klawiatury Windows, wszystkie klawisze działają tak jak powinny.

Więcej przydatnych skrótów można znaleźć na stronach supportu Apple: http://support.apple.com/kb/HT1220.

środa, 03 lipca 2013
Wykorzystanie klas .NET w PowerBuilder 12.5 Classic

 

Wykorzystanie klas.NET w aplikacji PowerBuilder powinno być proste i takie też jest, jeśli pracujemy z wersja .NET PowerBuilder’a, w przypadku wersji Classic, musimy nasza klasę odpowiednio do tego przygotować i zarejestrować jako komponent COM.

Załóżmy, że chcemy wykorzystać w naszej aplikacji PB metodę Sum() poniższej klasy:


using System;
using System.Runtime.InteropServices;

namespace SHARP
{
    [Guid("A08260C7-6F2D-400B-82BD-5B768CD2C1A6")]
    [ComVisible(true)]
    public class SharpClass
    {
        public int Sum(int a, int b) {
            return a + b;
        }
    }
}

 

1. Przede wszystkim musimy wskazać, jakie elementy naszego assembly będą dostępne przez interfejs COM, w powyższym przykładzie jest to pojedyncza klasa SharpClass.

„Widoczność” COM może być również zdefiniowana na dwa sposoby, na poziomie całego assembly.

Pierwszy sposób to edycja pliku AssemblyInfo.cs:

AssemblyInfo.cs

//Setting ComVisible to false makes the types in this assembly not visible
//to COM components.  If you need to access a type in this assembly from
//COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(true)]


// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c814a8fa-c35f-4306-b5e5-5b30f72ab21a")]


Zaletą tego podejścia jest to, iż nie musimy generować, w tym wypadku nowego identyfikatora GUID, będzie wykorzystany ten dostępny w pliku AssemblyInfo.cs.


„Widoczność” COM na poziomie assembly możemy również ustawić z wykorzystaniem adnotacji tak, jak pokazuje to poniższy przykład:


 

[assembly: System.Runtime.InteropServices.ComVisible(true)]
namespace SHARP
{
   ...
}

 

Tak jak poprzednio i w tym wypadku nie musimy generować nowego identyfikatora GUID.

UWAGA! „Widoczność” COM [ComVisible(true)] na poziomie assembly może być ustawiona tylko raz, bądź w pliku AssemblyInfo.cs, bądź z wykorzystaniem adnotacji. Podwoja deklaracja spowoduje błąd: “Duplicate 'ComVisible' attribute”.

 

Ostatnim sposobem jest udostepnienie przez interfejs COM pojedynczej klasy, tak jak pokazane to na pierwszym przykładzie (adnotacja przed definicja klasy). W tym jednak wypadku powinniśmy wygenerować nowy identyfikator GUID. W tym celu możemy wykorzystać narzędzie dostępne w Visual Studio - Create GUID (menu Tools)


Visual Studio - generowanie GUID

Visual Studio - generowanie GUID

 

2. Następnie musimy zaznaczyć opcje Register for COM interop, w ustawieniach Build/Output naszego projektu w Visual Studio.

Visual Studio - Register for COM interop

 

Po wybraniu tej opcji próba kompilacja bez wcześniejszego określenia, które z elementów assembly powinny być dostępne via COM spowoduje pojawienie się poniższego ostrzeżenia:

warning MSB3391: " SHARP.dll" does not contain any types that can be unregistered for COM Interop.


3. Teraz musimy zarejestrować nasze assembly:

c:\Windows\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe /codebase c:\Temp\SHARP\SHARP\bin\Debug\SHARP.dll /regfile:SHARP.reg

W przypadku, gdy na komputerze, na którym pracujemy zainstalowane jest kilka wersji .NET najlepiej jest podać pełną ścieżkę dostępu do programu RegAsm.exe. Jeśli tego nie zrobimy, próba rejestracji może zakończyć się niepowodzeniem z błędem: “Failed to load .dll because it is not a valid .NET assembly” error:

c:\Temp\SHARP\SHARP\bin\Debug>REGASM SHARP.dll /regfile:SHARP.reg
Microsoft (R) .NET Framework Assembly Registration Utility 2.0.50727.5420
Copyright (C) Microsoft Corporation 1998-2004.  All rights reserved.

RegAsm : error RA0000 : Failed to load 'c:\Temp\SHARP\SHARP\bin\Debug\SHARP.dll' because it is not a valid .NET assembly

c:\Temp\SHARP\SHARP\bin\Debug>

4. Po zmodyfikowaniu rejestru (uruchomieniu utworzonego pliku .reg) możemy zacząć wykorzystywać nasza klasę w aplikacji PowerBuilder:

 

integer            li_rc
oleobject          sharp

sharp = CREATE oleobject
li_rc = sharp.ConnectToNewObject ( "SHARP.SharpClass" )
li_rc = sharp.Sum(5, 1)
sharp.DisconnectObject()
DESTROY sharp

 

Dużym minusem, przynajmniej dla mnie, jest fakt, że w momencie pierwszego wykorzystania klasy przez aplikacje PowerBuilder, pliki *.dll i *.tlb (SHARP.dll i SHARP.tlb) zostaną zablokowane i pozostaną zablokowane do momentu zamknięcia aplikacji, nawet pomimo wykonania polecenia DisconnectObject.

 

Wersja PB 12.5.1 Build 4595.

Tagi




PowerBuilder Tetris
D - Tetris



Programowanie iOS

C# ToolBox

SQL / TSQL / PLSQL ToolBox

Linux / Unix ToolBox





Zaprzyjaznione Strony

Sprite Bandits

Cake Time