2013-10-31 64 views
0

首先,我對COM技術並不是很熟悉,這是我第一次使用它,因此忍受着我。 我想從C#調用一個COM對象函數。將C#結構元素編組爲C++ VARIANT

這是在IDL文件的接口:

[id(6), helpstring("vConnectInfo=ConnectInfoType")] 
HRESULT ConnectTarget([in,out] VARIANT* vConnectInfo); 

這是互操作接口I運行TLBIMP後得到:

void ConnectTarget(ref object vConnectInfo); 

在用於目標函數的COM對象C++代碼:

STDMETHODIMP PCommunication::ConnectTarget(VARIANT* vConnectInfo) 
{ 
    if (!((vConnectInfo->vt & VT_ARRAY) && (vConnectInfo->vt & VT_BYREF))) 
    { 
     return E_INVALIDARG; 
    } 

    ConnectInfoType *pConnectInfo = (ConnectInfoType *)((*vConnectInfo->pparray)->pvData); 
... 
} 

此COM對象在另一個進程中運行,它不在dll中。

我可以補充說COM對象也可以從另一個用C++編寫的程序中使用。在這種情況下,沒有問題,因爲在C++中創建VARIANT,並將pparray-> pvData設置爲connInfo數據結構,然後使用VARIANT作爲參數調用COM對象。

在C#中,據我瞭解,我的結構應自動編組爲VARIANT。

這兩種方法我已經使用(或實際上我已經嘗試了很多...)來調用C#這種方法:

private void method1_Click(object sender, EventArgs e) 
    { 
     pcom.PCom PCom = new pcom.PCom(); 
     pcom.IGeneralManagementServices mgmt = (pcom.IGeneralManagementServices)PCom; 
     m_ci = new ConnectInfoType(); 
     fillConnInfo(ref m_ci); 
     mgmt.ConnectTarget(m_ci); 
    } 

在結構被編組與上述情形VT_UNKNOWN。這是一個簡單的例子,如果參數不是一個結構(例如,爲int工作)。

private void method4_Click(object sender, EventArgs e) 
    { 
     ConnectInfoType ci = new ConnectInfoType(); 
     fillConnInfo(ref ci); 

     pcom PCom = new pcom.PCom(); 
     pcom.IGeneralManagementServices mgmt = (pcom.IGeneralManagementServices)PCom; 

     ParameterModifier[] pms = new ParameterModifier[1]; 
     ParameterModifier pm = new ParameterModifier(1); 
     pm[0] = true; 
     pms[0] = pm; 
     object[] param = new object[1]; 
     param[0] = ci; 
     object[] args = new object[1]; 
     args[0] = param; 
     mgmt.GetType().InvokeMember("ConnectTarget", BindingFlags.InvokeMethod, null, mgmt, args, pms, null, null); 
    } 

在這種情況下,它被編組爲VT_ARRAY | VT_BYREF | VT_VARIANT。問題是,當調試「目標函數」ConnectTarget時,我無法找到在SAFEARRAY結構中發送的數據(或者在內存中的任何其他地方)

我該如何處理VT_VARIANT?

關於如何獲取我的結構數據的任何想法?

更新:

的ConnectInfoType結構:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
    public class ConnectInfoType 
    { 
     public short network; 
     public short nodeNumber; 
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 51)] 
     public string connTargPassWord; 
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] 
     public string sConnectId; 
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] 
     public string sConnectPassword; 
     public EnuConnectType eConnectType; 
     public int hConnectHandle; 
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] 
     public string sAccessPassword; 
    }; 

而且在C++中對應的結構:

typedef struct ConnectInfoType 
{ 
    short network; 
    short nodeNumber; 
    char connTargPassWord[51]; 
    char sConnectId[8]; 
    char sConnectPassword[16]; 
    EnuConnectType eConnectType; 
    int hConnectHandle; 
    char sAccessPassword[8]; 
} ConnectInfoType; 
+0

您是否[搜索](https://www.google.com/search?q=c%23+COM+variant&oq=c%23+COM+variant&aqs=chrome..69i57j69i58j0l4.4373j0j7&sourceid=chrome&espv=210&es_sm= 93即= UTF-8)?看起來可能有些幫助。 – crashmstr

+0

我一直在搜索整天,幾乎所有的帖子都來自搜索。並繼續搜索,謝謝。 – rdrmntn

+0

@jortan,目前尚不清楚,你的目標是將C#中的'ConnectInfoType'數組傳遞給C++,還是隻傳遞一個'ConnectInfoType'? – Noseratio

回答

0

我發現了一個可接受的解決方案/解決方法。我將結構轉換爲字節數組,然後將接收到的數據轉換爲ConnectInfoType結構。

// C++ 
STDMETHODIMP PCommunication::ConnectTarget(VARIANT* vConnectInfo) 
{ 
    if (!((vConnectInfo->vt & VT_ARRAY) && (vConnectInfo->vt & VT_UI1))) 
    { 
     return E_INVALIDARG; 
    } 

    ConnectInfoType *pConnectInfo = (ConnectInfoType *)(vConnectInfo->parray->pvData); 
... 
} 

如果有人知道一個更好的解決方案,隨意添加它:

// C# 
    private void method3_Click(object sender, EventArgs e) 
    { 
     pcom.PCom PCom = new pcom.PCom(); 
     pcom.IGeneralManagementServices mgmt = (pcom.IGeneralManagementServices)PCom; 
     ConnectInfoType ci = new ConnectInfoType(); 
     fillConnInfo(ref ci); 

     IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(ci)); 
     Marshal.StructureToPtr(ci, ptr, false); 
     byte[] arr = new byte[Marshal.SizeOf(ci)]; 
     Marshal.Copy(ptr, arr, 0, Marshal.SizeOf(ci)); 

     mgmt.ConnectTarget(arr); 
    } 

VT_ARRAY |VT_UI1,所以我已經改變了C++代碼,這將被整理。

+0

您所做的是一個針對out-proc服務器的推薦解決方案,如我提到的MSKB文章中所述: http://support.microsoft.com/kb/122289。似乎沒有更好的方法來做到這一點,而無需顯着改變C++部分。然而,你應該在'Marshal.Copy'調用之後釋放分配的內存('Marshal.FreeHGlobal'),並且在C++端創建一個'ConnectInfoType'的副本,而不是存儲一個指向它的指針(不清楚你是否已經這麼做了)。 – Noseratio

0

使用此類http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.variantwrapper%28v=vs.110%29.aspx到wrapp它,然後調用所需的方法。

你可以發佈類型「ConnectInfoType」的C++源?

+0

如果這樣做,並在我的帖子中用「method1」調用COM函數,我會得到一個異常「VariantWrappers不能存儲在Variant中。」如果我用「method4」執行它,它將被編組爲VT_UNKNOWN。 – rdrmntn

+0

嘗試在您的c#struct枚舉類型中添加一個[MarshalAs(UnmanagedType.U4/SysUInt)]到您的c#struct枚舉類型 – user743414