X64與X32進程通訊

來源:互聯網
上載者:User
這裡是通用的調用COM組件的 一種方法(通過DISPATCH)
::CoInitialize( NULL );
CLSID clsid;// 通過 ProgID 得到 CLSID
HRESULT hr = ::CLSIDFromProgID( L"test1122.test1.1", &clsid );
hr = ::CoCreateInstance( clsid, NULL, CLSCTX_ALL, IID_IDispatch, (LPVOID *)&pDisp );
DISPID dispID;// 取得的序號,準備儲存到這裡
hr = pDisp->GetIDsOfNames(// 根據函數名,取得序號的函數
IID_NULL,&pwFunName,// 函數名稱的數組1,// 函數名稱數組中的元素個數
LOCALE_SYSTEM_DEFAULT,// 使用系統預設的語言環境
&dispID );
hr = pDisp->Invoke(// 調用函數
dispID,// 函數由 dispID 指定
IID_NULL,
LOCALE_SYSTEM_DEFAULT,// 使用系統預設的語言環境
DISPATCH_METHOD,// 調用的是方法,不是屬性
&dispParams2,// 參數
&vResult,// 傳回值
NULL,// 不考慮異常處理
NULL);

COM 組件設計與應用(一)
起源及複合檔案
檔案中的檔案
開啟記事本程式,輸入了一篇文章後,儲存。——這樣的檔案叫“非結構化檔案”;
  開啟試算表程式,輸入一個班的學生姓名和考試成績,儲存。——這樣的檔案叫“標準結構化檔案”;
複合檔案,俗稱“檔案中的檔案系統”
複合檔案的特點
1.複合檔案的內部是使用指標構造的一棵樹進行管理的。編寫程式的時候要注
意,由於使用的是單向指標,因此當做定位操作的時候,向後定位比向前定位要快;
2.複合檔案中的“流對象”,是真正儲存資料的空間。它的儲存單位為512字
節。也就是說,即使你在流中只儲存了一個位元組的資料,它也要佔據512位元組的檔案
空間。啊~~~,這也太浪費了呀?不浪費!因為檔案儲存在磁碟上,即使一個位元組也
還要佔用一個“簇”的空間那;
3.不同的進程,或同一個進程的不同線程可以同時訪問一個複合檔案的不同部
分而互不干擾;
4.大家都有這樣的體會,當需要往一個檔案中插入一個位元組的話,需要對整個
檔案進行操作,非常煩瑣並且效率低下。而複合檔案則提供了非常方便的“增量訪問
”能力;
5.當頻繁地刪除檔案,複製檔案後,磁碟空間會變的很零碎,需要使用磁碟整
理工具進行重新整合。和磁碟管理非常相似,複合檔案也會產生這個問題,在適當的
時候也需要整理,但比較簡單,只要調用一個函數就可以完成了
oid SampleCreateDoc()
{
::CoInitialize(NULL);// COM 初始化
// 如果是MFC程式,可以使用AfxOleInit()替代

HRESULT hr;// 函數執行傳回值
IStorage *pStg = NULL;// 根儲存介面指標
IStorage *pSub = NULL;// 子儲存介面指標
IStream *pStm = NULL;// 流介面指標

hr = ::StgCreateDocfile(// 建立複合檔案
L"c:\\a.stg",// 檔案名稱
STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE,// 開啟檔案
0,// 保留參數
&pStg);// 取得根儲存介面指標
ASSERT( SUCCEEDED(hr) );// 為了突出重點,簡化程式結構,所以使用了斷言。
// 在實際的程式中則要使用條件判斷和異常處理

hr = pStg->CreateStorage(// 建立子儲存
L"SubStg",// 子儲存名稱
STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE,
0,0,
&pSub);// 取得子儲存介面指標
ASSERT( SUCCEEDED(hr) );

hr = pSub->CreateStream(// 建立流
L"Stm",// 流名稱
STGM_CREATE | STGM_WRITE | STGM_SHARE_EXCLUSIVE,
0,0,
&pStm);// 取得流介面指標
ASSERT( SUCCEEDED(hr) );

hr = pStm->Write(// 向流中寫入資料
"Hello",// 資料地址
5,// 位元組長度(注意,沒有寫入字串結尾的\0)
NULL);// 不需要得到實際寫入的位元組長度
ASSERT( SUCCEEDED(hr) );

if( pStm )pStm->Release();// 釋放流指標
if( pSub )pSub->Release();// 釋放子儲存指標
if( pStg )pStg->Release();// 釋放根儲存指標

::CoUninitialize()// COM 釋放
// 如果使用 AfxOleInit(),則不調用該函數
}

圖二、運行樣本程式一後,使用 DFView.exe 開啟觀察複合檔案的

樣本二:開啟一個複合檔案,枚舉其根儲存下的所有對象。
#include // ANSI、MBCS、UNICODE 轉換

void SampleEnum()
{// 假設你已經做過 COM 初始化了

LPCTSTR lpFileName = _T( "c:\\a.stg" );
HRESULT hr;
IStorage *pStg = NULL;

USES_CONVERSION;// (注6)
LPCOLESTR lpwFileName = T2COLE( lpFileName );// 轉換T類型為寬字元
hr = ::StgIsStorageFile( lpwFileName );// 是複合檔案嗎?
if( FAILED(hr) )return;

hr = ::StgOpenStorage(// 開啟複合檔案
lpwFileName,// 檔案名稱
NULL,
STGM_READ | STGM_SHARE_DENY_WRITE,
0,
0,
&pStg);// 得到根儲存介面指標

IEnumSTATSTG *pEnum=NULL;// 列舉程式
hr = pStg->EnumElements( 0, NULL, 0, &pEnum );
ASSERT( SUCCEEDED(hr) );

STATSTG statstg;
while( NOERROR == pEnum->Next( 1, &statstg, NULL) )
{
// statstg.type 儲存著物件類型 STGTY_STREAM 或 STGTY_STORAGE
// statstg.pwcsName 儲存著對象名稱
// ...... 還有時間,長度等很多資訊。請查看 MSDN

::CoTaskMemFree( statstg.pwcsName );// 釋放名稱所使用的記憶體(注6)
}

if( pEnum )pEnum->Release();
if( pStg )pStg->Release();
}
複合檔案,結構化儲存,是微軟組件思想的起源,在此基礎上繼續發展出了持久性、命名、ActiveX、對象嵌入、現場啟用......一系列的新技術、新概念

COM組件設計與應用(二)
GUID 和 介面
CLSID
在對象資料的前面,儲存有處理這個資料的程式名。CLSID(注1)的方式間接描述這些對象資料的處理常式路徑。CLSID 其實就是一個號碼,或者說是一個16位元組的數。
ProgID 概念
字串名稱方式,叫 ProgID,LSID 和 ProgID 其實是一個概念的兩個不同的表示形式,所以我們在程式中可以隨便使用任何一種
六、介面(Interface)概念
1、函數是通過 VTAB 虛函數表提供其地址, 從另一個角度來看,不管用什麼語言開發,編譯器產生的代碼都能產生這個表。這樣就實現了組件的“二進位特性”輕鬆實現了組件的跨語言要求。
2、假設有一個指標型變數儲存著 VTAB 的首地址,則這個變數就叫“介面指標”(注6), 變數命名的時候,習慣上加上"I"開頭。另外為了區分不同的介面,每個介面 也都要有一個名字,該名字就和 CLSID 一樣,使用 GUID 方式,叫 IID。
3、介面一經發表,就不能再修改了。不然就會出現向前相容的問題。這個性質叫“介面不變性”。
4、組件中必須有3個函數,QueryInterface、AddRef、Release,它們3個函數也組成一個介面,叫"IUnknown"。(注7)
5、任何介面,其實都包含了 IUnknown 介面。隨著你接觸到更多的介面就會了更體會解到介面的另一個性質“繼承性”。
6、在任何介面上,調用表中的第一個函數,其實就是調用 QueryInterface()函數,就得到你想要的另外一個介面指標。這個性質叫“介面的傳遞性”
7、C/C++語言中需要事先對函式宣告,那麼就 會要求組件也必須提供C語言的標頭檔。不行!為了能使COM具有跨語言的能力,決定不再為任何語言提供對應的函數介面聲明,而是獨立地提供一個叫類型庫(TLB)的聲明。每個語言的IDE環境自己去根據TLB產生自己語言需要的封裝。這個性質叫“介面聲明的獨立性”(注8)
七、客戶程式與組件之間的協商調用
回到我們的上一個話題,Word中嵌入一個組件,那麼Word是如何協商使用這個組件的那?下面是容器和組件之間的一個類比對話過程:
 
 容器 協商部分組件 應答部分
1根據CLSID啟動組件 。
CoCreateInstance()產生對象,執行建構函式,執行初始化動作。
2你有IUnknown介面嗎?有,給你!
3恩,太好了,那麼你有IPersistStorage介面嗎?(注9)
IUnknown::QueryInterface(IID_IPersistStorage...)沒有!
4真差勁,連這個都沒有。那你有IPersistStreamInit介面嗎?(注10)
IUnknown::QueryInterface(IID_IPersistStreamInit...)哈,這個有,給!
5好,好,這還差不多。你現在給我初始化吧。
IPersistStreamInit::InitNew()OK,初始化完成了。
6完成了?好!現在你讀資料去吧。
IPersistStreamInit::Load()讀完啦。我根據資料,已經在視窗中顯示出來了。
7好,現在咱們各自處理使用者的滑鼠、鍵盤訊息吧............
8哎呀!使用者要儲存退出程式了。你的資料被使用者修改了嗎?
IPersistStreamInit::IsDirty()改了,使用者已經修改啦。
9那好,那麼使用者修改後,你的資料需要多大的儲存空間呀?
IPersistStreamInit::GetSizeMax()恩,我算算呀......好了,總共需要500KB。
10暈,你這麼個小玩意居然佔用這麼大空間?!......好了,你可以存了。
IPersistStreamInit::Save()謝謝,我已經存好了。
11恩。拜拜了您那。(注11)
IPersistStreamInit::Release();IUnknown::Release()執行解構函式,刪除對象。
12我自己也該退出了......
PostQuitMessage()

COM組件設計與應用(三)
資料類型
則COM系統經過判斷,會返回相應的錯誤值。常見的傳回值有:
HRESULT值含義
S_OK0x00000000成功
S_FALSE0x00000001函數成功執行完成,但返回時出現錯誤
E_INVALIDARG0x80070057參數有錯誤
E_OUTOFMEMORY0x8007000E記憶體申請錯誤
E_UNEXPECTED0x8000FFFF未知的異常
E_NOTIMPL0x80004001未實現功能
E_FAIL0x80004005沒有詳細說明的錯誤。一般需要取得 Rich Error 錯誤資訊(注1)
E_POINTER0x80004003無效的指標
E_HANDLE0x80070006無效的控制代碼
E_ABORT0x80004004終止操作
E_ACCESSDENIED0x80070005訪問被拒絕
E_NOINTERFACE0x80004002不支援介面

圖二、BSTR 記憶體結構
  BSTR 是一個指向 UNICODE 字串的指標,且 BSTR 向前的4個位元組中,使用DWORD儲存著這個字串的位元組長度( 沒有含字串的結束符)。因此系統就能夠正確處理並傳送這個字串到“地球另一 邊”了。特別需要注意的是,由於BSTR的指標就是指向 UNICODE 串,因此 BSTR 和 LPOLESTR 可以在一定程度上混用,但一定要注意:
  有函數 fun(LPCOLESTR lp),則你調用 BSTR p=...; fun(p); 正確
  有函數 fun(const BSTR bstr),則你調用 LPCOLESTR p=...; fun(p); 錯誤!!!
有關 BSTR 的處理函數:
六、VARIANT
COM 產生目的,其中之一就是要跨語言(注3)。而 VARIANT 資料類型就具有跨語言的特性,同時它可以表示(儲存)任意類型的資料。從C語言的角度來講,VARIANT 其實是一個結構,結構中用一個域(vt)表示------該變數到底表示的是什麼類型資料,同時真正的資料則存貯在 union 空間中
COM組件設計與應用(四)
簡單調用組件
1、啟動組件得到一個介面指標(Interface)後,不要調用AddRef()。因為系統知道你得到了一個指標,所以它已經幫你調用了AddRef()函數;
  2、通過QueryInterface()得到另一個介面指標後,不要調用AddRef()。因為......和上面的道理一樣;
  3、當你把介面指標賦值給(儲存到)另一個變數中的時候,請調用AddRef();
  4、當不需要再使用介面指標的時候,務必執行Release()釋放;
  5、當使用智能指標的時候,可以省略指標的維護工作;(注1)
 C語言C++語言Windows 平台COMIMalloc 介面BSTR
申請malloc()newGlobalAlloc()CoTaskMemAlloc()Alloc()SysAllocString()
重新申請realloc() GlobalReAlloc()CoTaskRealloc()Realloc()SysReAllocString()
釋放free()deleteGlobalFree()CoTaskMemFree()Free()SysFreeString()
以上這些函數必須要按類型配合使用(比如:new 申請的記憶體,則必須用 delete 釋放)。在 COM 內部,當然你可以隨便使用任何類型的記憶體配置釋放函數,但組件如果需要與客戶進行記憶體的互動,則必須使用上表中的後三類函數族。
  1、BSTR 記憶體在上回書中,已經有比較豐富的介紹了,不再重複;
  2、CoTaskXXX()函數族,其本質上就是調用C語言的函數(malloc...);
  3、IMalloc 介面又是對 CoTaskXXX() 函數族的一個封裝。封裝後,同時增強了一些功能,比如:IMalloc::GetSize()可以取得尺寸,使用 IMallocSpy 可以監視記憶體的使
四、參數傳遞方向
  在C語言的函式宣告中,尤其當參數為指標的時候,你是看不出它傳遞方向的。比如:
void fun(char * p1, int * p2); 請問,p1、p2 哪個是入參?哪個是出參?甚或都是入參或都是出參?由於牽扯到記憶體配置和釋放等問題,COM 需要明確標註參數方向。以後我們寫程式,就類似下面的樣子:
HRESULT Add([in] long n1, [in] long n2, [out] long *pnSum); // IDL檔案(注2) STDMETHOD(Add)(/*[in]*/ long n1, /*[in]*/ long n2, /*[out]*/ long *pnSum); // .h檔案
如果參數是動態分配的記憶體指標,那麼遵守如下的規定:
方向申請人釋放人提示
[in]調用者調用者組件接收指標後,不能重新分配記憶體
[out]組件調用者組件返回指標後,調用者“愛咋咋地”(注3)
[in,out]調用者調用者組件可以重新分配記憶體

COM 組件設計與應用(七)
編譯、註冊、調用

 說明1:編譯後,類型庫以 TLB 檔案形式單獨存在,同時也儲存在目標檔案的資源中。因此,我們將來在 #import 引入類型庫的時候,既可以指定 TLB 檔案,也可以指定目標檔案;
  說明2:我們作為 C/C++ 的程式員,還算是比較幸福的。因為 IDL 編譯後,特意為我們提供了 C 語言形式的介面檔案。
  說明3:IDL 編譯後組建代理程式/存根來源程式,有:dlldata.c、xxx_p.c、xxxps.def、xxxps.mak,我們可以用 NMAKE.EXE 再次編譯來產生真正的代理/存根DLL目標檔案(注1)。
四、關於組件調用
  總的來說,調用組件程式大概有如下方法:
#include 方法IDL編譯後,為方便C/C++程式員的使用,會產生xxx.h和xxx_i.c檔案。我們真幸福,直接#include後就可以使用了
#import 方法比較通用的方法,vc 會幫我們產生封裝類,讓我們的調用更方便
載入類型庫封裝類 方法如果組件提供了 IDispatch 介面,用這個方法調用組件是最簡單的啦。不過還沒講IDispatch,只能看以後的文章啦
載入ActiveX封裝類 方法ActiveX 還沒介紹呢,以後再說啦
COM組件設計與應用(九)
IDispatch 介面 for vc6.0
IDispatch介面
如果是編譯型語言,那麼我們可以讓編譯器在編譯的時候裝載類型庫,也就是裝載介面的描述。在第七迴文章當中,我們分別使用了 #include 方法和 #import 方法來實現的。裝載了類型庫後,編譯器就知道應該如何編譯介面函數的調用了---這叫“前綁定”。但是,如果想在指令碼語言中使用組件,問題就大了,因為指令碼語言是解釋執行的,它執行的時候不會知道具體的函數地址,怎麼辦?自動化介面就為此誕生了---“後綁定”。
自動化組件,其實就是實現了 IDispatch 介面的組件。IDispatch 介面有4個函數,解釋語言的執行器就通過這僅有的4個函數來動作項目所提供的功能。IDispatch 介面用 IDL 形式說明如下:(注1)
[
object,
uuid(00020400-0000-0000-C000-000000000046),// IDispatch 介面的 IID = IID_IDispatch
pointer_default(unique)
]

interface IDispatch : IUnknown
{
typedef [unique] IDispatch * LPDISPATCH;// 轉定義 IDispatch * 為 LPDISPATCH

HRESULT GetTypeInfoCount([out] UINT * pctinfo);// 有關類型庫的這兩個函數,咱們以後再說
HRESULT GetTypeInfo([in] UINT iTInfo,[in] LCID lcid,[out] ITypeInfo ** ppTInfo);

HRESULT GetIDsOfNames(// 根據函數名字,取得函數序號(DISPID)
[in] REFIID riid,
[in, size_is(cNames)] LPOLESTR * rgszNames,
[in] UINT cNames,
[in] LCID lcid,
[out, size_is(cNames)] DISPID * rgDispId
);

[local]// 本地版函數
HRESULT Invoke(// 根據函數序號,解釋執行函數功能
[in] DISPID dispIdMember,
[in] REFIID riid,
[in] LCID lcid,
[in] WORD wFlags,
[in, out] DISPPARAMS * pDispParams,
[out] VARIANT * pVarResult,
[out] EXCEPINFO * pExcepInfo,
[out] UINT * puArgErr
);

[call_as(Invoke)]// 遠程版函數
HRESULT RemoteInvoke(
[in] DISPID dispIdMember,
[in] REFIID riid,
[in] LCID lcid,
[in] DWORD dwFlags,
[in] DISPPARAMS * pDispParams,
[out] VARIANT * pVarResult,
[out] EXCEPINFO * pExcepInfo,
[out] UINT * pArgErr,
[in] UINT cVarRef,
[in, size_is(cVarRef)] UINT * rgVarRefIdx,
[in, out, size_is(cVarRef)] VARIANTARG * rgVarRef
);
}
以上 IDispatch 介面函數的講解,我們留到後回中進行介紹。如何在組件程式中實現這些函數那?還好,還好,就象 IUnknown 一樣,MFC 和 ATL 都幫我們已經完成了
COM 組件設計與應用(十一)
IDispatch 及雙介面的調用
樣本一、IDispatch 調用原理篇
void demo()
{
::CoInitialize( NULL );// COM 初始化

CLSID clsid;// 通過 ProgID 得到 CLSID
HRESULT hr = ::CLSIDFromProgID( L"Simple8.DispSimple.1", &clsid );
ASSERT( SUCCEEDED( hr ) );// 如果失敗,說明沒有註冊組件

IDispatch * pDisp = NULL;// 由 CLSID 啟動組件,並得到 IDispatch 指標
hr = ::CoCreateInstance( clsid, NULL, CLSCTX_ALL, IID_IDispatch, (LPVOID *)&pDisp );
ASSERT( SUCCEEDED( hr ) );// 如果失敗,說明沒有初始化 COM

LPOLESTR pwFunName = L"Add";// 準備取得 Add 函數的序號 DispID
DISPID dispID;// 取得的序號,準備儲存到這裡
hr = pDisp->GetIDsOfNames(// 根據函數名,取得序號的函數
IID_NULL,
&pwFunName,// 函數名稱的數組
1,// 函數名稱數組中的元素個數
LOCALE_SYSTEM_DEFAULT,// 使用系統預設的語言環境
&dispID );// 傳回值
ASSERT( SUCCEEDED( hr ) );// 如果失敗,說明組件根本就沒有 ADD 函數

VARIANTARG v[2];// 調用 Add(1,2) 函數所需要的參數
v[0].vt = VT_I4;v[0].lVal = 2;// 第二個參數,整數2
v[1].vt = VT_I4;v[1].lVal = 1;// 第一個參數,整數1

DISPPARAMS dispParams = { v, NULL, 2, 0 };// 把參數封裝在這個結構中
VARIANT vResult;// 函數返回的計算結果

hr = pDisp->Invoke(// 調用函數
dispID,// 函數由 dispID 指定
IID_NULL,
LOCALE_SYSTEM_DEFAULT,// 使用系統預設的語言環境
DISPATCH_METHOD,// 調用的是方法,不是屬性
&dispParams,// 參數
&vResult,// 傳回值
NULL,// 不考慮異常處理
NULL);// 不考慮錯誤處理
ASSERT( SUCCEEDED( hr ) );// 如果失敗,說明參數傳遞錯誤

CString str;// 顯示一下結果
str.Format("1 + 2 = %d", vResult.lVal );
AfxMessageBox( str );

pDisp->Release();// 釋放介面指標
::CoUninitialize();// 釋放 COM
}
樣本二、CComDispatchDriver 智能指標封裝類的使用方法
void demo()
{
// 已經進行過了 COM 初始化

CLSID clsid;// 通過 ProgID 取得組件的 CLSID
HRESULT hr = ::CLSIDFromProgID( L"Simple8.DispSimple.1", &clsid );
ASSERT( SUCCEEDED( hr ) );// 如果失敗,說明沒有註冊組件

CComPtr spUnk;// 由 CLSID 啟動組件,並取得 IUnknown 指標
hr = ::CoCreateInstance( clsid, NULL, CLSCTX_ALL, IID_IUnknown, (LPVOID *)&spUnk );
ASSERT( SUCCEEDED( hr ) );

CComDispatchDriver spDisp( spUnk );// 構造只能指標
CComVariant v1(1), v2(2), vResult;// 參數
hr = spDisp.Invoke2(// 調用2個參數的函數
L"Add",// 函數名是 Add
&v1,// 第一個參數,值為整數1
&v2,// 第二個參數,值為整數2
&vResult);// 傳回值
ASSERT( SUCCEEDED( hr ) );// 如果失敗,說明或者沒有 ADD 函數,或者參數錯誤

CString str;// 顯示一下結果
str.Format("1 + 2 = %d", vResult.lVal );
AfxMessageBox( str );
}
COM組件設計與應用(十五)
連接點(vc6.0)
表4-3 VARIANT支援的類型
類型名含義
VT_EMPTY指示未指定值
VT_NULL指示空值(類似於 SQL 中的空值)
VT_I2指示 short 整數
VT_I4指示 long 整數
VT_R4指示 float 值
VT_R8指示 double 值
VT_CY指示貨幣值
VT_DATE指示 DATE 值
VT_BSTR指示 BSTR 字串
VT_DISPATCH指示 IDispatch 指標
VT_ERROR指示 SCODE
VT_BOOL指示一個布爾值
VT_VARIANT指示 VARIANTfar 指標
VT_UNKNOWN指示 IUnknown 指標
VT_DECIMAL指示 decimal 值
VT_I1指示 char 值
 (續表)
  
類型名含義
VT_UI1指示 byte
VT_UI2指示 unsignedshort
VT_UI4指示 unsignedlong
VT_I8指示 64 位元整數
VT_UI8指示 64 位元不帶正負號的整數
VT_INT指示整數值
VT_UINT指示 unsigned 整數值
VT_VOID指示 C 樣式 void
VT_HRESULT指示 HRESULT
VT_PTR指示指標類型
VT_SAFEARRAY指示 SAFEARRAY
VT_CARRAY指示 C 樣式數組
VT_USERDEFINED指示使用者定義的類型
VT_LPSTR指示一個以 NULL 結尾的字串
VT_LPWSTR指示由 nullNothingnullptrnull
  引用(在 Visual Basic
  中為 Nothing) 終止的寬字元串
VT_RECORD指示使用者定義的類型
VT_FILETIME指示 FILETIME 值
VT_BLOB指示以長度為首碼的位元組
VT_STREAM指示隨後是流的名稱
VT_STORAGE指示隨後是儲存的名稱
VT_STREAMED_OBJECT指示流包含對象
VT_STORED_OBJECT指示儲存包含對象
VT_BLOB_OBJECT指示 Blob 包含對象
VT_CF指示剪貼簿格式
VT_CLSID指示類別識別碼
VT_VECTOR指示簡單的已計數數組
VT_ARRAY指示 SAFEARRAY 指標
VT_BYREF指示值為引

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.