通过互操作接收字符串

发布于 2024-08-17 17:06:00 字数 1616 浏览 6 评论 0原文

我无法从我编写的一些 C 代码中获取字符串。

首先是一些通常不相关的背景信息:我想从 TAPI API 接收 TAPI TSP 的用户可读字符串。我已经实现了一个半可行的 TAPI 解决方案,依赖于将驱动程序名称与存储的字符串相匹配,但希望将其更改为在永久线路 ID 上工作,因为我们的一个客户拥有一个 (阿尔卡特) PBX,它拒绝以任何其他方式工作。

在 C 中,我在头文件中将函数定义为:

__declspec(dllexport) void GetDeviceName(long DeviceId, wchar_t* DeviceName);

该函数是这样编写的:

__declspec(dllexport) void GetDeviceName(long DeviceId, wchar_t* DeviceName)
{
    //tapi code here...

    //copy the string to DeviceName
    wcscpy(DeviceName, (wchar_t*)((char *)devCaps + devCaps->dwLineNameOffset));
}

如上所述,这最终会做一些有用的事情,但现在如果 abc 放在我的 wchar_t*/StringBuilder 中,我会很高兴,我可以请参阅 C# 中的内容。

在 C# 中,我将函数定义为:

    [DllImport("SBW.Tapi.TapiInterop.dll", CharSet = CharSet.Auto)]
    static extern void GetDeviceName(long DeviceId, StringBuilder DeviceName);

我将 DeviceName 定义为 StringBuilder,因为字符串是不可变的,并且我希望在 C 中设置 DeviceName (这是 MS 推荐的)。我还将返回类型设置为 void ,希望这也会影响某些内容(请参阅 这篇 半有用的单一文章,用于部分伪科学解释)。

并这样称呼它:

StringBuilder name = new StringBuilder();
name.EnsureCapacity(100);
long n = 0;
GetDeviceName(n, name);

我将调试器附加到正在运行的进程,设置一个断点,并在 C 代码中注意到 StringBuilder 以某种方式被扭曲并作为空指针提供给非托管代码。

随后在 C# 中抛出 AccessViolationException。

出了什么问题?

删除长参数会有所帮助。我可以将“abc”添加到 C 中的 DeviceName 参数中。但是我想要那个长变量!我做错了什么或令人不安,以便通过在那里使用那么长的参数来强制崩溃?

I'm having trouble getting a string back from some c code I wrote.

First some generally unrealated background info: I'd like to receive the user readable string for a TAPI TSP from the TAPI API. I've implemented a semi workable TAPI solution relying on matching driver names to stored strings, but would like to change this to work on permanant line id's instead, as one of our customers has an (Alcatel) PBX that refuses to work any other way.

In C, I define the function in my header file as:

__declspec(dllexport) void GetDeviceName(long DeviceId, wchar_t* DeviceName);

The function is written thusly:

__declspec(dllexport) void GetDeviceName(long DeviceId, wchar_t* DeviceName)
{
    //tapi code here...

    //copy the string to DeviceName
    wcscpy(DeviceName, (wchar_t*)((char *)devCaps + devCaps->dwLineNameOffset));
}

As stated above, this will eventually do something useful but for now I'll be happy if abc is placed in my wchar_t*/StringBuilder and I can see that in C#.

In C# I define the function as:

    [DllImport("SBW.Tapi.TapiInterop.dll", CharSet = CharSet.Auto)]
    static extern void GetDeviceName(long DeviceId, StringBuilder DeviceName);

I define DeviceName as a StringBuilder because a string is immuatable and I want the DeviceName to be set in C (This is recommended by MS). I also set the return type to void on the slim hope that that also affects something (see this semi helpful mono article for a partial pseudo scientific explanation).

And call it so:

StringBuilder name = new StringBuilder();
name.EnsureCapacity(100);
long n = 0;
GetDeviceName(n, name);

I attach my debugger to the running process, set a breakpoint, and notice in the C code that somehow the StringBuilder is perverted and is provided to the unmanaged code as a null pointer.

An AccessViolationException is subsequently thrown in C#.

What's gone wrong?

removing the long parameter helps. I'm able to add "abc" to my DeviceName parameter in C. I want that long variable however! What am I doing wrong or upsetting so as to force a crash by having that long parameter there?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

╰◇生如夏花灿烂 2024-08-24 17:06:00

但是我想要那个长变量

那么你应该在 C 中将参数定义为 long long。正如 Lucero 指出的,C++ 中的 long 是 32 位,而 C# 中的 long 是 64 位。因此,如果您传递 64 位值,而 C 函数需要 32 位值,则该函数会读取额外的 32 位作为第二个参数,这显然会导致缓冲区地址错误......

I want that long variable however

Then you should define the parameter in C as long long. As Lucero pointed out, a long in C++ is 32 bits, while a long in C# is 64 bits. So if you pass a 64 bit value where the C function expects a 32 bit value, the function reads the extra 32 bits as the second parameter, which obviously results in a wrong address for the buffer...

再见回来 2024-08-24 17:06:00

在 32 位平台上,long 在 .NET 中为 8 字节(64 位)宽,在本机代码中为 4 字节(32 位)宽。您需要像这样更改 P/Invoke 方法:

[DllImport("SBW.Tapi.TapiInterop.dll", CharSet = CharSet.Auto)]
static extern void GetDeviceName(int DeviceId, StringBuilder DeviceName);

这样,C# int 和 C long 在 32 位平台上具有相同的宽度。

On 32 bits platforms long is 8 bytes (64 bits) wide in .NET and 4 bytes (32 bits) wide in native code. You need to change your P/Invoke method like this:

[DllImport("SBW.Tapi.TapiInterop.dll", CharSet = CharSet.Auto)]
static extern void GetDeviceName(int DeviceId, StringBuilder DeviceName);

This way, C# int and C long have the same width on 32 bits platforms.

灰色世界里的红玫瑰 2024-08-24 17:06:00

C++ long 是 32 位,C# long 是 64 位,不是吗?因此,您需要使用 int 作为第一个参数。

The c++ long is 32 bits, the C# long is 64 bits, isn't it? Therefore you'd need to use an int for the first parameter.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文