// tab size: 4 // following special commands are recognized and handled by this client // #quit // #exit // terminate client // #dbgon // turn on debug printing of windows messages // #dbgoff // turn off debug printing of windows messages // #connect // discover skype api // #disconnect // terminate connection to skype api // all other commands are sent "as is" directly to Skype // (no UTF-8 translation is done at present) #pragma comment(lib, "rpcrt4.lib") #include #include #include #include #include #include static LRESULT APIENTRY SkypeAPITest_Windows_WindowProc(HWND, UINT, WPARAM, LPARAM); HWND hInit_MainWindowHandle; // hWnd HINSTANCE hInit_ProcessHandle; // このプログラムのプロセスハンドル? char acInit_WindowClassName[128]; HANDLE hGlobal_ThreadShutdownEvent; bool volatile fGlobal_ThreadRunning = true; // Skypeとやりとりするメッセージ UINT uiGlobal_MsgID_SkypeControlAPIAttach; UINT uiGlobal_MsgID_SkypeControlAPIDiscover; HWND hGlobal_SkypeAPIWindowHandle = NULL; #if defined(_DEBUG) bool volatile fGlobal_DumpWindowsMessages = true; #else bool volatile fGlobal_DumpWindowsMessages = false; #endif DWORD ulGlobal_PromptConsoleMode = 0; HANDLE volatile hGlobal_PromptConsoleHandle = NULL; enum { SKYPECONTROLAPI_ATTACH_SUCCESS = 0, // Client is successfully attached and API window handle can be found in wParam parameter SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION = 1, // Skype has acknowledged connection request and is waiting for confirmation from the user. // The client is not yet attached and should wait for SKYPECONTROLAPI_ATTACH_SUCCESS message SKYPECONTROLAPI_ATTACH_REFUSED = 2, // User has explicitly denied access to client SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE = 3, // API is not available at the moment. For example, this happens when no user is currently logged in. // Client should wait for SKYPECONTROLAPI_ATTACH_API_AVAILABLE broadcast before making any further // connection attempts. SKYPECONTROLAPI_ATTACH_API_AVAILABLE = 0x8001 }; // // ウィンドウの作成と破棄 // bool Initialize_CreateWindowClass(void) { unsigned char *paucUUIDString; RPC_STATUS lUUIDResult; bool fReturnStatus; UUID oUUID; fReturnStatus = false; lUUIDResult = UuidCreate(&oUUID); hInit_ProcessHandle = (HINSTANCE)OpenProcess( PROCESS_DUP_HANDLE, FALSE, GetCurrentProcessId()); if ( hInit_ProcessHandle != NULL && (lUUIDResult == RPC_S_OK || lUUIDResult == RPC_S_UUID_LOCAL_ONLY) ) { if ( UuidToString( &oUUID, &paucUUIDString) == RPC_S_OK ) { WNDCLASS oWindowClass; strcpy( acInit_WindowClassName, "Skype-API-Test-"); strcat( acInit_WindowClassName, (char *)paucUUIDString); oWindowClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; oWindowClass.lpfnWndProc = (WNDPROC)&SkypeAPITest_Windows_WindowProc; oWindowClass.cbClsExtra = 0; oWindowClass.cbWndExtra = 0; oWindowClass.hInstance = hInit_ProcessHandle; oWindowClass.hIcon = NULL; oWindowClass.hCursor = NULL; oWindowClass.hbrBackground = NULL; oWindowClass.lpszMenuName = NULL; oWindowClass.lpszClassName = acInit_WindowClassName; if ( RegisterClass(&oWindowClass) != 0 ) fReturnStatus = true; RpcStringFree(&paucUUIDString); } } if ( fReturnStatus == false ) CloseHandle(hInit_ProcessHandle),hInit_ProcessHandle = NULL; return (fReturnStatus); } void DeInitialize_DestroyWindowClass(void) { UnregisterClass( acInit_WindowClassName, hInit_ProcessHandle); CloseHandle(hInit_ProcessHandle),hInit_ProcessHandle = NULL; } bool Initialize_CreateMainWindow(void) { hInit_MainWindowHandle = CreateWindowEx( WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, acInit_WindowClassName, "", WS_BORDER | WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 128, 128, NULL, 0, hInit_ProcessHandle, 0); return (hInit_MainWindowHandle != NULL? true:false); } void DeInitialize_DestroyMainWindow(void) { if ( hInit_MainWindowHandle != NULL ) DestroyWindow(hInit_MainWindowHandle),hInit_MainWindowHandle = NULL; } // // メッセージ処理 // // メッセージループ(メッセージを受け取る) void Global_MessageLoop(void) { MSG oMessage; while(GetMessage( &oMessage, 0, 0, 0) != FALSE) { TranslateMessage(&oMessage); DispatchMessage(&oMessage); } } // ウィンドウプロシージャ static LRESULT APIENTRY SkypeAPITest_Windows_WindowProc( HWND hWindow, UINT uiMessage, WPARAM uiParam, LPARAM ulParam) { LRESULT lReturnCode; bool fIssueDefProc; lReturnCode = 0; fIssueDefProc = false; switch (uiMessage) { // ウィンドウが破棄されたとき case WM_DESTROY: hInit_MainWindowHandle = NULL; PostQuitMessage(0); break; case WM_COPYDATA: // メッセージ受信側のスレッドからデータが送られてきたら if ( hGlobal_SkypeAPIWindowHandle == (HWND)uiParam ) { PCOPYDATASTRUCT poCopyData = (PCOPYDATASTRUCT)ulParam; printf( "Message from Skype(%u): %.*s\n", poCopyData->dwData, poCopyData->cbData, poCopyData->lpData); lReturnCode = 1; } break; default: // Skypeからのメッセージ if ( uiMessage == uiGlobal_MsgID_SkypeControlAPIAttach ) { switch (ulParam) { // アタッチに成功したら、Skypeのハンドルを記憶 case SKYPECONTROLAPI_ATTACH_SUCCESS: printf("!!! Connected; to terminate issue #disconnect\n"); hGlobal_SkypeAPIWindowHandle = (HWND)uiParam; break; case SKYPECONTROLAPI_ATTACH_PENDING_AUTHORIZATION: printf("!!! Pending authorization\n"); break; case SKYPECONTROLAPI_ATTACH_REFUSED: printf("!!! Connection refused\n"); break; case SKYPECONTROLAPI_ATTACH_NOT_AVAILABLE: printf("!!! Skype API not available\n"); break; case SKYPECONTROLAPI_ATTACH_API_AVAILABLE: printf("!!! Try connect now (API available); issue #connect\n"); break; } lReturnCode = 1; break; } fIssueDefProc = true; break; } // メッセージ処理をデフォルトの処理に任せる if ( fIssueDefProc ) lReturnCode = DefWindowProc( hWindow, uiMessage, uiParam, ulParam); // デバッグモードだったら if ( fGlobal_DumpWindowsMessages ) { // メッセージ内容を出力 printf( "WindowProc: hWindow = 0x%08X, MainWindow = 0x%08X, Message = %5u, WParam = 0x%08X, LParam = 0x%08X; Return = %ld%s\n", hWindow, hInit_MainWindowHandle, uiMessage, uiParam, ulParam, lReturnCode, fIssueDefProc? " (default)":""); } return (lReturnCode); } // コンソールから文字列を一行読み込む bool Global_Console_ReadRow( char *pacPromptBuffer, unsigned int uiMaxLength) { HANDLE hConsoleHandle, hDuplicatedConsoleHandle; DWORD ulCharactersRead, ulConsoleMode; unsigned int uiNewLength; BOOL fReadConsoleResult; bool fReturnStatus; char cCharacter; fReturnStatus = false; // 標準入力デバイスのハンドルを得られる間繰り返す while((hConsoleHandle = GetStdHandle(STD_INPUT_HANDLE)) != INVALID_HANDLE_VALUE) { // オブジェクトハンドルの複製を作る if ( DuplicateHandle( GetCurrentProcess(), hConsoleHandle, GetCurrentProcess(), &hDuplicatedConsoleHandle, 0, FALSE, DUPLICATE_SAME_ACCESS) == FALSE ) break; // コンソール入力バッファの現在の入力モード、またはコンソールスクリーンバッファの現在の出力モードを取得します。 GetConsoleMode( hDuplicatedConsoleHandle, &ulConsoleMode); SetConsoleMode( hDuplicatedConsoleHandle, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT); // コンソールのハンドルと状態を記憶 hGlobal_PromptConsoleHandle = hDuplicatedConsoleHandle; ulGlobal_PromptConsoleMode = ulConsoleMode; // コンソールから文字列を読み込む fReadConsoleResult = ReadConsole( hGlobal_PromptConsoleHandle, (LPVOID)pacPromptBuffer, uiMaxLength, &ulCharactersRead, (LPVOID)0); if ( hGlobal_PromptConsoleHandle == (HANDLE)0 ) break; hGlobal_PromptConsoleHandle = (HANDLE)0; // コンソールの状態を戻す? SetConsoleMode( hDuplicatedConsoleHandle, ulConsoleMode); // コンソールを解放 CloseHandle(hDuplicatedConsoleHandle); if ( fReadConsoleResult == FALSE || ulCharactersRead>uiMaxLength ) break; // 読み込んだ文字列の末尾にNULL文字を入れる pacPromptBuffer[ulCharactersRead] = 0; uiNewLength = ulCharactersRead; // 改行以降を削る while(uiNewLength != 0) { cCharacter = pacPromptBuffer[uiNewLength-1]; if ( cCharacter != '\r' && cCharacter != '\n' ) break; uiNewLength--; } // 文字列の末尾にNULL文字を入れる pacPromptBuffer[uiNewLength] = 0; fReturnStatus = true; break; } // 失敗してるときは、返す文字列を""(NULL文字)にする。 if ( fReturnStatus == false ) pacPromptBuffer[0] = 0; return (fReturnStatus); } // コンソールを元の状態に戻し解放 void Global_Console_CancelReadRow(void) { if ( hGlobal_PromptConsoleHandle != (HANDLE)0 ) { SetConsoleMode( hGlobal_PromptConsoleHandle, ulGlobal_PromptConsoleMode); CloseHandle(hGlobal_PromptConsoleHandle); hGlobal_PromptConsoleHandle = (HANDLE)0; } } // メッセージを送るためのスレッド void __cdecl Global_InputProcessingThread(void *) { static char acInputRow[1024]; bool fProcessed; // まずSkypeControlAPIDiscoverを全てのウィンドウにブロードキャストする(SkypeAPI仕様) if ( SendMessage( HWND_BROADCAST, uiGlobal_MsgID_SkypeControlAPIDiscover, (WPARAM)hInit_MainWindowHandle, 0) != 0 ) { // コンソールで入力されたデータから一行読み込む while(Global_Console_ReadRow( acInputRow, sizeof(acInputRow)-1)) { // // このプログラムへのコマンド // // 終了 if ( stricmp( acInputRow, "#quit") == 0 || stricmp( acInputRow, "#exit") == 0 ) break; fProcessed = false; // デバッグモードにする(受け取ったメッセージを全て出力) if ( stricmp( acInputRow, "#dbgon") == 0 ) { printf( "SkypeControlAPIAttach = %u, SkypeControlAPIDiscover = %u, hInit_MainWindowHandle = 0x%08lX\n", uiGlobal_MsgID_SkypeControlAPIAttach, uiGlobal_MsgID_SkypeControlAPIDiscover, hInit_MainWindowHandle); fGlobal_DumpWindowsMessages = true,fProcessed = true; } // デバッグモードを解除する if ( stricmp( acInputRow, "#dbgoff") == 0 ) fGlobal_DumpWindowsMessages = false,fProcessed = true; // 接続する if ( stricmp( acInputRow, "#connect") == 0 ) { SendMessage( HWND_BROADCAST, uiGlobal_MsgID_SkypeControlAPIDiscover, (WPARAM)hInit_MainWindowHandle, 0); fProcessed = true; } // 切断する if ( stricmp( acInputRow, "#disconnect") == 0 ) { hGlobal_SkypeAPIWindowHandle = NULL; printf("!!! Disconnected\n"); fProcessed = true; } // Skypeに送る if ( fProcessed == false && hGlobal_SkypeAPIWindowHandle != NULL ) { COPYDATASTRUCT oCopyData; // send command to skype oCopyData.dwData = 0; oCopyData.lpData = acInputRow; oCopyData.cbData = (DWORD)strlen(acInputRow)+1; if ( oCopyData.cbData != 1 ) { if ( SendMessage(hGlobal_SkypeAPIWindowHandle, WM_COPYDATA, (WPARAM)hInit_MainWindowHandle, (LPARAM)&oCopyData) == FALSE) { hGlobal_SkypeAPIWindowHandle = NULL; printf("!!! Disconnected\n"); } } } } } // 自身にウィンドウを閉じるメッセージを送る PostMessage( hInit_MainWindowHandle, WM_CLOSE, 0, 0); SetEvent(hGlobal_ThreadShutdownEvent); fGlobal_ThreadRunning = false; } // // エントリポイント // int main(void) { // create window class // create dummy/hidden window for processing messages // run message loop thread // do application control until exit // exit: send QUIT message to our own window // wait until thred terminates // destroy main window // destroy window class // メッセージ番号の取得(複数のアプリケーションで同じメッセージを使うための登録) uiGlobal_MsgID_SkypeControlAPIAttach = RegisterWindowMessage("SkypeControlAPIAttach"); uiGlobal_MsgID_SkypeControlAPIDiscover = RegisterWindowMessage("SkypeControlAPIDiscover"); if ( uiGlobal_MsgID_SkypeControlAPIAttach != 0 && uiGlobal_MsgID_SkypeControlAPIDiscover != 0 ) { // ウィンドウの作成 if ( !Initialize_CreateWindowClass() ) return FALSE; if ( !Initialize_CreateMainWindow() ) { DeInitialize_DestroyWindowClass(); return FALSE; } // イベントオブジェクトを作る(スレッド終了の制御に使っているらしい) hGlobal_ThreadShutdownEvent = CreateEvent( NULL, TRUE, FALSE, NULL); if ( hGlobal_ThreadShutdownEvent != NULL ) { // スレッドの作成 if ( _beginthread( &Global_InputProcessingThread, 64*1024, NULL) != (unsigned long)-1 ) { // メッセージループを回す Global_MessageLoop(); // 終了処理 Global_Console_CancelReadRow(); WaitForSingleObject( hGlobal_ThreadShutdownEvent, INFINITE); } //スレッドが終わるのを待つ CloseHandle(hGlobal_ThreadShutdownEvent); } // ウィンドウの後始末 DeInitialize_DestroyMainWindow(); DeInitialize_DestroyWindowClass(); return TRUE; } }