Строки в структурах API

Язык Visual Basic на платформе .NET.

Модераторы: Ramzes, Sebas

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Строки в структурах API

Сообщение Viper » 08.05.2005 (Вс) 11:01

В свое время на VB5 мною был написан класс, инкасулирующий в себя диалоги открытия и сохранения файлов. Он чудесно работал. При появлении VB.NET этот класс я переписал и он работал также замечательно. Но совсем недавно выяснилось, что не все так хорошо. Попробуйте выполнить вот такой код

Код: Выделить всё
<StructLayout(LayoutKind.Sequential, pack:=1)> Private Structure OPENFILENAME1
    Public lStructSize As Integer
    Public hwndOwner As IntPtr
    Public hInstance As IntPtr
    <MarshalAs(UnmanagedType.LPWStr)> Public lpstrFilter As String
    <MarshalAs(UnmanagedType.LPWStr)> Public lpstrCustomFilter As String
    Public nMaxCustFilter As Integer
    Public nFilterIndex As Integer
    <MarshalAs(UnmanagedType.LPWStr)> Public lpstrFile As String
    Public nMaxFile As Integer
    <MarshalAs(UnmanagedType.LPWStr)> Public lpstrFileTitle As String
    Public nMaxFileTitle As Integer
    <MarshalAs(UnmanagedType.LPWStr)> Public lpstrInitialDir As String
    <MarshalAs(UnmanagedType.LPWStr)> Public lpstrTitle As String
    Public Flags As Integer
    Public nFileOffset As Short
    Public nFileExtension As Short
    <MarshalAs(UnmanagedType.LPWStr)> Public lpstrDefExt As String
    Public lCustData As Integer
    Public lpfnHook As IntPtr
    Public lpTemplateName As Integer
    Public pvReserved As IntPtr
    Public dwReserved As Integer
    Public FlagsEx As Integer
End Structure

Private Declare Unicode Function GetOpenFileName Lib "comdlg32" Alias "GetOpenFileNameW" (ByRef ofn As OPENFILENAME1) As Integer

Private Const OFN_READONLY As Integer = &H1
Private Const OFN_OVERWRITEPROMPT As Integer = &H2
Private Const OFN_HIDEREADONLY As Integer = &H4
Private Const OFN_NOCHANGEDIR As Integer = &H8
Private Const OFN_SHOWHELP As Integer = &H10
Private Const OFN_ENABLEHOOK As Integer = &H20
Private Const OFN_ENABLETEMPLATE As Integer = &H40
Private Const OFN_ENABLETEMPLATEHANDLE As Integer = &H80
Private Const OFN_NOVALIDATE As Integer = &H100
Private Const OFN_ALLOWMULTISELECT As Integer = &H200
Private Const OFN_EXTENSIONDIFFERENT As Integer = &H400
Private Const OFN_PATHMUSTEXIST As Integer = &H800
Private Const OFN_FILEMUSTEXIST As Integer = &H1000
Private Const OFN_CREATEPROMPT As Integer = &H2000
Private Const OFN_SHAREAWARE As Integer = &H4000
Private Const OFN_NOREADONLYRETURN As Integer = &H8000
Private Const OFN_NOTESTFILECREATE As Integer = &H10000
Private Const OFN_NONETWORKBUTTON As Integer = &H20000
Private Const OFN_NOLONGNAMES As Integer = &H40000
Private Const OFN_EXPLORER As Integer = &H80000
Private Const OFN_NODEREFERENCELINKS As Integer = &H100000
Private Const OFN_LONGNAMES As Integer = &H200000
Private Const OFN_ENABLEINCLUDENOTIFY As Integer = &H400000
Private Const OFN_ENABLESIZING As Integer = &H800000
Private Const OFN_DONTADDTORECENT As Integer = &H2000000
Private Const OFN_FORCESHOWHIDDEN As Integer = &H10000000

'FlagsEx Values
Private Const OFN_EX_NOPLACESBAR As Integer = &H1

Private ReadOnly cNull As Char = ChrW(0)

Function Main() As Integer
Dim ofn As OPENFILENAME1
    ofn.lStructSize = Marshal.SizeOf(ofn)
    ofn.Flags = OFN_ALLOWMULTISELECT Or OFN_EXPLORER
    ofn.lpstrFile = New String(cNull, 256)
    ofn.nMaxFile = 256
    ofn.lpstrFileTitle = New String(cNull, 256)
    ofn.nMaxFileTitle = 256
    ofn.lpstrFilter = "Текстовые файлы" & cNull & "*.txt" & cNull & "Все   файлы" & cNull & "*.*" & cNull
    ofn.nFilterIndex = 1
    If GetOpenFileName(ofn) <> 0 Then
        Console.WriteLine("Имя файла: {0}", ofn.lpstrFile)
        Console.WriteLine("Длина строки: {0}", ofn.lpstrFile.Length)
    End If
End Function


Если в диалоге выбрать несколько файлов, то в ofn.lpstrFile будет содержаться только путь к папке из которой были выбраны файлы. Длина сей строки будет соответствующей.

В соответствии с MSDN при установки флагов OFN_ALLOWMULTISELECT и OFN_EXPLORER в ofn.lpstrFile должны возвращаться путь к папке и собственно файлы, разделенные нуль-терминатороми. После нескольких попыток выснить в чем тут дело, оказалось, что ВСЕ элементы структуры, являющиеся строками обрезаются до ближайщего нуль-терминатора. Дальше выяснилось, что таким образом обрабатываются ВСЕ аналогичные API функции, то есть функции использующие в качестве параметра структуры, содержащие указатели на строки.

Для интереса я сменил определение структуры и получил более приемлимые результаты:

Код: Выделить всё
<StructLayout(LayoutKind.Sequential, pack:=1)> Private Structure OPENFILENAME2
    Public lStructSize As Integer
    Public hwndOwner As IntPtr
    Public hInstance As IntPtr
    Public lpstrFilter As IntPtr
    Public lpstrCustomFilter As IntPtr
    Public nMaxCustFilter As Integer
    Public nFilterIndex As Integer
    Public lpstrFile As IntPtr
    Public nMaxFile As Integer
    Public lpstrFileTitle As IntPtr
    Public nMaxFileTitle As Integer
    Public lpstrInitialDir As IntPtr
    Public lpstrTitle As IntPtr
    Public Flags As Integer
    Public nFileOffset As Short
    Public nFileExtension As Short
    Public lpstrDefExt As IntPtr
    Public lCustData As Integer
    Public lpfnHook As IntPtr
    Public lpTemplateName As Integer
    Public pvReserved As IntPtr
    Public dwReserved As Integer
    Public FlagsEx As Integer
End Structure

Private Declare Unicode Function GetOpenFileName Lib "comdlg32" Alias "GetOpenFileNameW" (ByRef ofn As OPENFILENAME2) As Integer

Private Sub Main()
Dim ofn As OPENFILENAME2
    ofn.lStructSize = Marshal.SizeOf(ofn)
    ofn.Flags = OFN_ALLOWMULTISELECT Or OFN_EXPLORER
    Dim sFile(255) As Char
    Dim sFileTitle(255) As Char
    Dim sFilter() As Char = ("Текстовые файлы" & cNull & "*.txt" & cNull  & "Все файлы" & cNull & "*.*" & cNull).ToCharArray
    ofn.lpstrFile = Marshal.UnsafeAddrOfPinnedArrayElement(sFile, 0)
    ofn.nMaxFile = 256
    ofn.lpstrFileTitle = Marshal.UnsafeAddrOfPinnedArrayElement(sFileTitle, 0)
    ofn.nMaxFileTitle = 256
    ofn.lpstrFilter = Marshal.UnsafeAddrOfPinnedArrayElement(sFilter, 0)
    ofn.nFilterIndex = 1
    If GetOpenFileName(ofn) <> 0 Then
        Console.WriteLine("Имя файла: {0}", New String(sFile))
        Console.WriteLine("Длина строки: {0}", sFile.Length)
    End If
End Sub



То есть во втором варианте вместо строк передается указатель на первый элемент символьного массива, что для C в принципе тоже самое. Длина символьного массива после возвращения из функции здесь не меняется и он содержит то что должен содержать.

Может ли кто-нить что-нить сказать по этому поводу?

P.S. Про существование OpenFileDialog я знаю
Весь мир матрица, а мы в нем потоки байтов!

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Сообщение Viper » 13.06.2005 (Пн) 11:00

так ни у кого и нет мыслей по этому поводу?

за что VB.NET обрезает строки в структурах до первого встреченного null-терминатора при возвращаении из API функций?

проверял: обрезает именно VB.NET C пытается вернуть строку неизменной длины
Весь мир матрица, а мы в нем потоки байтов!

gaidar
System Debugger
System Debugger
 
Сообщения: 3152
Зарегистрирован: 23.12.2001 (Вс) 13:22

Сообщение gaidar » 13.06.2005 (Пн) 15:13

Так для него null-терминатор - всегда конец, если строка не фиксированной длины (а иначе как понять, где строка кончается?). А строк фиксированной длины в VB.NET просто нет. Выход - использовать массив символов char.
The difficult I’ll do right now. The impossible will take a little while. (c) US engineers in WWII
I don't always know what I'm talking about, but I know I'm right. (c) Muhammad Ali

Viper
Артефакт VBStreets
Артефакт VBStreets
Аватара пользователя
 
Сообщения: 4394
Зарегистрирован: 12.04.2005 (Вт) 17:50
Откуда: Н.Новгород

Сообщение Viper » 14.06.2005 (Вт) 17:24

gaidar писал(а):Так для него null-терминатор - всегда конец, если строка не фиксированной длины (а иначе как понять, где строка кончается?).


не совсем так. для VB null-терминатор не конец строки. При возврате из функций API где строка является параметром VB длину строки не обрезает (оставшиеся символы все null-терминаторы), надо делать это самостоятельно. А вот в структурах строка именно что обрезается

Походу такова се ля ви
Весь мир матрица, а мы в нем потоки байтов!


Вернуться в Visual Basic .NET

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 5

    TopList