piątek, 10 lipca 2009

Blog przeniesiony

Blog przeniesiony pod nowy adres:
http://itblog.socar.pl

wtorek, 2 czerwca 2009

[.NET] Chowanie okna konsoli

Aby schować okno konsoli, w ramach Console Window Application należy skorzystać z interops i biblioteki systemu windows:


using System.Runtime.InteropServices
//...
[DllIMport("user32.dll")]
private static extern IntPtr FindWindow(IntPtr hWnd, int nCmdShow);

[DllIMport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

//...
if(hWnd != IntPtr.Zero)
{
ShowWindow(hWnd, 0) // Ukryj okno

//ShowWindow(hWnd, 1) // Pokaż okno
}

niedziela, 29 marca 2009

[MySQL] Importowanie danych z CSV w UTF8

W poprzedniej notce opisywałem importowanie danych do MySQL z wykorzystaniem plików CSV i zapytania IMPORT DATA INFILE. Okazuje się że "zabawny problem z kodowaniem" który kiedyś mi się zdażył w ramach zapytania wykorzystującego CONCAT, jest dość "globalnym" problemem MySQL.

Jeżeli posiadamy dane w kodowaniu UTF8, tabele w UTF8 i kolumny w UTF8, to próba importowania danych w sposób opisany wcześniej ... nie powiedzie się. Okazuje się że MySQL (od 2005 roku! od 4 lat!) posiada bug, który importuje dane w kodowaniu bazy danych w zależności od systemu na jakim się znajduje.

Aby importować dane zawierające polskie znaki należy rozszerzyć zapytanie o "character set", ale nie o utf8 (co byłoby logiczne!) ale o... latin2. (W przypadku innych kodowań np. hiszpańskich, należy próbować z latin1);

Rozszerzamy nasze zapytanie o


character set latin2


przez co nasze zapytanie wyglada tak:

LOAD DATA INFILE 'C:/data.csv' INTO TABLE `tbl1`
CHARACTER SET latin2
FIELDS TERMINATED BY ';'
ENCLOSED BY '"'
ESCAPED BY '\\'
LINES TERMINATED BY '\n' (col1, col2);

[MySQL] Importowanie danych z CSV

Dawno temu napisałem skrypt który wczytywał plik CSV do bazy MySQL. Można to zrobić bezpośrednio z poziomu bazy danych.
Przypadek testowy:
1) Tabela o nazwie tbl1
2) Tabela posiada 2 kolumny o nazwie col1, col2 (int i varchar)
3) plik z danymi: data.csv
4) lokalizacja pliku z danymi: c:\
5) Część danych zawarta jest pomiędzy cudzysłowami (")
6) Znacznik kolejnej linii jest typu UNIXowego**

Przykład pliku z danymi wygląda następująco:


1;"some data 1"
2;"some data 2"
3;"some data x"


W imporcie powyższego pliku pomoże nam zapytanie:


LOAD DATA INFILE 'C:/data.csv' INTO TABLE `tbl1`
FIELDS TERMINATED BY ';'
ENCLOSED BY '"'
ESCAPED BY '\\'
LINES TERMINATED BY '\n' (col1, col2);


Wywołanie powyższego zapytania wypełni kolumny danymi. MySQL dba o to aby dane zostały odpowiednio zinterpretowane (text jako text, liczby jako liczby, itd);

** -Należy zauważyć fakt że każdy system operacyjny różnie interpretuje koniec linii. W rzeczywistości każdy plik ma tylko jedną linię. Jednak aby użytkownikowi zasymulować przejście do nowej linii wstawiane są "znaki nowej linii". System Windows, w przeciwieństwie np. do MacOS czy Unixów, stosuje podwójny znak końca linii jakim jest \r\n. Unix natomiast stosuje tylko \n a w przypadku MacOS jest to \r. Aby płynnie zmieniać rodzaj znaku kończącego linię można wykorzystać edytor tekstowy PsPad lub zmienić zapis w LINES TERMINATED na '\n\r'

środa, 25 marca 2009

[C#] Aplikacja transparentna (click-thru)

Ostatnio zacząłem się zastanawiać jak napisać aplikację przez którą można kliknać na inne aplikacje, ikony pulpitu itp itd. Są to aplikacje tzw. "click-thru". Mogą zostać wykorzystane jako dodatek do elementów pulpitu, które jednak nie będą przeszkadzać bardzo użytkownikowi. Aby zrealizować taką aplikację należy odwołać się do WindowsAPI:


using System.Runtime.InteropServices;
//(...)
[DllImport("user32.dll", SetLastError = true)]
private static extern UInt32 GetWindowLong(IntPtr hWnd, int nIndex);

[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

public const int GWL_EXSTYLE = -20;
public const int WS_EX_LAYERED = 0x80000;
public const int WS_EX_TRANSPARENT = 0x20;
public const int LWA_ALPHA = 0x2;
public const int LWA_COLORKEY = 0x1;

/*
* Aby dane okno było Click-Thru najlepiej kod wywołujący taką funkcjonalność
* umieścić w konstruktorze obiektu okna. Tuż po "InitializeComponents()";
*/

public Form1()
{
InitializeComponent();
SetWindowLong(this.Handle, GWL_EXSTYLE,
(IntPtr)(GetWindowLong(this.Handle, GWL_EXSTYLE) | WS_EX_LAYERED | WS_EX_TRANSPARENT));
this.TopMost = true;
}


Więcej informacji na forum MSDNu

wtorek, 24 marca 2009

MySQL CONCAT traci kodowanie przy zcalaniu z liczbą

Ostatnio dłubiąc przy projekcie który wykorzystuje bazę danych MySQL natrafiłem na "ciekawy" błąd. Błąd na tyle ciekawy że jest znany od 2005 roku. W 2005 roku MySQL w wersji 3.3 posiadał ten bug. Ówcześni twórcy stwierdzili "poprawimy to w wersji ..5.1". Nadeszła wersja 5.1, a poprawki jak nie ma tak nie ma. Ale o co chodzi?

Załóżmy że mamy tabelę z dwoma kolumnami. Pierwsza kolumna jest typu INTEGER o nazwie myNumber, a druga typu VARCHAR o nazwie myText. Kolumna VARCHAR jest ustawiona na porównywanie ciągów z kodowanie UTF-8 (UTF-8_general_ci). Jeżeli teraz wykonamy zapytanie:


SELECT CONCAT(myNumber, myText) as myResult FROM myTable;


otrzymamy w efekcie zcalony ciąg który ... utracił kodowanie UTF8. Pomimo tego że dane zostały wprowadzone i są w UTF-8, operacja CONCAT kolumny CHAR (lub VCHAR) i INTEGER (lub innej numerycznej) powoduje utracenie kodowania.

Rozwiązaniem tego problemu jest castowanie zmiennej liczbowej

SELECT CONCAT(CAST(myNumber as CHAR), myText) as myResult FROM myTable;


To umożliwi nam otrzymanie prawidłowego ciągu.

czwartek, 15 stycznia 2009

services.msc, compmgmt.msc ... - Odmowa dostępu (Access denied)


Ostatnio natrafiłem na problem. Mianowicie przy próbie uruchomienia jakiejkolwiek przystawki administracyjnej (o rozszerzeniu msc), np. gpedit.msc, compmgmt.msc, services.msc etc. otrzymywałem komunikat o błędzie: services.msc. Odmowa dostępu pomimo tego że posiadałem uprawnienia administratora. Rozwiązanie było proste.

Wystarczyło przypisać pliki *.msc do programu obsługującego je, czyli mmc.exe (C:\windows\system32\mmc.exe). Przypisanie następuje w prosty sposób. Klikamy prawym przyciskiem myszy na np. services.msc i wybieramy "Otwórzy przy pomocy...", po czym klikamy "Przeglądaj" i wybieramy mmc.exe

Jeżeli to rozwiązanie nie działa, należy jeszcze sprawdzić Uprawnienia dla dysku C.

poniedziałek, 10 listopada 2008

[C#] Wielowątkowość i GUI

Bardzo częstym problemem wielowątkowych aplikacji jest obługa GUI. Wszyscy zapewne świetne znają kwestię wykorzystania Invoke oraz InvokeRequired. Jednym z problemów na które ostatnio się natknąłem jest kwestia inicjalizacji rysowania formatki z innego wątku. W WinForms i .NET stosowane do rysowania jest GDI+. To jednak nie wyrzuca klasycznego wyjątku w przypadku odwołania się do funkcji formatki z innego wątku.

Rozważmy następujący kod w którym mamy 2 formatki (Form1 i Form2), przycisk (button1) oraz obiekt BackGroundWorker (backgroundworker1). Formatkę Form2 (TheForm) inicjalizujemy w wątku GUI a konkretnie w konstruktorze Form1. Następnie pod przycisk button1 przypisane jest wywołanie BackgroundWorkera (BackgroundWorker.StartWorkAsync()):

  
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
Form2 TheForm;
public Form1()
{
TheForm = new Form2();
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
TheForm.Show();

}
}
}



W powyższym kodzie formatka TheForm (Form2) zostanie wyświetlona, jednak będzie "wisieć", w czasie gdy reszta aplikacji (Form1) działa bez problemu. Również Visual Studio nie zgłasza żadnych problemów. Wg. moich ustaleń (a poniekąd przypuszczeń) dzieje się tak ze względu na Repaint() zaimplementowany w GDI+ który chcąc odmalować formatkę nie jest w stanie tego zrobić, gdyż obiekt z którego korzysta został zniszczony przy zakończeniu wątku, zanim zdążył się uruchomić. Pomimo tego nie rzuca żadnych ostrzeżeń.

Wartym uwagi jest fakt że jeżeli TheForm (Form2) wyświetlimy w aplikacji poza osobnym wątkiem, następnie go ukryjemy (nie niszcząc go), to ponowne jego wyświetlenie, już z innego wątku nie spowoduje problemu. Oczywiście należy zauważyć że omijamy tutaj wykorzystanie powszechnie znanego ShowDialog(), które powoduje zatrzymanie wykonywania pozostałego kodu aplikacji, aż do zamknięcia okna modalnego.

Jednocześnie rozwiązaniem jest Invoke. Co ciekawe nie na obiekt docelowy (Form2 - TheForm) tylko na obiekt źródłowy. Prawidłowy kod powinien wyglądać tak (istotne linie to 26 do 37):


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
Form2 TheForm;
public Form1()
{
TheForm = new Form2();
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}

private delegate void showWindowCallback();
private void showWindow()
{
if (this.InvokeRequired)
{
this.Invoke(new showWindowCallback(showWindow));
}
else
{
TheForm.Show();
}
}

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
showWindow();

}
}
}