Как функция Left () работает с API GetWindowsDirectory при передаче строки фиксированной длины? - PullRequest
0 голосов
/ 21 июня 2019

Я не мог сделать свой заголовок описательным и кратким. Я читаю книгу под названием Power Programming с VBA, и в одном из примеров используется Windows API под названием GetWindowsDirectoryA, который принимает параметры string lpBuffer & long nSize. Как только он объявлен, автор использует его в подпрограмме для извлечения каталога Windows.

Что меня смущает, так это то, как это происходит. Вот код:

#If VBA7 And Win64 Then
  Declare PtrSafe Function GetWindowsDirectoryA Lib "kernel32" _
  (ByVal lpBuffer As String, ByVal nSize As Long) As Long
#Else
  Declare Function GetWindowsDirectoryA Lib "kernel32" _
  (ByVal lpBuffer As String, ByVal nSize As Long) As Long
#End If

Sub ShowWindowsDir()
    Dim WinPath As String * 255 'Why does this work even if I make it a regular variant string?
    Dim WinDir As String

    WinPath = Space(255) 'Also works fine if I change this number or comment it out, though if I comment it out, the symbols remain
    WinDir = Left(WinPath, GetWindowsDirectoryA(WinPath, Len(WinPath)))
    MsgBox WinDir, vbInformation, "Windows Directory"
End Sub

Нет ничего плохого в том, как это работает. Когда я наблюдаю, как он выполняется пошагово в редакторе, кажется, что он каким-то образом выполняет дополнительный шаг, который мне не очевиден.

  1. Когда объявляется WinPath, это пустая строка длиной 255 символов. Я не уверен, почему это необходимо, поскольку удаление строки фиксированной длины по-прежнему работает нормально.

  2. WinPath заполнено 255 пробелами по неизвестным причинам. Это работает так же хорошо без этого.

  3. Вот мой парадокс. Мы передаем WinPath в качестве аргумента Left, но WinPath содержит только 255 пробелов, а вторая часть Left обычно говорит ему обрезать 1-й параметр до числа X пробелов. Как предоставление GetWindowsDirectoryA(WinPath, Len(WinPath)) обходит эту нормальную логику и неожиданно приводит к результату: "C: \ Windows"?

Мне кажется, если я пойму, как это работает, это поможет мне понять, как другие API также работают. Кстати, комментарии в коде мои, а не автора.

Ответы [ 2 ]

0 голосов
/ 21 июня 2019

Перед вызовом функции необходимо оценить все ее параметры.Пока это не произойдет, функция еще не вызывается.

Left имеет два параметра, и мы предоставляем выражение для второго параметра.Это выражение, GetWindowsDirectoryA(WinPath, Len(WinPath)), должно быть выполнено полностью перед вызовом Left.

У выражения есть побочный эффект изменения содержимого WinPath, поэтому после возврата GetWindowsDirectoryA(), WinPath содержит новое значение.

Только тогда выполнение входит в Left и получает шанс прочитать переданные ему аргументы.

Обратите внимание, что в этом даже не имеет значенияслучай, если WinPath был фактически помещен в стек до того, как GetWindowsDirectoryA был выполнен.Если это так, то передается переменная, а не ее содержимое.Поэтому, когда Left был бы вызван, он считал бы переменную WinPath из стека и в любом случае указал бы на измененные данные.

Что касается вашей озабоченности по поводу начального содержимого WinPath, буфер должен быть выделен перед вызовом GetWindowsDirectoryA, поэтому вам нужно заполнить его некоторыми одноразовыми символами, такими как пробелы или vbNullChar s.Вы можете сделать это, используя модификатор * 255 или вызывая = Space(255), и то и другое излишне.Обратите внимание, что размер буфера должен составлять MAX_PATH символов, что составляет 260.

0 голосов
/ 21 июня 2019

Когда функция выполняется, список параметров должен быть в положительном порядке, когда он выскакивает из стека, поэтому он должен быть в обратном порядке (от начала к концу) при загрузке, сначала выполняется GetWindowsDirectoryA, затем WinPath получает значение, а затем возвращает значение в список параметров. Вот простая программа для Win32.

#include <windows.h>
#include <iostream>
void f(int a, int b)
{
    printf("a = %d\nb = %d\n", a, b);
}
int main()
{
    int i = 1;
    f(i, ++i);
    getchar();
    return 0;
}

результат:

a = 2
b = 2

но не

a = 1
b = 2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...