【问题标题】:How to call an ActiveX function with char * parameter in C#如何在 C# 中调用带有 char * 参数的 ActiveX 函数
【发布时间】:2013-04-11 07:01:52
【问题描述】:

我有这个 c++ 函数:

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

它从内存的iAddress 中读取nNumberOfByte 字节并将其放入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);

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

【问题讨论】:

  • 您是否有理由使用 COM 而不是 C++ 互操作。后者实际上要容易得多。
  • @PeterR ~> 只是因为我老板想要!他想在其他语言中毫无问题地使用这个对象!
  • 您还应该考虑使用Marshal.Copy 方法(IntPtr, Byte[], Int32, Int32)。它内置在 .NET 中,可以执行您的功能似乎可以执行的操作。
  • @Tom Blodget 如果可行(看起来应该可行),这可能是一个更好的前进方向

标签: c# c++ activex atl


【解决方案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;
}

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

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。

【讨论】:

  • 感谢您的回答。我一回家就检查。
猜你喜欢
  • 1970-01-01
  • 2017-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-13
  • 2011-07-03
  • 1970-01-01
  • 2014-06-29
相关资源
最近更新 更多