开发者

What is the correct way to set StringBuilder.Capacity when using P/Invoke?

Should StringBuilder.Capacity be set to the maximum number of .NET characters, without regards to null termination, or must it be set one higher to reserve space for a null terminator when using P/Invoke.

The natural reaction is that it should be set one higher, but it seems like P/Invoke is supposed to automatically compensate. In fact this is actually documented right here: http://msdn.microsoft.com/en-US/library/s9ts558h(v=VS.100).aspx

The reason for this question is that most examples are not strictly consistent with th开发者_运维百科e above documentation. Almost always they are coded:

StringBuilder sb = new StringBuilder(dotNetChars + 1);
SomeWindowsAPI(sb, sb.Capacity);

Instead of:

StringBuilder sb = new StringBuilder(dotNetChars);
SomeWindowsAPI(sb, sb.Capacity + 1);

(I realize that some APIs handle the buffer size parameter differently. Assume that the API handles this the must common way, like GetFullPathName: http://msdn.microsoft.com/en-us/library/aa364963(v=VS.85).aspx)

Using an expression with sb.Capacity directly in the API call seems to be a best practice to avoid a mismatch. The issue is whether or not adding the +1 is correct.

Look around. You'll probably find that the only place showing sb.Capacity + 1 is the MSDN documentation.

Of course, one can allocate on the side of caution with a larger buffer than is strictly necessary, but I would like to know the consensus on how to do this.


I realise you have five-year-old answers already, but in my opinion they do not really answer the question, they basically wave away the potential problem without checking whether doing so is correct.

The MSDN documentation guarantees that the marshaller will ensure that there is enough room to store the full Capacity of the StringBuilder plus an additional null terminator. Quoting Default Marshaling for Strings:

Fixed-Length String Buffers

[...]

The solution is to pass a StringBuilder buffer as the argument instead of a string. A StringBuilder can be dereferenced and modified by the callee, provided it does not exceed the capacity of the StringBuilder. It can also be initialized to a fixed length. For example, if you initialize a StringBuilder buffer to a capacity of N, the marshaler provides a buffer of size (N+1) characters. The +1 accounts for the fact that the unmanaged string has a null terminator while StringBuilder does not.

[...]

So you don't have to worry about adding one to the capacity, the marshaller will already do this for you.


Inside the constructor of StringBuilder, capacity is used like this:

m_ChunkChars = new char[capacity];

This is used together with the m_ChunkLength field to determine the contents of the StringBuilder. This only describes actual characters, not including a terminating character.

So your answer is the + 1 is not necessary.


Probably a certain amount of cargo cult programming going on. Someone picks up the habit, when asked about memory to allocate for strings, of answering "length+1" instead of length. Rather than read the documentation for new circumstances in which you're asked about memory to allocate for strings, the developer just "errs on the safe side" and passes in length + 1. Others who read that code infer the same rule and the practice spreads. It doesn't have to be right to be passed from person to person, just reasonably likely to be right, and not actively harmful.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜