如何在C#中使用char *参数调用ActiveX函数

时间:2013-04-18 08:42:37

标签: c# c++ activex atl

我有这个c ++函数:

unsigned int ReadUserMemory( unsigned char *lpBuffer, unsigned int iAddress, unsigned int nNumberOfByte );

从内存的nNumberOfByte读取iAddress字节并将其放入lpBuffer
我用它创建了一个ATL对象(将在C#中使用):

[id(6), helpstring("method ReadUserMemory")] HRESULT ReadUserMemory(BYTE* lpBuffer, [in] ULONG iAddress, [in] ULONG nNumberOfByte, [out,retval] ULONG* result);  

我既不是C#程序员,也不是经验丰富的ATL程序员!现在我将使用以下代码在C#中测试此函数:

byte [] b = new byte[100];
axObj.ReadUserMemory(b, 0, 100);

但显然这段代码错了。如何调用此方法?提前谢谢。

1 个答案:

答案 0 :(得分:1)

我认为Peter R有一个非常有效的观点,因为使用COM很复杂,因为它需要学习COM以及C ++和C#,正如您所发现的那样。 C ++互操作是另一种选择,但这也需要学习第三种技术,称为C ++ / CLI,它可能比COM更合适,但仍然可能存在问题。 COM和C ++ / CLI都是非常庞大,复杂的野兽,所以你可能最好只在dll中声明你需要的C函数并使用P / Invoke,参见例如http://msdn.microsoft.com/en-us/magazine/cc164123.aspx。如果是这样,您可以在How can I pass a pointer to an array using p/invoke in C#?中找到您想要的魔力。

假设你想继续沿着COM路线行进,我就玩了它,对我而言,看起来你需要通过嵌入在COM dll中的类型库来获得一些东西。简而言之,我无法让COM互操作与unsigned char *lpBuffer一起工作,尽管也许其他人可以!要使用的类型库友好类型是SAFEARRAY,它本质上是VB喜欢看数组的方式。

在这种情况下,你的idl变为

[id(1)] HRESULT ReadUserMemory([out] SAFEARRAY(byte)* buffer, [in] ULONG iAddress, [in] ULONG nNumberOfByte, [out,retval] ULONG* result);

您的C ++实现看起来像这样

STDMETHODIMP CObj::ReadUserMemory(SAFEARRAY ** pBuffer, ULONG iAddress, ULONG nNumberOfByte, ULONG* result)
{
    if (pBuffer== nullptr)
        return E_POINTER;

    SAFEARRAY*& psa = *pBuffer;
    SAFEARRAYBOUND rgsabound[1];
    rgsabound[0].lLbound = 0;
    rgsabound[0].cElements = nNumberOfByte;
    psa = SafeArrayCreate(VT_UI1, 1, rgsabound);
    if(psa == NULL)
        return E_OUTOFMEMORY;

    void* data = nullptr;
    SafeArrayAccessData(psa, &data);
    for(int i=0; i<nNumberOfByte; ++i) {
        ((char*)data)[i] = (char)i;
    }
    SafeArrayUnaccessData(psa);

    return S_OK;
}

即使SafeArrayUnaccessData之后的代码失败,也应该调用SafeArrayAccessData

C#客户端现在看起来像这样,特别是我们已从byte[]更改为System.Array

    static void Main(string[] args)
    {
        RUMLib.IObj axObj = new RUMLib.Obj();
        Array a = null;
        axObj.ReadUserMemory(out a, 2, 6);
        for (int i = 0; i < a.Length; ++i)
        {
            Console.Write("{0},", a.GetValue(i));
        }
        Console.WriteLine();
    }

并且程序的输出是

  

0,1,2,3,4,5,

请注意,当数据从SAFEARRAY返回到C#数组时,可能会进行编组,即复制数据。这对您来说可能是非常昂贵的,在这种情况下建议您使用C ++ / CLI或P / Invoke。