1
paź/09
0

[C#] Własny ciąg wynikowy z daty i czasu

Tabela ze skrótami umożliwiającymi formatowania daty i czasu z wykorzystaniem…


DateTime.Now.ToString("ddMMyyyyHHmmss") ;

// Wynik: 01102009094315

… znajduje się tutaj: http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx

Zakres tematyczny: Bez kategorii
25
sie/09
0

[C#] Niskopoziomowe przechwytywanie klawiszy klawiatury

Aby przechwycić naciśnięcie klawiszy na niskim poziomie (np. przed wszystkimi innymi aplikacjami) należy skorzystać z InteropSys aby „wpiąć” się do API systemu operacyjnego. Poniższy przykład z bloga Stephena Touba pokazuje jak to szybko i prosto zrobić:


using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;

class InterceptKeys
{
 private const int WH_KEYBOARD_LL = 13;
 private const int WM_KEYDOWN = 0x0100;
 private const int WM_KEYUP = 0x0101;
 private static LowLevelKeyboardProc _proc = HookCallback;
 private static IntPtr _hookID = IntPtr.Zero;

 public static void Main()
 {
 _hookID = SetHook(_proc);
 Application.Run();
 UnhookWindowsHookEx(_hookID);
 }

 private static IntPtr SetHook(LowLevelKeyboardProc proc)
 {
 using (Process curProcess = Process.GetCurrentProcess())
 using (ProcessModule curModule = curProcess.MainModule)
 {
 return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
 GetModuleHandle(curModule.ModuleName), 0);
 }
 }

 private delegate IntPtr LowLevelKeyboardProc(
 int nCode, IntPtr wParam, IntPtr lParam);

 private static IntPtr HookCallback(
 int nCode, IntPtr wParam, IntPtr lParam)
 {
 if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
 {
 int vkCode = Marshal.ReadInt32(lParam);
 Console.WriteLine((Keys)vkCode);
 }
 return CallNextHookEx(_hookID, nCode, wParam, lParam);
 }

 [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
 private static extern IntPtr SetWindowsHookEx(int idHook,
 LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

 [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
 [return: MarshalAs(UnmanagedType.Bool)]
 private static extern bool UnhookWindowsHookEx(IntPtr hhk);

 [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
 private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
 IntPtr wParam, IntPtr lParam);

 [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
 private static extern IntPtr GetModuleHandle(string lpModuleName);
}

„Problem” zaczyna się w przypadku gdy chcemy obsłużyć naciśnięcie kombinacji klawiszy, a w szczególnym przypadku ALT i jakiegoś innego klawisza. ALT nie jest „przechwytywany” przez hook, przez co funkcję private static IntPtr HookCallback należy nieco przerobić, korzystając ze wcześniej zdefiniowanej struktury KeyboardHookStruct w której to możemy sprawdzić również flagi naciśniętych przycisków (w tym przypadku ALT+ESC):


public struct KeyboardHookStruct
 {
 public int vkCode;
 public int scanCode;
 public int flags;
 public int time;
 public int dwExtraInfo;

 };

// ...

KeyboardHookStruct hookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));

 if (hookStruct.vkCode == ESCAPE && (hookStruct.flags & LLKHF_ALTDOWN) == hookStruct.flags)
     MessageBox.Show("I pressed ALT + ESC!");

Kilka uwag:

  1. Należy zauważć że w tym przypadku najpierw sprawdzamy drugi klawisz z kombinacji, a potem fakt czy jest wciśnięty ALT.
  2. Druga istotna rzecz to fakt zastosowania porównania wartości klawiszy do znanych z WinAPI nazw klawiszy wirtualnych.
  3. Listę kodów klawiszy wirtualnych można znaleźć na tej stronie Microsoft MSDN
  4. Łatwo napisać aplikację która przechwytuje klawisze, ale nie przekazuje ich dalej do kolejnych aplikacji zarejestrowanych w systemie w ramach łańcucha uchwytów. Wystarczy usunąć wywołania do funkcji:  CallNextHookEx
  5. Jeżeli wciśniemy klawisz i przytrzymamy, informacje o jego naciśnięciu (KEYDOWN) będą w kółko (w odpowiednim odstępie czasowym) przesyłane. Należy to odpowiednio obsłużyć.
  6. Przykład z klawiszem ALT będzie wykonywał się dwa razy, ze względu na fakt że w nacisnięcie klawisza to w rzeczywistości dwie akcje. Naciśnięcie (KEY_DOWN) oraz podniesienie (KEY_UP) klawisza. Można to naprawić poprzez rozpoznanie jaka aktualnie akcja (DOWN/UP) jest wykonywana (poprzez porównanie z wParam) lub poprzez dodanie prostego semaforu
13
lip/09
0

[.NET] Klikanie myszą w danej lokalizacji

Klikanie myszą w danej lokalizacji (W punkcie p) zrealizowane w C#. Niby proste, jednak nie oczywiste. Poniższy kod umożliwia wykonanie kliknięcia lewym przyciskiem myszy. Należy zauważyć że składa się on z dwóch eventów które można przetłumaczyć na „LeftMouseButtonDown” oraz „LeftMouseButtonUp”:

using System.Runtime.InteropServices;
//...
[DllImport("user32.dll")]
public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo);

public void DoMouseClick(Point p)
{
mouse_event(2, p.X, p.Y, 0, 0);
mouse_event(4, p.X, p.Y, 0, 0);
}

Więcej tutaj: http://msdn.microsoft.com/en-us/library/ms646260(VS.85).aspx

13
lip/09
0

[.NET] Zwalnianie nadmiarowej pamięci zaalokowanej przez CLR

Poniżej przedstawiony kawałek kodu osobiście uważam za „Świętego Grala” .NET, chociaż pochodzi prosto z WinApi. Umożliwia wymuszenie na aplikacji napisanej w .NET na zwolnienie za alokowanej pamięci w której aktualnie nie przechowuje obiektów.

.NET wykonując swoje programy zawsze alokuje więcej pamięci niż potrzebuje, gdyż polityka kompilatora zakłada że proces alokowania pamięci jest czasochłonny. Faktycznie tak jest, jednak w zależności od sytuacji ta czasochłonność może nie być zauważona. Wielu użytkowników panicznie interpretuje zapisy w Menedżerze zadań (taskmgr.exe) w którym to zwykła aplikacja posiadająca pojedynczą formatkę i przycisk zajmuje 11MB pamięci. Wskazanie to jest nieprawidłowe, jednak w tej chwili nie będę wdawał się w szczegóły, poza krótkim stwierdzeniem, że obiekty aplikacji zajmują mniej pamięci, a znaczna większość z tych 11MB to za alokowana przestrzeń pamięci dla aplikacji, która pozostaje pusta (a można ją zwrócić do systemu).

Do zalet stosowania poniższej funkcji dotychczas mogę zaliczyć dwie rzeczy:

  1. Możliwość pisania aplikacji rezydentnych w .NET (aplikacja co jakiś wywołuje zwolnienie pamięci, przez co jest w stanie zminimalizowanym może zajmować nawet 500KB)
  2. Możliwość uniknięcia wyjątku „OutOfMemoryException”, który jest generowany na podstawie wiadomości wysyłanej przez system operacyjny do wszystkich aplikacji, gdy ilość pamięci spadnie do 32MB (Sprawdzone w kodzie produkcyjnym).
using System.Runtime.InteropServices;
//(....)

[DllImport("kernel32.dll")]
public static extern bool SetProcessWorkingSetSize(IntPtr proc, int min, int max);

public void CutRamUsage()
{
GC.Collect();
GC.WaitForPendingFinalizers();

if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
}
}

Więcej informacji pod adresem:
http://msdn.microsoft.com/en-us/library/ms686234(VS.85).aspx

2
cze/09
0

[.NET] Ukrywanie 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
}
Zakres tematyczny: .NET, c#, console
29
mar/09
0

[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);
Zakres tematyczny: csv, encoding, mysql
29
mar/09
0

[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’

Zakres tematyczny: csv, mysql
25
mar/09
0

[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

Zakres tematyczny: .NET, c#
24
mar/09
0

MySQL CONCAT Utrata kodowania przy konkatenacji 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.

Zakres tematyczny: mysql, sql
15
sty/09
0

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.

Zakres tematyczny: Hardware, Windows