0

我有这个 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);

但显然这段代码是错误的。如何调用这个方法?提前致谢。

4

1 回答 1

1

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

假设您想继续沿 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。

于 2013-04-20T18:27:17.800 回答