Firebird 2.5 Release Candidate 3が部分的にリリースされました。Windows x64版は現在QA中で、まもなくリリースされる予定です。
Firebird File Repositories (download)
Firebird 2.5 Release Notes
2010/08/02追記: Windows x64版もQAが完了してリリースされています。現時点でMacOSX 32bit版のみ未リリースとなっています。
2010/08/03追記: MaxOSX 32bit版もリリースされています。
2010年7月30日
2010年7月29日
ヘルプインサイトの利用方法
Delphi 2007以降の新機能であるヘルプインサイトの利用方法に関する記事。興味深い。とりあえずメモ。
Help insight - Delphi Programming
In Praise of Help Insight Support
Delphi Help Insight Customization
Delphi Help Insight XSL
Help insight - Delphi Programming
In Praise of Help Insight Support
Delphi Help Insight Customization
Delphi Help Insight XSL
2010年7月28日
ウェブセミナー資料ダウンロード
過去のウェブセミナーのプレゼンテーション資料がダウンロードできるようになっています(要登録)。
エンバカデロ Webセミナー プレゼンテーション資料ダウンロード
2010/07/28時点で以下のプレゼンテーション資料がダウンロードできます。
Webセミナー「データベースパフォーマンスチューニングの概要と評価」(2010/06/02)
Webセミナー「システム品質を高めるデータベースツールからのアプローチ」(2010/05/19,2010/06/09,2010/07/14)
Webセミナー「異種データベース利用者必見!管理負荷を軽減するベストプラクティス」(2010/06/16,2010/07/28)
Webセミナー「新規プロジェクト開発に必須、データベースの情報共有インフラの活用」(2010/06/23,2010/07/21)
Webセミナー「チュートリアル:すぐに始められるデータベース設計とSQL文の作成」(2010/06/30)
Webセミナー「データベース設計ツールER/Studioの概要と効果的活用法」(2010/07/07)
開発系(RAD Studio)のものはどうなんでしょう…。
エンバカデロ Webセミナー プレゼンテーション資料ダウンロード
2010/07/28時点で以下のプレゼンテーション資料がダウンロードできます。
Webセミナー「データベースパフォーマンスチューニングの概要と評価」(2010/06/02)
Webセミナー「システム品質を高めるデータベースツールからのアプローチ」(2010/05/19,2010/06/09,2010/07/14)
Webセミナー「異種データベース利用者必見!管理負荷を軽減するベストプラクティス」(2010/06/16,2010/07/28)
Webセミナー「新規プロジェクト開発に必須、データベースの情報共有インフラの活用」(2010/06/23,2010/07/21)
Webセミナー「チュートリアル:すぐに始められるデータベース設計とSQL文の作成」(2010/06/30)
Webセミナー「データベース設計ツールER/Studioの概要と効果的活用法」(2010/07/07)
開発系(RAD Studio)のものはどうなんでしょう…。
2010年7月27日
keyed events
以前クリティカルセクションの仕様変更の影響について触れましたが、この件に関連して、CriticalSectionに代わる非公開機能の"keyed events"に関する興味深い記事を見つけました。
Windows keyed events, critical sections, and new Vista synchronization features(Joe Duffy's Weblog)
とってもいいかげんな要約: クリティカルセクションはWindows 2000以前の実装ではSMP上でのパフォーマンスのボトルネックになってしまっていた。これを改善するためにWindows 2000で仕様を変更したが、今度は信頼性を損なうことになってしまった。これを解決するためにWindows XPで新たに非公開の"keyed events"が作られ、Windows Vistaではその実装が改善された。ハンドルとノンページメモリを圧迫しない新しい同期機能としてユーザモードのコードからも利用可能になることを期待している。
もう少し詳しい話がWindows Internals, Fifth Edition (amazon)にあるそうですが、日本語訳はいつ出るんでしょうか…。
元ねたは江添さんの本の虫: keyed-eventsとNyaRuRuさんのTwitter上の2010/07/24-25あたりの発言。
Windows keyed events, critical sections, and new Vista synchronization features(Joe Duffy's Weblog)
とってもいいかげんな要約: クリティカルセクションはWindows 2000以前の実装ではSMP上でのパフォーマンスのボトルネックになってしまっていた。これを改善するためにWindows 2000で仕様を変更したが、今度は信頼性を損なうことになってしまった。これを解決するためにWindows XPで新たに非公開の"keyed events"が作られ、Windows Vistaではその実装が改善された。ハンドルとノンページメモリを圧迫しない新しい同期機能としてユーザモードのコードからも利用可能になることを期待している。
もう少し詳しい話がWindows Internals, Fifth Edition (amazon)にあるそうですが、日本語訳はいつ出るんでしょうか…。
元ねたは江添さんの本の虫: keyed-eventsとNyaRuRuさんのTwitter上の2010/07/24-25あたりの発言。
2010年7月26日
InterBase SMP 2009 Hotfix Update 4リリース
InterBase SMP 2009 Hotfix Update 4がリリースされています。Hotfix Update 4を適用するには予めHotfix Update 3が適用されている必要があります。またHotfix Update 4は英語版のものを日本語版にも適用可能です。
27729 InterBase SMP 2009 Hotfix Update 4 (9.0.4.443) for Windows
InterBase 2009 Hotfix Update 4 (Windows版) リリースノート (version 9.0.4.443)
27729 InterBase SMP 2009 Hotfix Update 4 (9.0.4.443) for Windows
InterBase 2009 Hotfix Update 4 (Windows版) リリースノート (version 9.0.4.443)
Delphiのエディタのキーバインドを作成する
OTA(Open tools API)を使用してDelphiのエディタで新しくキーバインドを作成する方法を説明した記事。興味深い。とりあえずメモ。
Cary Jensen "Let's Get Technical": Creating Editor Key Bindings in Delphi
Cary Jensen "Let's Get Technical": Creating Editor Key Bindings in Delphi
2010年7月23日
Windowsのシステムキーコンビネーションを無効にする
全画面で動作するようなアプリケーションなどでは、[Win]、[Ctrl]+[ESC]、[ALT]+[TAB]、[ALT]+[ESC]、[Ctrl]+[Shift]+[ESC]、[ALT]+[F4]のようなWindowsのシステムキーコンビネーションを無効にしたいような状況があります。そのような場合はキーボードを低レベルフックします。フックの設定はSetWindowsHookEx(ja)で、解除はUnhookWindowsHookEx(ja)で行い、フック関数はLowLevelKeyboardProc(ja)に従って作成します。フック関数が呼び出されるときにlParamにはKBDLLHOOKSTRUCT構造体へのポインタが格納されており、この中のvkCodeで押されたキーの仮想キーコードを、flagsのLLKHF_ALTDOWNビット(bit5)で[ALT]キーが押されているかどうかを、それぞれ知ることができます。ここで無効化したいキーだった場合は戻値を1としてそのままリターンします。一方でそのままにしたいキーだった場合はCallNextHookEx(ja)で次のフックチェーンに処理を委ねる必要があります。また[ALT]以外のキー([Ctrl]や[Shift]など)とのコンビネーションを知りたい場合はGetAsyncKeyState(ja)で取得します(bit15が現在そのキーが押されているかどうかを示しています)。
まずキーボードの低レベルフックに必要な定義を行います。
次にフック関数を用意します。
ここでは集合型のInvalidCombinationsに含まれるキーコンビネーションであればResultを1にしてそのままリターン、そうでなければCallNextHookExでフックチェーンの呼び出しを行うようにしています。
最後にキーボードの低レベルフックを設定、解除する関数は
と単にSetWindowsHookExおよびUnhookWindowsHookExを呼び出すだけです。
なお[Ctrl]+[ALT]+[Del]のキーストロークだけはキーボードの低レベルフックでも捕捉できません(GINAの置き換えが必要になります)。
まずキーボードの低レベルフックに必要な定義を行います。
uses Windows; type LPKBDLLHOOKSTRUCT = ^KBDLLHOOKSTRUCT; {$EXTERNALSYM LPKBDLLHOOKSTRUCT} tagKBDLLHOOKSTRUCT = record vkCode: DWORD; scanCode: DWORD; flags: DWORD; time: DWORD; dwExtraInfo: Pointer; end; {$EXTERNALSYM tagKBDLLHOOKSTRUCT} KBDLLHOOKSTRUCT = tagKBDLLHOOKSTRUCT; {$EXTERNALSYM KBDLLHOOKSTRUCT} TKbDllHookStruct = KBDLLHOOKSTRUCT; PKbDllHookStruct = LPKBDLLHOOKSTRUCT; const WH_KEYBOARD_LL = 13; {$EXTERNALSYM WH_KEYBOARD_LL} LLKHF_EXTENDED = KF_EXTENDED shr 8; {$EXTERNALSYM LLKHF_EXTENDED} LLKHF_INJECTED = $00000010; {$EXTERNALSYM LLKHF_INJECTED} LLKHF_ALTDOWN = KF_ALTDOWN shr 8; {$EXTERNALSYM LLKHF_ALTDOWN} LLKHF_UP = KF_UP shr 8; {$EXTERNALSYM LLKHF_UP}
次にフック関数を用意します。
type TSystemKeyCombination = (skLWin, // [WIN] (Left) - Open Start menu skRWin, // [WIN] (Right) - Open Start menu skCtrlEsc, // [CTRL] + [ESC] - Open Start menu skAltTab, // [ALT] + [TAB] - Switch programs skAltEsc, // [ALT] + [ESC] - Next program skCtrlShiftEsc, // [CTRL] + [SHIFT] + [ESC] - Open task manager skAltF4); // [ALT] + [F4] - Quit program TSystemKeyCombinations = set of TSystemKeyCombination; var hh: HHOOK; InvalidCombinations: TSystemKeyCombinations; function LowLevelKeyboardProc(nCode: Integer; wP: WPARAM; lP: LParam): LRESULT; stdcall; export; var pkbhs: PKbDllHookStruct; CtrlKey: Boolean; ShiftKey: Boolean; begin pkbhs := PKbDllHookStruct(lP); case nCode of HC_ACTION: begin case pkbhs^.vkCode of VK_LWIN: begin if skLWin in InvalidCombinations then begin { LWIN } Result := 1; Exit; end; end; VK_RWIN: begin if skRWin in InvalidCombinations then begin { RWIN } Result := 1; Exit; end; end; VK_TAB: begin if ((pkbhs^.flags and LLKHF_ALTDOWN) <> 0) and (skAltTab in InvalidCombinations) then begin { ALT + TAB } Result := 1; Exit; end; end; VK_ESCAPE: begin CtrlKey := ((GetAsyncKeyState(VK_CONTROL) and $8000) <> 0); ShiftKey := ((GetAsyncKeyState(VK_SHIFT) and $8000) <> 0); if ((pkbhs^.flags and LLKHF_ALTDOWN) <> 0) and (skAltEsc in InvalidCombinations) then begin { ALT + ESC } Result := 1; Exit; end; if ShiftKey = False then begin if (CtrlKey = True) and (skCtrlEsc in InvalidCombinations) then begin { CTRL + ESC } Result := 1; Exit; end; end else begin if (CtrlKey = True) and (skCtrlShiftEsc in InvalidCombinations) then begin { CTRL + SHIFT + ESC } Result := 1; Exit; end; end; end; VK_F4: begin if ((pkbhs^.flags and LLKHF_ALTDOWN) <> 0) and (skAltF4 in InvalidCombinations) then begin { ALT + F4 } Result := 1; Exit; end; end; end; end; end; Result := CallNextHookEx(hh,nCode,wP,lP); end;
ここでは集合型のInvalidCombinationsに含まれるキーコンビネーションであればResultを1にしてそのままリターン、そうでなければCallNextHookExでフックチェーンの呼び出しを行うようにしています。
最後にキーボードの低レベルフックを設定、解除する関数は
procedure HookKeyboardLL; begin if hh <> 0 then begin Exit; end; hh := SetWindowsHookEx(WH_KEYBOARD_LL,LowLevelKeyboardProc,HInstance,0); end; procedure UnhookKeyboardLL; begin if hh = 0 then begin Exit; end; UnhookWindowsHookEx(hh); hh := 0; end;
と単にSetWindowsHookExおよびUnhookWindowsHookExを呼び出すだけです。
なお[Ctrl]+[ALT]+[Del]のキーストロークだけはキーボードの低レベルフックでも捕捉できません(GINAの置き換えが必要になります)。
2010年7月22日
サービスに関する情報を列挙する
インストールされているサービスを取得するにはまずOpenSCManager(ja)でサービスコントロールマネージャをオープンし、EnumServicesStatus(ja)でサービスの情報を取得し、最後にCloseServiceHandle(ja)でサービスコントロールマネージャをクローズします。EnumServicesStatusはサイズを0として呼び出すことで必要な領域の大きさを取得し、領域を確保後に改めてEnumServicesStatusを呼び出します。取得した情報はENUM_SERVICE_STATUS構造体の配列と、そのメンバであるSERVICE_STATUS構造体に格納されます。
ところがこれらの情報の中にはサービスを起動したときのコマンドラインなどが含まれていません。そこでEnumServicesStatusで取得したそれぞれのサービス名についてOpenService(ja)でサービスをオープンし、QueryServiceConfig(ja)でサービスの構成パラメータを取得し、最後にCloseServiceHandleでサービスをクローズします。QueryServiceConfigもまずサイズを0として呼び出して必要な領域のサイズを取得し、領域を確保後に改めてQueryServiceConfigを呼び出します。取得した情報はQUERY_SERVICE_CONFIG構造体に格納されます。
必要な定義は概ねWinSvcユニットに存在しますが、EnumServicesStatusで取得するTEnumServiceStatus(=ENUM_SERVICE_STATUS構造体)の配列へのポインタの型はそのままでは扱いにくいので、
と再定義しておきます。そして
とします。例によってDelphi 2009以降の無名メソッド(anonymous method)を使用する場合は
こんな感じになります。なお取得したコマンドラインの中にはパス名に空白文字を含むにもかかわらずダブルクォーテーションで括られていないものもあるため、コマンドラインから実行ファイルのフルパス名を取り出すには
このように一捻り必要です。
元ねたは旧Delphi-MLの90131以下のスレッド(特に90133のKHE00221さんの回答)。
ところがこれらの情報の中にはサービスを起動したときのコマンドラインなどが含まれていません。そこでEnumServicesStatusで取得したそれぞれのサービス名についてOpenService(ja)でサービスをオープンし、QueryServiceConfig(ja)でサービスの構成パラメータを取得し、最後にCloseServiceHandleでサービスをクローズします。QueryServiceConfigもまずサイズを0として呼び出して必要な領域のサイズを取得し、領域を確保後に改めてQueryServiceConfigを呼び出します。取得した情報はQUERY_SERVICE_CONFIG構造体に格納されます。
必要な定義は概ねWinSvcユニットに存在しますが、EnumServicesStatusで取得するTEnumServiceStatus(=ENUM_SERVICE_STATUS構造体)の配列へのポインタの型はそのままでは扱いにくいので、
uses Windows, SysUtils, WinSvc; type TEnumServiceStatusArray = array [0..0] of TEnumServiceStatus; PEnumServiceStatusArray = ^TEnumServiceStatusArray; { Alias } function EnumServicesStatus(hSCManager: SC_HANDLE; dwServiceType: DWORD; dwServiceState: DWORD; lpServices: PEnumServiceStatusArray; cbBufSize: DWORD; var pcbBytesNeeded: DWORD; var lpServicesReturned: DWORD; var lpResumeHandle: DWORD): BOOL; stdcall; external advapi32 name {$IFDEF Unicode} 'EnumServicesStatusW'; {$ELSE} 'EnumServicesStatusA'; {$ENDIF} {$EXTERNALSYM EnumServicesStatus}
と再定義しておきます。そして
type TEnumServicesFunc = function (const ServiceName: String; const DisplayName: String; ServiceStatus: TServiceStatus; const CommandLine: String; Data: Pointer): Boolean; procedure EnumServices(Func: TEnumServicesFunc; Data: Pointer); var hSCManager: SC_HANDLE; RetVal: Boolean; BytesNeeded: DWORD; ServicesReturned: DWORD; ResumeHandle: DWORD; PServiceStatus: PEnumServiceStatusArray; hService: SC_HANDLE; PServiceConfig: Pointer; Index: Integer; ServiceName: String; DisplayName: String; ServiceStatus: TServiceStatus; CommandLine: String; begin { Open service manager } hSCManager := OpenSCManager(nil,nil,SC_MANAGER_ENUMERATE_SERVICE); if hSCManager = 0 then begin RaiseLastOSError; end; PServiceStatus := nil; try { Get buffer size } BytesNeeded := 0; ServicesReturned := 0; ResumeHandle := 0; RetVal := EnumServicesStatus(hSCManager,SERVICE_WIN32, SERVICE_STATE_ALL,nil,0, BytesNeeded,ServicesReturned,ResumeHandle); if RetVal = False then begin { Allocate buffer for EnumServicesStatus } PServiceStatus := AllocMem(BytesNeeded); { Enumerate service status } ResumeHandle := 0; RetVal := EnumServicesStatus(hSCManager,SERVICE_WIN32, SERVICE_STATE_ALL,PServiceStatus,BytesNeeded, BytesNeeded,ServicesReturned,ResumeHandle); end; if RetVal = False then begin RaiseLastOSError; end; for Index := 0 to ServicesReturned - 1 do begin ServiceName := PServiceStatus^[Index].lpServiceName; DisplayName := PServiceStatus^[Index].lpDisplayName; ServiceStatus := PServiceStatus^[Index].ServiceStatus; CommandLine := ''; { Open service for QueryServiceConfig } hService := OpenService(hSCManager,PChar(ServiceName),SERVICE_QUERY_CONFIG); if hService <> 0 then begin PServiceConfig := nil; try { Get buffer size } QueryServiceConfig(hService,nil,0,BytesNeeded); { Allocate buffer for QueryServiceConfig } PServiceConfig := AllocMem(BytesNeeded); { Query service configuration } if QueryServiceConfig(hService,PServiceConfig, BytesNeeded,BytesNeeded) = True then begin { Binary pathname } CommandLine := TQueryServiceConfig(PServiceConfig^).lpBinaryPathName; end; finally { Close service } CloseServiceHandle(hService); { Free buffer } if PServiceConfig <> nil then begin FreeMem(PServiceConfig); end; end; end; { Callback } if Assigned(Func) then begin if Func(ServiceName,DisplayName, ServiceStatus,CommandLine,Data) = False then begin Break; end; end; end; finally { Close service manager handle } CloseServiceHandle(hSCManager); { Free buffer } if PServiceStatus <> nil then begin FreeMem(PServiceStatus); end; end; end;
とします。例によってDelphi 2009以降の無名メソッド(anonymous method)を使用する場合は
type TEnumServicesFunc = reference to function (const ServiceName: String; const DisplayName: String; ServiceStatus: TServiceStatus; const CommandLine: String): Boolean; procedure EnumServices(Func: TEnumServicesFunc); var hSCManager: SC_HANDLE; RetVal: Boolean; BytesNeeded: DWORD; ServicesReturned: DWORD; ResumeHandle: DWORD; PServiceStatus: PEnumServiceStatusArray; hService: SC_HANDLE; PServiceConfig: Pointer; Index: Integer; ServiceName: String; DisplayName: String; ServiceStatus: TServiceStatus; CommandLine: String; begin { Open service manager } hSCManager := OpenSCManager(nil,nil,SC_MANAGER_ENUMERATE_SERVICE); if hSCManager = 0 then begin RaiseLastOSError; end; PServiceStatus := nil; try { Get buffer size } BytesNeeded := 0; ServicesReturned := 0; ResumeHandle := 0; RetVal := EnumServicesStatus(hSCManager,SERVICE_WIN32, SERVICE_STATE_ALL,nil,0, BytesNeeded,ServicesReturned,ResumeHandle); if RetVal = False then begin { Allocate buffer for EnumServicesStatus } PServiceStatus := AllocMem(BytesNeeded); { Enumerate service status } ResumeHandle := 0; RetVal := EnumServicesStatus(hSCManager,SERVICE_WIN32, SERVICE_STATE_ALL,PServiceStatus,BytesNeeded, BytesNeeded,ServicesReturned,ResumeHandle); end; if RetVal = False then begin RaiseLastOSError; end; for Index := 0 to ServicesReturned - 1 do begin ServiceName := PServiceStatus^[Index].lpServiceName; DisplayName := PServiceStatus^[Index].lpDisplayName; ServiceStatus := PServiceStatus^[Index].ServiceStatus; CommandLine := ''; { Open service for QueryServiceConfig } hService := OpenService(hSCManager,PChar(ServiceName),SERVICE_QUERY_CONFIG); if hService <> 0 then begin PServiceConfig := nil; try { Get buffer size } QueryServiceConfig(hService,nil,0,BytesNeeded); { Allocate buffer for QueryServiceConfig } PServiceConfig := AllocMem(BytesNeeded); { Query service configuration } if QueryServiceConfig(hService,PServiceConfig, BytesNeeded,BytesNeeded) = True then begin { Binary pathname } CommandLine := TQueryServiceConfig(PServiceConfig^).lpBinaryPathName; end; finally { Close service } CloseServiceHandle(hService); { Free buffer } if PServiceConfig <> nil then begin FreeMem(PServiceConfig); end; end; end; { Callback } if Assigned(Func) then begin if Func(ServiceName,DisplayName, ServiceStatus,CommandLine) = False then begin Break; end; end; end; finally { Close service manager handle } CloseServiceHandle(hSCManager); { Free buffer } if PServiceStatus <> nil then begin FreeMem(PServiceStatus); end; end; end;
こんな感じになります。なお取得したコマンドラインの中にはパス名に空白文字を含むにもかかわらずダブルクォーテーションで括られていないものもあるため、コマンドラインから実行ファイルのフルパス名を取り出すには
uses StrUtils; function CommandlineToPathname(const CommandLine: String): String; var Start: Integer; Position: Integer; begin Result := CommandLine; if Result = '' then begin Exit; end; if Result[1] = '"' then begin { Quoted } Delete(Result,1,1); // Delete first double quote {$IFDEF Unicode} Position := Pos('"',Result); {$ELSE} Position := AnsiPos('"',Result); {$ENDIF} if Position > 0 then begin Delete(Result,Position,Length(Result)); end; end else begin { Not quoted } Start := 1; while True do begin Position := PosEx(' ',Result,Start); if Position = 0 then begin Break; end; Start := Position + 1; {$IFDEF Unicode} if CharInSet(Result[Start],['-','/']) then {$ELSE} if Result[Start] in ['-','/'] then {$ENDIF} begin Delete(Result,Position,Length(Result)); Break; end; end; end; end;
このように一捻り必要です。
元ねたは旧Delphi-MLの90131以下のスレッド(特に90133のKHE00221さんの回答)。
2010年7月21日
ネットワークアダプタの情報を取得する(IPv4)
ネットワークアダプタの情報を取得するにはGetAdaptersInfoを使用します。取得した情報はIP_ADAPTER_INFO構造体に格納されますが、複数のネットワークアダプタが存在する場合は戻値がERROR_BUFFER_OVERFLOWになり、第2パラメータのpOutBufLenに必要なサイズがバイト単位で返されるので、領域を再確保して再びGetAdaptersInfoを呼び出す必要があります。この場合はIP_ADAPTER_INFO構造体のNextがNULLになるまでたどっていくことになります。
まずこれらのIPヘルパAPIや構造体を定義します。
そして
とします。Delphi 2009以降の無名メソッド(anonymous method)を使用する場合はこんな感じに。
なおIP_ADAPTER_INFO構造体のTypeの値として無線LANの場合の定義がMIB_IF_TYPE_...にはありませんが、実際には71が格納されるようです。
またIPv6(Windows XP以降)の場合はGetAdaptersAddressesを使用する必要があります。
まずこれらのIPヘルパAPIや構造体を定義します。
uses Windows, SysUtils; const MAX_ADAPTER_DESCRIPTION_LENGTH = 128; {$EXTERNALSYM MAX_ADAPTER_DESCRIPTION_LENGTH} MAX_ADAPTER_NAME_LENGTH = 256; {$EXTERNALSYM MAX_ADAPTER_NAME_LENGTH} MAX_ADAPTER_ADDRESS_LENGTH = 8; {$EXTERNALSYM MAX_ADAPTER_ADDRESS_LENGTH} MIB_IF_TYPE_OTHER = 1; {$EXTERNALSYM MIB_IF_TYPE_OTHER} MIB_IF_TYPE_ETHERNET = 6; {$EXTERNALSYM MIB_IF_TYPE_ETHERNET} MIB_IF_TYPE_TOKENRING = 9; {$EXTERNALSYM MIB_IF_TYPE_TOKENRING} MIB_IF_TYPE_FDDI = 15; {$EXTERNALSYM MIB_IF_TYPE_FDDI} MIB_IF_TYPE_PPP = 23; {$EXTERNALSYM MIB_IF_TYPE_PPP} MIB_IF_TYPE_LOOPBACK = 24; {$EXTERNALSYM MIB_IF_TYPE_LOOPBACK} MIB_IF_TYPE_SLIP = 28; {$EXTERNALSYM MIB_IF_TYPE_SLIP} type time_t = Longint; {$EXTERNALSYM time_t} PIP_MASK_STRING = ^IP_MASK_STRING; {$EXTERNALSYM PIP_MASK_STRING} IP_ADDRESS_STRING = record S: array [0..15] of AnsiChar; end; {$EXTERNALSYM IP_ADDRESS_STRING} PIP_ADDRESS_STRING = ^IP_ADDRESS_STRING; {$EXTERNALSYM PIP_ADDRESS_STRING} IP_MASK_STRING = IP_ADDRESS_STRING; {$EXTERNALSYM IP_MASK_STRING} PIP_ADDR_STRING = ^IP_ADDR_STRING; {$EXTERNALSYM PIP_ADDR_STRING} _IP_ADDR_STRING = record Next: PIP_ADDR_STRING; IpAddress: IP_ADDRESS_STRING; IpMask: IP_MASK_STRING; Context: DWORD; end; {$EXTERNALSYM _IP_ADDR_STRING} IP_ADDR_STRING = _IP_ADDR_STRING; {$EXTERNALSYM IP_ADDR_STRING} PIP_ADAPTER_INFO = ^IP_ADAPTER_INFO; {$EXTERNALSYM PIP_ADAPTER_INFO} _IP_ADAPTER_INFO = record Next: PIP_ADAPTER_INFO; ComboIndex: DWORD; AdapterName: array [0..MAX_ADAPTER_NAME_LENGTH + 3] of AnsiChar; Description: array [0..MAX_ADAPTER_DESCRIPTION_LENGTH + 3] of AnsiChar; AddressLength: UINT; Address: array [0..MAX_ADAPTER_ADDRESS_LENGTH - 1] of BYTE; Index: DWORD; Type_: UINT; DhcpEnabled: UINT; CurrentIpAddress: PIP_ADDR_STRING; IpAddressList: IP_ADDR_STRING; GatewayList: IP_ADDR_STRING; DhcpServer: IP_ADDR_STRING; HaveWins: BOOL; PrimaryWinsServer: IP_ADDR_STRING; SecondaryWinsServer: IP_ADDR_STRING; LeaseObtained: time_t; LeaseExpires: time_t; end; {$EXTERNALSYM _IP_ADAPTER_INFO} IP_ADAPTER_INFO = _IP_ADAPTER_INFO; {$EXTERNALSYM IP_ADAPTER_INFO} function GetAdaptersInfo(pAdapterInfo: PIP_ADAPTER_INFO; var pOutBufLen: ULONG): DWORD; stdcall; external 'iphlpapi.dll' Name 'GetAdaptersInfo'; {$EXTERNALSYM GetAdaptersInfo}
そして
type TAdapterType = (atUnknown, atOther, atEthernet, atTokenRing, atFDDI, atPPP, atLoopback, atSLIP, atWLAN); TEnumAdapterInfoFunc = function (const AdapterName: String; const Description: String; const HardwareAddress: String; AdapterIndex: Integer; AdapterType: TAdapterType; DhcpEnabled: Boolean; const IPAddress: String; const Mask: String; const Gateway: String; const DHCPServer: String; Data: Pointer): Boolean; procedure EnumAdapterInfo(Func: TEnumAdapterInfoFunc; Data: Pointer); function FormatHardwareAddress(Ptr: PIP_ADAPTER_INFO): String; var Index: Integer; begin Result := ''; for Index := 0 to Ptr^.AddressLength - 1 do begin Result := Result + Format('%02.2X-',[Ptr^.Address[Index]]); end; if Result <> '' then begin Delete(Result,Length(Result),1); end; end; var pAdapterInfo: PIP_ADAPTER_INFO; Ptr: PIP_ADAPTER_INFO; pOutBufLen: ULONG; RetVal: DWORD; AdapterIndex: Integer; AdapterName: String; Description: String; HardwareAddress: String; AdapterType: TAdapterType; IPAddress: String; NetMask: String; Gateway: String; DHCPServer: String; begin pOutBufLen := SizeOf(IP_ADAPTER_INFO); pAdapterInfo := AllocMem(pOutBufLen); try RetVal := GetAdaptersInfo(pAdapterInfo,pOutBufLen); if RetVal = ERROR_BUFFER_OVERFLOW then begin FreeMem(pAdapterInfo); pAdapterInfo := AllocMem(pOutBufLen); RetVal := GetAdaptersInfo(pAdapterInfo,pOutBufLen); end; if RetVal <> NO_ERROR then begin Exit; end; Ptr := pAdapterInfo; while Ptr <> nil do begin AdapterName := Trim(String(Ptr^.AdapterName)); Description := Trim(String(Ptr^.Description)); HardwareAddress := FormatHardwareAddress(Ptr); AdapterIndex := Ptr^.Index; case Ptr^.Type_ of MIB_IF_TYPE_OTHER: begin AdapterType := atOther; end; MIB_IF_TYPE_ETHERNET: begin AdapterType := atEthernet; end; MIB_IF_TYPE_TOKENRING: begin AdapterType := atTokenRing; end; MIB_IF_TYPE_FDDI: begin AdapterType := atFDDI; end; MIB_IF_TYPE_PPP: begin AdapterType := atPPP; end; MIB_IF_TYPE_LOOPBACK: begin AdapterType := atLoopback; end; MIB_IF_TYPE_SLIP: begin AdapterType := atSLIP; end; 71: begin AdapterType := atWLAN; end; else begin AdapterType := atUnknown; end; end; IPAddress := String(Ptr^.IpAddressList.IpAddress.S); NetMask := String(Ptr^.IpAddressList.IpMask.S); Gateway := String(Ptr^.GatewayList.IpAddress.S); if Ptr^.DhcpEnabled = 0 then begin DHCPServer := ''; end else begin DHCPServer := String(Ptr^.DhcpServer.IpAddress.S); end; if Assigned(Func) then begin if Func(AdapterName,Description,HardwareAddress,AdapterIndex, AdapterType,Ptr^.DhcpEnabled <> 0,IPAddress,NetMask, Gateway,DHCPServer,Data) = False then begin Break; end; end; Ptr := Ptr^.Next; end; finally FreeMem(pAdapterInfo); end; end;
とします。Delphi 2009以降の無名メソッド(anonymous method)を使用する場合はこんな感じに。
type TAdapterType = (atUnknown, atOther, atEthernet, atTokenRing, atFDDI, atPPP, atLoopback, atSLIP, atWLAN); TEnumAdapterInfoFunc = reference to function (const AdapterName: String; const Description: String; const HardwareAddress: String; AdapterIndex: Integer; AdapterType: TAdapterType; DhcpEnabled: Boolean; const IPAddress: String; const Mask: String; const Gateway: String; const DHCPServer: String): Boolean; procedure EnumAdapterInfo(Func: TEnumAdapterInfoFunc); function FormatHardwareAddress(Ptr: PIP_ADAPTER_INFO): String; var Index: Integer; begin Result := ''; for Index := 0 to Ptr^.AddressLength - 1 do begin Result := Result + Format('%02.2X-',[Ptr^.Address[Index]]); end; if Result <> '' then begin Delete(Result,Length(Result),1); end; end; var pAdapterInfo: PIP_ADAPTER_INFO; Ptr: PIP_ADAPTER_INFO; pOutBufLen: ULONG; RetVal: DWORD; AdapterIndex: Integer; AdapterName: String; Description: String; HardwareAddress: String; AdapterType: TAdapterType; IPAddress: String; NetMask: String; Gateway: String; DHCPServer: String; begin pOutBufLen := SizeOf(IP_ADAPTER_INFO); pAdapterInfo := AllocMem(pOutBufLen); try RetVal := GetAdaptersInfo(pAdapterInfo,pOutBufLen); if RetVal = ERROR_BUFFER_OVERFLOW then begin FreeMem(pAdapterInfo); pAdapterInfo := AllocMem(pOutBufLen); RetVal := GetAdaptersInfo(pAdapterInfo,pOutBufLen); end; if RetVal <> NO_ERROR then begin Exit; end; Ptr := pAdapterInfo; while Ptr <> nil do begin AdapterName := Trim(String(Ptr^.AdapterName)); Description := Trim(String(Ptr^.Description)); HardwareAddress := FormatHardwareAddress(Ptr); AdapterIndex := Ptr^.Index; case Ptr^.Type_ of MIB_IF_TYPE_OTHER: begin AdapterType := atOther; end; MIB_IF_TYPE_ETHERNET: begin AdapterType := atEthernet; end; MIB_IF_TYPE_TOKENRING: begin AdapterType := atTokenRing; end; MIB_IF_TYPE_FDDI: begin AdapterType := atFDDI; end; MIB_IF_TYPE_PPP: begin AdapterType := atPPP; end; MIB_IF_TYPE_LOOPBACK: begin AdapterType := atLoopback; end; MIB_IF_TYPE_SLIP: begin AdapterType := atSLIP; end; 71: begin AdapterType := atWLAN; end; else begin AdapterType := atUnknown; end; end; IPAddress := String(Ptr^.IpAddressList.IpAddress.S); NetMask := String(Ptr^.IpAddressList.IpMask.S); Gateway := String(Ptr^.GatewayList.IpAddress.S); if Ptr^.DhcpEnabled = 0 then begin DHCPServer := ''; end else begin DHCPServer := String(Ptr^.DhcpServer.IpAddress.S); end; if Assigned(Func) then begin if Func(AdapterName,Description,HardwareAddress,AdapterIndex, AdapterType,Ptr^.DhcpEnabled <> 0,IPAddress,NetMask, Gateway,DHCPServer) = False then begin Break; end; end; Ptr := Ptr^.Next; end; finally FreeMem(pAdapterInfo); end; end;
なおIP_ADAPTER_INFO構造体のTypeの値として無線LANの場合の定義がMIB_IF_TYPE_...にはありませんが、実際には71が格納されるようです。
またIPv6(Windows XP以降)の場合はGetAdaptersAddressesを使用する必要があります。
2010年7月20日
参加しているドメインまたはワークグループの種類と名前を取得する
前回のNetWkstaGetInfoを使用する方法ではNTドメイン(AD)に参加しているのかワークグループに参加しているのかを知ることができませんでした。そこでNetGetJoinInformation(ja)で参加しているNTドメインまたはワークグループの名前と参加ステータスを取得してみます。ここでもNetWkstaGetInfoで取得したドメインまたはワークグループの名前の領域はNetApiBufferFree(ja)で解放する必要があることに注意が必要です。
まずこれらのネットワーク管理APIと列挙を定義します。
あとはこれを呼び出すだけです。
まずこれらのネットワーク管理APIと列挙を定義します。
uses Windows; type NET_API_STATUS = DWORD; {$EXTERNALSYM NET_API_STATUS} _NETSETUP_JOIN_STATUS = (NetSetupUnknownStatus = 0, // The status is unknown. NetSetupUnjoined, // The computer is not joined. NetSetupWorkgroupName, // The computer is joined to a workgroup. NetSetupDomainName); // The computer is joined to a domain. {$EXTERNALSYM _NETSETUP_JOIN_STATUS} NETSETUP_JOIN_STATUS = _NETSETUP_JOIN_STATUS; {$EXTERNALSYM NETSETUP_JOIN_STATUS} PNETSETUP_JOIN_STATUS = ^NETSETUP_JOIN_STATUS; {$EXTERNALSYM PNETSETUP_JOIN_STATUS} TNetsetupJoinStatus = NETSETUP_JOIN_STATUS; const NERR_Success = 0; {$EXTERNALSYM NERR_Success} function NetGetJoinInformation(lpServer: PWideChar; var lpNameBuffer: PWideChar; BufferType: PNETSETUP_JOIN_STATUS): NET_API_STATUS; stdcall; external 'netapi32.dll' Name 'NetGetJoinInformation'; {$EXTERNALSYM NetGetJoinInformation} function NetApiBufferFree(Buffer: Pointer): NET_API_STATUS; stdcall; external 'netapi32.dll' Name 'NetApiBufferFree'; {$EXTERNALSYM NetApiBufferFree}
あとはこれを呼び出すだけです。
function GetNetworkJoinInformation(var NetworkName: String; var Status: TNetsetupJoinStatus): Boolean; var Buffer: PWideChar; begin if NetGetJoinInformation(nil,Buffer,@Status) <> NERR_Success then begin Result := False; Exit; end; NetworkName := Buffer; NetApiBufferFree(Buffer); Result := True; end;
2010年7月16日
プログラミングの魔導書 予約受付開始
プログラミングの魔導書 ~Programmers' Grimoire~ Vol.1の予約受付が始まっています。
株式会社ロングゲート - 製品案内
書籍版は2010/08/06までに予約しないと入手不能になる(それ以降はPDFのみになる)とのことです。
元ねたは江添さんの本の虫: 新しいプログラミング雑誌の発行とアキラさんの[Grimoire]「プログラミングの魔導書」予約開始! - Faith and Brave - C++で遊ぼう。
株式会社ロングゲート - 製品案内
ただいま予約受付中
予約期間は 2010年8月6日(金) までとなります。
完全受注生産のため、書籍版については予約でのみご購入いただけます。
PDF版につきましては、予約終了後でも購入可能です。
書籍版 1,500円(税込・送料別)
PDF版 1,000円(税込)
書籍+PDFセット 2,000円(税込・送料別)
書籍版は2010/08/06までに予約しないと入手不能になる(それ以降はPDFのみになる)とのことです。
元ねたは江添さんの本の虫: 新しいプログラミング雑誌の発行とアキラさんの[Grimoire]「プログラミングの魔導書」予約開始! - Faith and Brave - C++で遊ぼう。
参加しているドメインまたはワークグループの名前を取得する
現在参加しているNTドメインまたはワークグループの名前を取得するにはNetWkstaGetInfo(ja)を使用します。NetWkstaGetInfoで取得した構造体(WKSTA_INFO_100、WKSTA_INFO_101、WKSTA_INFO_102)はNetApiBufferFree(ja)で解放する必要があることに注意が必要です。
まずこれらのネットワーク管理APIや構造体を定義します。
今回はローカルコンピュータのNTドメイン/ワークグループ名があれば十分なので、servernameにNULLを、levelに100を指定してWKSTA_INFO_100構造体を取得し、wki100_langroupに格納されている文字列を取り出します。
ただしこの方法ではNTドメインに参加しているのかワークグループに参加しているのかを知ることができないので、NetGetJoinInformationを使用する方法をお勧めします。
元ねたはDelphiDabbler.comのGet the network computer and domain names。
まずこれらのネットワーク管理APIや構造体を定義します。
uses Windows; type NET_API_STATUS = DWORD; {$EXTERNALSYM NET_API_STATUS} PWKSTA_INFO_100 = ^WKSTA_INFO_100; {$EXTERNALSYM PWKSTA_INFO_100} _WKSTA_INFO_100 = record wki100_platform_id: DWORD; wki100_computername: PWideChar; wki100_langroup: PWideChar; wki100_ver_major: DWORD; wki100_ver_minor: DWORD; end; {$EXTERNALSYM _WKSTA_INFO_100} WKSTA_INFO_100 = _WKSTA_INFO_100; {$EXTERNALSYM WKSTA_INFO_100} PWKSTA_INFO_101 = ^WKSTA_INFO_101; {$EXTERNALSYM PWKSTA_INFO_101} _WKSTA_INFO_101 = record wki101_platform_id: DWORD; wki101_computername: PWideChar; wki101_langroup: PWideChar; wki101_ver_major: DWORD; wki101_ver_minor: DWORD; wki101_lanroot: PWideChar; end; {$EXTERNALSYM _WKSTA_INFO_101} WKSTA_INFO_101 = _WKSTA_INFO_101; {$EXTERNALSYM WKSTA_INFO_101} PWKSTA_INFO_102 = ^WKSTA_INFO_102; {$EXTERNALSYM PWKSTA_INFO_102} _WKSTA_INFO_102 = record wki102_platform_id: DWORD; wki102_computername: PWideChar; wki102_langroup: PWideChar; wki102_ver_major: DWORD; wki102_ver_minor: DWORD; wki102_lanroot: PWideChar; wki102_logged_on_users: DWORD; end; {$EXTERNALSYM _WKSTA_INFO_102} WKSTA_INFO_102 = _WKSTA_INFO_102; {$EXTERNALSYM WKSTA_INFO_102} const NERR_Success = 0; {$EXTERNALSYM NERR_Success} function NetWkstaGetInfo(servername: PWideChar; level: DWORD; var bufptr: PByte): NET_API_STATUS; stdcall; external 'netapi32.dll' Name 'NetWkstaGetInfo'; {$EXTERNALSYM NetWkstaGetInfo} function NetApiBufferFree(Buffer: Pointer): NET_API_STATUS; stdcall; external 'netapi32.dll' Name 'NetApiBufferFree'; {$EXTERNALSYM NetApiBufferFree}
今回はローカルコンピュータのNTドメイン/ワークグループ名があれば十分なので、servernameにNULLを、levelに100を指定してWKSTA_INFO_100構造体を取得し、wki100_langroupに格納されている文字列を取り出します。
function GetNTDomainName: String; var Ptr: PWKSTA_INFO_100; begin Result := ''; if NetWkstaGetInfo(nil,100,PByte(Ptr)) <> NERR_Success then begin Exit; end; Result := Ptr^.wki100_langroup; NetApiBufferFree(Ptr); end;
ただしこの方法ではNTドメインに参加しているのかワークグループに参加しているのかを知ることができないので、NetGetJoinInformationを使用する方法をお勧めします。
元ねたはDelphiDabbler.comのGet the network computer and domain names。
Nick HodgesがEmbarcaderoを退職
RAD StudioのR&DマネージャだったNick Hodgesさんがエンバカデロ・テクノロジーズを退職したというニュースが飛び込んできました。
Good luck Nick « Delphi Haven
Embarcadero Discussion Forums: Nick Hodges blog doesn't exist in ...
ForumのRobert Loveさんの発言によれば、
ForumにEmbarcadero Discussion Forums: Changes for Nick ...というスレッドが立っています。
2010/07/19追記: Nick Hodges | A man's got to know his limitationsができています。"Nick, thank you for your all works, and good luck!"
Good luck Nick « Delphi Haven
Embarcadero Discussion Forums: Nick Hodges blog doesn't exist in ...
ForumのRobert Loveさんの発言によれば、
I was notified today that Nick no longer works at Embarcardero.とのことです。
ForumにEmbarcadero Discussion Forums: Changes for Nick ...というスレッドが立っています。
There have been some rumors floating around, and I wanted to set things straight.ということだそうです。
I was let go by Embarcadero on Monday. This wasn't my decision.
2010/07/19追記: Nick Hodges | A man's got to know his limitationsができています。"Nick, thank you for your all works, and good luck!"
2010年7月15日
第17回エンバカデロ・デベロッパーキャンプ開催決定
第17回エンバカデロ・デベロッパーキャンプは2010年09月15日に開催されます。時期的にRAD Studio 2011の話が出てくるのでしょうか。今回はエンバカデロ・テクノロジーズ 製品担当副社長("Senior Vice President of Marketing and Product Management")のMichael Swindellさんがいらっしゃるようです。
2010/09/07追記: 一部のセッションがUstreamで配信されるそうです。詳細は当日(2010/09/15)にデベロッパーキャンプのページで確認してくださいとのこと。
2010/09/09再追記: http://www.embarcadero.com/jp/landing-pages/developer-camp-liveでストリーミングが配信されるとのことです。
2010/09/10再々追記: Team Japanに情報が追加されています。
Team Japan » 第17回 エンバカデロ・デベロッパーキャンプ情報(その1) - 会場へのアクセス
Team Japan » 第17回 エンバカデロ・デベロッパーキャンプ情報(その2) - ライブ配信
2010/09/07追記: 一部のセッションがUstreamで配信されるそうです。詳細は当日(2010/09/15)にデベロッパーキャンプのページで確認してくださいとのこと。
2010/09/09再追記: http://www.embarcadero.com/jp/landing-pages/developer-camp-liveでストリーミングが配信されるとのことです。
2010/09/10再々追記: Team Japanに情報が追加されています。
Team Japan » 第17回 エンバカデロ・デベロッパーキャンプ情報(その1) - 会場へのアクセス
Team Japan » 第17回 エンバカデロ・デベロッパーキャンプ情報(その2) - ライブ配信
2010年7月14日
実行環境のアーキテクチャを取得する
実行環境のアーキテクチャはGetSystemInfo(ja)で取得したSYSTEM_INFO構造体のwProcessorArchitectureで知ることができるはずです。ところが実際には32bitのアプリケーションからは64bit版WindowsでもwProcessorArchitectureがPROCESSOR_ARCHITECTURE_INTEL(0)となってしまいます。GetSystemInfoの説明をよく見ると
2012/07/07追記: Delphi XE2以降であればSystem.SysUtilsユニットのTOSVersion.Architectureで実行環境のアーキテクチャを知ることができます。ねた元はZarko GajicさんのIs Your 32bit Delphi Applications Running On x86 (Win 32) OR x64 (Win 64)?。
To retrieve accurate information for an application running on WOW64, call the GetNativeSystemInfo function.ということでGetNativeSystemInfoを使用する必要があるようです。GetNativeSystemInfoはWindows XP/Server 2003以降でしか使えませんので、それ以前の環境ではGetSystemInfoを使用します。
uses Windows; const PROCESSOR_ARCHITECTURE_AMD64 = 9; // x64 (AMD or Intel) PROCESSOR_ARCHITECTURE_IA64 = 6; // Intel Itanium Processor Family (IPF) PROCESSOR_ARCHITECTURE_INTEL = 0; // x86 PROCESSOR_ARCHITECTURE_UNKNOWN = $FFFF; // Unknown architecture type TGetSystemInfoFunc = procedure (var lpSystemInfo: TSystemInfo); stdcall; function GetProcessorArchitecture: Word; var SI: TSystemInfo; GetSystemInfoFunc: TGetSystemInfoFunc; begin FillChar(SI,SizeOf(SI),0); { Call GetNativeSystemInfo or GetSystemInfo } @GetSystemInfoFunc := GetProcAddress(GetModuleHandle(kernel32), 'GetNativeSystemInfo'); if Assigned(GetSystemInfoFunc) = True then begin GetSystemInfoFunc(SI); end else begin GetSystemInfo(SI); end; Result := SI.wProcessorArchitecture; end;
2012/07/07追記: Delphi XE2以降であればSystem.SysUtilsユニットのTOSVersion.Architectureで実行環境のアーキテクチャを知ることができます。ねた元はZarko GajicさんのIs Your 32bit Delphi Applications Running On x86 (Win 32) OR x64 (Win 64)?。
2010年7月13日
コンピュータ名を取得する
一時ファイル名の一部として使うなど、コンピュータ名を取得したい状況というのはそれなりにあると思います。そこでまずGetComputerName(ja)でNetBIOS名を取得してみます。
次にGetComputerNameEx(ja)(Windows 2000以降であれば使用可能)でDNSホスト名や完全修飾DNS名を取得してみます。まずはWindows.pasにGetComputerNameEx(W)と第1パラメータのCOMPUTER_NAME_FORMATの定義がWindows.pasに存在しない(Delphi 2010にはあるのかな?)ので用意します。
そしてこれを
という形で呼び出します。1回目のGetComputerNameExではlpBufferをNULLに、nSizeを0にすることでバッファに必要なサイズ(終端のNUL文字を含む)を取得し、2回目のGetComputerNameExで実際のコンピュータ名を取得するのですが、GetComputerNameExA(ANSI版)ではlpBufferをNULLにしても正常にバッファサイズを取得できないため、GetComputerNameExW(Unicode版)を使用しています。
uses Windows, SysUtils; function GetComputerName: String; var Size: DWORD; begin { Adjust buffer size } Size := MAX_COMPUTERNAME_LENGTH + 1; SetLength(Result,Size); { Get computer name } if Windows.GetComputerName(PChar(Result),Size) = False then begin RaiseLastOSError; end; SetLength(Result,Size); end;
次にGetComputerNameEx(ja)(Windows 2000以降であれば使用可能)でDNSホスト名や完全修飾DNS名を取得してみます。まずはWindows.pasにGetComputerNameEx(W)と第1パラメータのCOMPUTER_NAME_FORMATの定義がWindows.pasに存在しない(Delphi 2010にはあるのかな?)ので用意します。
type {$Z4} _COMPUTER_NAME_FORMAT = (ComputerNameNetBIOS, ComputerNameDnsHostname, ComputerNameDnsDomain, ComputerNameDnsFullyQualified, ComputerNamePhysicalNetBIOS, ComputerNamePhysicalDnsHostname, ComputerNamePhysicalDnsDomain, ComputerNamePhysicalDnsFullyQualified, ComputerNameMax); {$EXTERNALSYM _COMPUTER_NAME_FORMAT} COMPUTER_NAME_FORMAT = _COMPUTER_NAME_FORMAT; {$EXTERNALSYM COMPUTER_NAME_FORMAT} TComputerNameFormat = COMPUTER_NAME_FORMAT; function GetComputerNameExW(NameType: COMPUTER_NAME_FORMAT; lpBuffer: PWideChar; var nSize: DWORD): BOOL; stdcall; external kernel32 name 'GetComputerNameExW'; {$EXTERNALSYM GetComputerNameExW}
そしてこれを
uses Windows, SysUtils; function GetComputerNameEx(NameType: TComputerNameFormat): String; var {$IFDEF Unicode} Buffer: String; {$ELSE} Buffer: WideString; {$ENDIF} Size: DWORD; begin { Adjust buffer size } Size := 0; GetComputerNameExW(NameType,nil,Size); SetLength(Buffer,Size); { Get computer name } if GetComputerNameExW(NameType,PWideChar(Buffer),Size) = False then begin RaiseLastOSError; end; SetLength(Buffer,Size); Result := Buffer; end;
という形で呼び出します。1回目のGetComputerNameExではlpBufferをNULLに、nSizeを0にすることでバッファに必要なサイズ(終端のNUL文字を含む)を取得し、2回目のGetComputerNameExで実際のコンピュータ名を取得するのですが、GetComputerNameExA(ANSI版)ではlpBufferをNULLにしても正常にバッファサイズを取得できないため、GetComputerNameExW(Unicode版)を使用しています。
2010年7月10日
[書籍]アプレンティスシップ・パターン
有隣堂 ヨドバシAKIBA店で
アプレンティスシップ・パターン (amazon)/Dave H. Hoover、Adewale Oshiney著/柴田 芳樹訳/オライリージャパン/ISBN 978-4-87311-460-6/2,310円
を購入。
アプレンティスシップ・パターン (amazon)/Dave H. Hoover、Adewale Oshiney著/柴田 芳樹訳/オライリージャパン/ISBN 978-4-87311-460-6/2,310円
を購入。
2010年7月8日
2nd anniversary
さすがに今年は覚えてました。とはいえここのところ内容がいまいちですね。この1年間のエントリは155件(2009/07/07-2010/07/02)。そろそろDelphi 2011の話が出始めると思われるので、今年も追いかけていきたいと思っています。
2010年7月2日
David Iが日本にきているらしい
David IことDavid Intersimoneさんが日本に来ていて懇談会をやっているようです(プレス関係かな?)。詳しくはTwitterのハッシュタグ#davidiかembarcadero_jp、CYonezawaあたりで。どうせならustreamあたりで生中継でもどうでしょう?
2010/07/04追記: builder by ZDNetの2010/06/18の開発環境の整備とクロスプラットフォームに注力--エンバカデロが新たな製品戦略 - builder by ZDNet Japanという記事にMalcolm Grovesさんの発言として
2010/07/05追記: 藤井さんの記事に従って2010/07/04追記分の記述を修正しました。アメリカ合衆国ではこの週末は独立記念日でお休みなんですね。
2010/07/04追記: builder by ZDNetの2010/06/18の開発環境の整備とクロスプラットフォームに注力--エンバカデロが新たな製品戦略 - builder by ZDNet Japanという記事にMalcolm Grovesさんの発言として
7月には、CodeGear製品のチーフエバンジェリストを務めるDavid Intersimone(David I)氏が来日し、開発ツールに関する詳細な説明を行うことも明らかにした。とあるので、週明けにはまた発表かインタビューがあるのではないでしょうかありますが、藤井さんのDavid I プライベートで日本へによれば今回はほぼプライベートだったようです。今月中にまた来るってことでしょうか。
2010/07/05追記: 藤井さんの記事に従って2010/07/04追記分の記述を修正しました。アメリカ合衆国ではこの週末は独立記念日でお休みなんですね。
2010年7月1日
2010/07開催のウェブセミナー
2010/07/07 18:00-19:00(JST) 7/7 - Webセミナー「データベース設計ツールER/Studioの概要と効果的活用法」
2010/07/14 18:00-19:00(JST) 7/14 - Webセミナー「システム品質を高めるデータベースツールからのアプローチ」
2010/07/21 18:00-19:00(JST) 7/21 - Webセミナー「新規プロジェクト開発に必須、データベースの情報共有インフラの活用」
2010/07/28 18:00-19:00(JST) 7/28 - Webセミナー「異種データベース利用者必見! 管理負荷を軽減するベストプラクティス」
2010/07/14 18:00-19:00(JST) 7/14 - Webセミナー「システム品質を高めるデータベースツールからのアプローチ」
2010/07/21 18:00-19:00(JST) 7/21 - Webセミナー「新規プロジェクト開発に必須、データベースの情報共有インフラの活用」
2010/07/28 18:00-19:00(JST) 7/28 - Webセミナー「異種データベース利用者必見! 管理負荷を軽減するベストプラクティス」
登録:
投稿 (Atom)