• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 60, 文章 - 0, 評論 - 197, 引用 - 0
            數據加載中……

            Win32 RPC 編程(一)

            我們從一個簡單的 RPC “Hello, world!”的例子開始。
            參考資料:MSDN: Win32 and COM Development -> Networking -> Network Protocols -> Remote Procedure Calls (RPC)


            第1步:編寫 IDL(Interface Description Language,接口描述語言)文件
            -------------------------------------------------------------------------
            IDL 是一個通用的工業標準語言,大家應該不陌生,因為 COM 里面也是用它來描述接口的。
            Hello.idl:

            [
                 uuid("4556509F-618A-46CF-AB3D-ED736ED66477"),   // 唯一的UUID,用 GUIDGen 生成
                 version(1.0)
            ]

            interface HelloWorld 
            {
                 // 我們定義的方法
                 void Hello([in,string]const char * psz);
                 void Shutdown(void); 
            }


            一個可選的文件是應用程序配置文件(.acf),它的作用是對 RPC 接口進行配置,例如下面的 Hello.acf 文件:
            Hello.acf:


                 implicit_handle(handle_t    HelloWorld_Binding) 


            interface HelloWorld
            {

            }

            上面定義了 implicit_handle,這樣客戶端將綁定句柄 HelloWorld_Binding 了,后面的客戶端代碼中我們會看到。


            編譯 IDL 文件:
            >midl Hello.idl
            Microsoft (R) 32b/64b MIDL Compiler Version 6.00.0366
            Copyright (c) Microsoft Corporation 1991-2002. All rights reserved.
            Processing .\Hello.idl
            Hello.idl
            Processing .\Hello.acf
            Hello.acf

             
            我們可以看到自動生成了 Hello.h, Hello_s.c, Hello_c.c 文件,這些叫做 rpc stub 程序,不過我們可以不管這個概念,
            我們只需要知道 Hello.h 里面定義了一個

            extern RPC_IF_HANDLE HelloWorld_v1_0_s_ifspec;

            這個 RPC_IF_HANDLE 將在后面用到。

             
            第2步:編寫服務端程序
            -------------------------------------------------------------------------
            第1步中我們已經約定了調用的接口,那么現在我們開始實現其服務端。代碼如下:
            server.c

            #include <stdlib.h>
            #include <stdio.h>
            #include "Hello.h"     // 引用MIDL 生成的頭文件

            /**
             * 這是我們在IDL 中定義的接口方法
             * 需要注意一點,IDL 里面的聲明是:void Hello([in,string]const char * psz);
             * 但是這里變成了const unsigned char *,為什么呢?
             * 參見MSDN 中的MIDL Command-Line Reference -> /char Switch
             * 默認的編譯選項,對 IDL 中的char 按照unsigned char 處理
             */

            void Hello(const unsigned char * psz)
            {
                 printf("%s\n", psz);
            }

             
            /** 這也是我們在IDL 中定義的接口方法,提供關閉server 的機制*/
            void Shutdown(void)
            {
                 // 下面的操作將導致 RpcServerListen() 退出
                 RpcMgmtStopServerListening(NULL);
                 RpcServerUnregisterIf(NULL, NULL, FALSE);
            }

            int main(int argc,char * argv[])
            {
                 // 用Named Pipe 作為RPC 的通道,這樣EndPoint 參數就是Named Pipe 的名字
                 // 按照Named Pipe 的命名規范,\pipe\pipename,其中pipename 可以是除了\
                 // 之外的任意字符,那么這里用一個GUID 串來命名,可以保證不會重復
                 RpcServerUseProtseqEp((unsigned char *)"ncacn_np", 20, (unsigned char *)"\\pipe\\{8dd50205-3108-498f-96e8-dbc4ec074cf9}", NULL);   

                 // 注冊接口,HelloWorld_v1_0_s_ifspec 是在MIDL 生成的Hello.h 中定義的
                 RpcServerRegisterIf(HelloWorld_v1_0_s_ifspec, NULL, NULL);
               
                 // 開始監聽,本函數將一直阻塞
                 RpcServerListen(1,20,FALSE);
                 return 0;
            }

            // 下面的函數是為了滿足鏈接需要而寫的,沒有的話會出現鏈接錯誤
            void __RPC_FAR* __RPC_USER midl_user_allocate(size_t len)
            {
                 return(malloc(len));
            }

            void __RPC_USER midl_user_free(void __RPC_FAR *ptr)
            {
                 free(ptr);
            }

             

            編譯:
            >cl /D_WIN32_WINNT=0x500 server.c Hello_s.c rpcrt4.lib
            用于 80x86 的 Microsoft (R) 32 位 C/C++ 優化編譯器 14.00.50727.42 版
            版權所有(C) Microsoft Corporation。保留所有權利。

            server.c
            Hello_s.c
            正在生成代碼...
            Microsoft (R) Incremental Linker Version 8.00.50727.42
            Copyright (C) Microsoft Corporation.  All rights reserved.

            /out:server.exe
            server.obj
            Hello_s.obj
            rpcrt4.lib

            編譯時為什么要指定 _WIN32_WINNT=0x500 呢?因為如果沒有的話會報告下面的錯誤:
            Hello_s.c(88) : fatal error C1189: #error :  You need a Windows 2000 or later to
            run this stub because it uses these features:

             
            第3步:編寫客戶端程序
            -------------------------------------------------------------------------
            客戶端的代碼:
            client.c

            #include <stdlib.h>
            #include <stdio.h>
            #include <string.h>
            #include "Hello.h"     // 引用MIDL 生成的頭文件

            int main(int argc, char * argv[])
            {
                 unsigned char * pszStringBinding = NULL;
                 if ( argc != 2 )
                 {
                     printf("Usage:%s <Hello Text>\n", argv[0]);
                     return 1;
                 }   

                 // 用Named Pipe 作為RPC 的通道。參見server.c 中的RpcServerUseProtseqEp() 部分
                 // 第3 個參數NetworkAddr 如果取NULL,那么就是連接本機服務
                 // 否則要取\\\\servername 這樣的格式,例如你的計算機名為jack,那么就是\\jack
                 RpcStringBindingCompose( NULL, (unsigned char*)"ncacn_np", /*(unsigned char*)"\\\\servername"*/ NULL, (unsigned char*)"\\pipe\\{8dd50205-3108-498f-96e8-dbc4ec074cf9}", NULL, &pszStringBinding );

                 // 綁定接口,這里要和 Hello.acf 的配置一致,那么就是HelloWorld_Binding
                 RpcBindingFromStringBinding(pszStringBinding, & HelloWorld_Binding );   

                 // 下面是調用服務端的函數了
                 RpcTryExcept
                 {
                     if ( _stricmp(argv[1], "SHUTDOWN") == 0 )
                     {
                          Shutdown();
                     }
                     else
                     {
                          Hello((unsigned char*)argv[1]);
                     }
                 }
                 RpcExcept(1)
                 {
                     printf( "RPC Exception %d\n", RpcExceptionCode() );
                 }
                 RpcEndExcept

             
                 // 釋放資源
                 RpcStringFree(&pszStringBinding);
                 RpcBindingFree(&HelloWorld_Binding);
                 return 0;
            }

             
            // 下面的函數是為了滿足鏈接需要而寫的,沒有的話會出現鏈接錯誤
            void __RPC_FAR* __RPC_USER midl_user_allocate(size_t len)
            {
                 return(malloc(len));
            }

            void __RPC_USER midl_user_free(void __RPC_FAR *ptr)
            {
                 free(ptr);
            }

             

            編譯:
            >cl /D_WIN32_WINNT=0x500 client.c Hello_c.c rpcrt4.lib
            用于 80x86 的 Microsoft (R) 32 位 C/C++ 優化編譯器 14.00.50727.42 版
            版權所有(C) Microsoft Corporation。保留所有權利。

            client.c
            Hello_c.c
            正在生成代碼...
            Microsoft (R) Incremental Linker Version 8.00.50727.42
            Copyright (C) Microsoft Corporation.  All rights reserved.

            /out:client.exe
            client.obj
            Hello_c.obj
            rpcrt4.lib

             

            第4步:測試:
            -------------------------------------------------------------------------
            運行 server.exe,將彈出一個 console 窗口,等待客戶端調用。
            運行客戶端 client.exe:

            >client hello
            可以看到 server.exe 的 console 窗口出現 hello 的字符串。


            >client shutdown
            server.exe 退出。

            示例下載
             

            posted on 2008-04-28 18:50 Normandy 閱讀(23001) 評論(16)  編輯 收藏 引用 所屬分類: Networking

            評論

            # re: Win32 RPC 編程(一)  回復  更多評論   

            你好,你的Win32 RPC 編程(一)讓我對Windows RPC編程有了一個很形象地認識,非常感謝。不過我在用你的代碼做實驗時發現編譯有些問題,之今不知道為什么。還望賜教。

            我用2005新建了一個c++ Console Application,然后按照你的順序,依次新建了Hello.idl和Hello.acf文件,編譯后,的確看到生成出來的Hello_c.c,Hello_h.h和Hello_s.c,但在Solution Explorer中看不到這些生成出來的文件。接著我復制完服務端程序后編譯后,出現下列錯誤:
            1>RPCServer.obj : error LNK2019: unresolved external symbol __imp__RpcServerUnregisterIf@12 referenced in function _Shutdown
            1>RPCServer.obj : error LNK2019: unresolved external symbol __imp__RpcMgmtStopServerListening@4 referenced in function _Shutdown
            1>RPCServer.obj : error LNK2019: unresolved external symbol __imp__RpcServerListen@12 referenced in function _wmain
            1>RPCServer.obj : error LNK2019: unresolved external symbol __imp__RpcServerRegisterIf@12 referenced in function _wmain
            1>RPCServer.obj : error LNK2001: unresolved external symbol _HelloWorld_v1_0_s_ifspec
            1>RPCServer.obj : error LNK2019: unresolved external symbol __imp__RpcServerUseProtseqEpW@16 referenced in function _wmain

            如果我將那幾個生成出來的文件手動加入到Solution Explore中后,編譯后報下列錯誤:

            1>Hello_s.c
            1>c:\zhongwei\rpc\rpcserver\rpcserver\hello_s.c(226) : fatal error C1010: unexpected end of file while looking for precompiled header. Did you forget to add '#include "stdafx.h"' to your source?
            1>Hello_c.c
            1>c:\zhongwei\rpc\rpcserver\rpcserver\hello_c.c(225) : fatal error C1010: unexpected end of file while looking for precompiled header. Did you forget to add '#include "stdafx.h"' to your source?

            你能知道是什么原因造成的么?
            2008-10-19 13:56 | 金中偉

            # re: Win32 RPC 編程(一)  回復  更多評論   

            我的EMAIL:juniorzhong@gmail.com
            MSN:junior_zhong@tom.com

            期待你的指點,謝謝!
            2008-10-19 14:08 | 金中偉

            # re: Win32 RPC 編程(一)  回復  更多評論   

            @金中偉
            其實沒有這么復雜,我是通過 Makefile 編譯的, 沒有用 IDE。你打開 Visual Studio 2005 Command Prompt 或 Visual Studio 2008 Command Prompt ,然后進到源碼目錄下 敲一個 nmake 命令, 所有的都會為你自動生成。細節可查看源碼目錄下的 Makefile 文件。
            2008-10-20 10:09 | Normandy

            # re: Win32 RPC 編程(一)  回復  更多評論   

            @金中偉
            你出錯的原因貌似是有些函數庫沒有添加到引用中,所以編譯時有無法解析的標志錯誤。或者跟工程編碼有關,自己察看下吧
            2009-08-03 18:17 | qb

            # re: Win32 RPC 編程(一)  回復  更多評論   

            你的問題是沒有添加RPC Runtime Library,你可以在工程屬性的'連接器'下的輸入 添加'附加依賴項' Rpcrt4.lib,或者在你的CPP文件開頭添加如下語句
            #pragma comment(lib, "Rpcrt4.lib")
            2009-08-24 23:15 | Quincy, Hu

            # re: Win32 RPC 編程(一)  回復  更多評論   

            RPC 學好了,大有用處。
            2009-10-14 21:49 | jc_ontheroad

            # re: Win32 RPC 編程(一)  回復  更多評論   

            問下大家,在windows下編譯器cl.exe 用/I來說明頭文件的路徑,用什么參數來說明庫文件的路徑啊?
            2011-04-27 08:03 | liweihua

            # re: Win32 RPC 編程(一)  回復  更多評論   

            @liweihua
            /LIBPATH:"X:\xxx\xxx"
            2011-04-27 09:28 | 溪流

            # re: Win32 RPC 編程(一)  回復  更多評論   

            windows下有沒有像RPCGEN這樣的工具啊?
            2011-08-24 16:38 | tal

            # re: Win32 RPC 編程(一)  回復  更多評論   

            呵呵,樓主,感謝你的文章啊。
            我也喜歡用命令行編譯程序。呵呵
            2012-05-25 15:16 | 憑凡

            # re: Win32 RPC 編程(一)  回復  更多評論   

            我在2010編譯的。結果是1714結果,
            服務器起不來
            2012-09-03 14:23 | 董香升

            # re: Win32 RPC 編程(一)  回復  更多評論   

            我把程序改成相應的wchar_t的unicode版本。可是server.cpp中RpcServerUseProtseqEp返回1703號錯誤。找不到愿意,希望指點一下。我在開頭#define UNICODE了。
            2012-09-04 11:31 |

            # re: Win32 RPC 編程(一)  回復  更多評論   

            我把程序改成相應的wchar_t的unicode版本。可是server.cpp中RpcServerUseProtseqEp返回1703號錯誤。找不到愿意,希望指點一下。我在開頭#define UNICODE了。
            2012-09-04 11:32 | 董香升

            # re: Win32 RPC 編程(一)  回復  更多評論   

            請教原因和解決方法:
            網上獲取RPC實例,比如http://blog.163.com/junior_zhong/blog/static/27871180200891810561138/
            在main()中的:
            status = RpcServerUseProtseqEp(
            reinterpret_cast <unsigned char*>("ncacn_np"),
            nMaxCalls,
            reinterpret_cast <unsigned char*>("//pipe//{a5194558-21a6-4978-9610-2072fcf1dc6e}"),
            NULL );

            編譯時出現錯誤:
            error C2664: 'RpcServerUseProtseqEpW' : cannot convert parameter 1 from 'unsigned char *' to 'RPC_WSTR'
            按照網上方法,reinterpret_cast <unsigned char*>可以解決兩個字符串問題,但是不能搞定最后一個參數NULL一直報錯C2664。
            2012-09-06 14:34 | iHyy

            # re: Win32 RPC 編程(一)  回復  更多評論   

            @董香升
            請問后來怎么解決的啊?
            2015-07-13 15:58 | 范范

            # re: Win32 RPC 編程(一)  回復  更多評論   

            如果你沒有使用Makefile編譯而是采用vs的屬性中配置來編譯,server.cpp中RpcServerUseProtseqEp返回1703號錯誤, 或是RpcServerListen報錯。請在屬性頁->常規->字符集中選擇未設置,也就是disable UNICODE
            2016-02-17 13:55 | zhang_luo@qq.com
            久久国产精品久久久| 99久久精品国产一区二区蜜芽| 青青草国产成人久久91网| 精品国产乱码久久久久久浪潮| 久久精品无码一区二区三区免费| 久久人人爽人人澡人人高潮AV | 超级碰碰碰碰97久久久久| 久久精品国产亚洲AV影院| 久久精品夜夜夜夜夜久久| 观看 国产综合久久久久鬼色 欧美 亚洲 一区二区 | 日韩亚洲欧美久久久www综合网| 久久久久九国产精品| 久久大香香蕉国产| 久久久久国产精品麻豆AR影院| 一本色综合网久久| 久久免费视频一区| 91精品久久久久久无码| 日韩乱码人妻无码中文字幕久久 | 人妻少妇久久中文字幕一区二区| 国产成人精品久久综合| 久久久久人妻一区精品色| 午夜精品久久久内射近拍高清 | 色欲综合久久躁天天躁| 精品久久一区二区| 亚洲国产精品久久电影欧美| 欧美日韩成人精品久久久免费看 | 精品久久无码中文字幕| 亚洲女久久久噜噜噜熟女| 亚洲伊人久久成综合人影院| 久久99久久无码毛片一区二区| 久久久久久免费一区二区三区| 亚洲AV无码久久精品色欲| 亚洲午夜无码久久久久小说| 人妻无码久久精品| 国产精品欧美久久久久无广告| 久久精品国产亚洲一区二区| 久久99精品久久久久久久久久| 久久中文骚妇内射| 大伊人青草狠狠久久| 狠狠色婷婷综合天天久久丁香 | 久久精品视频免费|