Для передачи данных между приложениями, т.к. необходимые объёмы совсем небольшие (массивы из нескольких коротких стрингов), решил попробовать заюзать переменные окружения Windows Погуглил, поковырялся в процедурах. Оказывается, это совсем не сложно. Единственно, что мне не понятно - относительно большое и абсолютно не предсказуемое время создания-чтения-удаления пользовательской переменной окружения. Для тестирования написал процедурку:
Мало того, что длительности достаточно велики (но для моих целей это не очень критично), но от теста к тесту они ещё и существенно разные. А Длительность чтения вообще иногда показывается отрицательной!
[vba]
Код
---------- тест 1 ------------ Create Duration = 0,1953125 s Environment("USER").Count = 5 Read Duration = 0,197265625 s Value = test-test-test Delete Duration = 0,27734375 s Environment("USER").Count = 4 ---------- тест 2 ------------ Environment("USER").Count = 4 Create Duration = 0,796875 s Environment("USER").Count = 5 Read Duration = -0,201171875 s Value = test-test-test Delete Duration = 1,900390625 s Environment("USER").Count = 4
[/vba]
Такое впечатление, что при передаче управления объекту WScript.Shell таймер не только останавливается, но иногда ещё и отматывается обратно. Это как?
Для передачи данных между приложениями, т.к. необходимые объёмы совсем небольшие (массивы из нескольких коротких стрингов), решил попробовать заюзать переменные окружения Windows Погуглил, поковырялся в процедурах. Оказывается, это совсем не сложно. Единственно, что мне не понятно - относительно большое и абсолютно не предсказуемое время создания-чтения-удаления пользовательской переменной окружения. Для тестирования написал процедурку:
Мало того, что длительности достаточно велики (но для моих целей это не очень критично), но от теста к тесту они ещё и существенно разные. А Длительность чтения вообще иногда показывается отрицательной!
[vba]
Код
---------- тест 1 ------------ Create Duration = 0,1953125 s Environment("USER").Count = 5 Read Duration = 0,197265625 s Value = test-test-test Delete Duration = 0,27734375 s Environment("USER").Count = 4 ---------- тест 2 ------------ Environment("USER").Count = 4 Create Duration = 0,796875 s Environment("USER").Count = 5 Read Duration = -0,201171875 s Value = test-test-test Delete Duration = 1,900390625 s Environment("USER").Count = 4
[/vba]
Такое впечатление, что при передаче управления объекту WScript.Shell таймер не только останавливается, но иногда ещё и отматывается обратно. Это как? Alex_ST
С уважением, Алексей MS Excel 2003 - the best!!!
Сообщение отредактировал Alex_ST - Вторник, 29.11.2016, 09:21
Проверил с системным таймером через GetTickCount ( с возможностью VBA7 And Win64 заморачиваться не стал, т.к. у меня это не бывает никогда) Непонятно, почему время разное получается? И, таки-да, нулевое время реально напрягает К стати, Евгений, в зачем ты добавил - в Debug.Print перед системным таймером? Не понял, поэтому убрал, чтобы глаз не мозолило.
[vba]
Код
Environment("USER").Count = 4 Create Duration = 1,5703125 s 1,092s Environment("USER").Count = 5 Read Duration = -0,4296875 s Value = test-test-test 0s Delete Duration = 1,65625 s 2,075s Environment("USER").Count = 4
[/vba]
Проверил с системным таймером через GetTickCount ( с возможностью VBA7 And Win64 заморачиваться не стал, т.к. у меня это не бывает никогда) Непонятно, почему время разное получается? И, таки-да, нулевое время реально напрягает К стати, Евгений, в зачем ты добавил - в Debug.Print перед системным таймером? Не понял, поэтому убрал, чтобы глаз не мозолило.
[vba]
Код
Environment("USER").Count = 4 Create Duration = 1,5703125 s 1,092s Environment("USER").Count = 5 Read Duration = -0,4296875 s Value = test-test-test 0s Delete Duration = 1,65625 s 2,075s Environment("USER").Count = 4
Но вообще-то, поковырявшись, много нового узнал о работе с переменными окружения. Например, то, что есть разные типы, но для самостоятельного использования доступны только переменные типа User Также то, что имена переменных уникальны только внутри типа. В общем, слепил процедурку, которая на лист выводит перечень всех переменных окружения, их типы и значения. Может быть кому-нибудь пригодится:
[vba]
Код
Sub ENVIROMENTS_Excel_Sheet() ' создать в текущей книге лист со списоком переменных окружения, их типами и значениями ' On Error Resume Next Dim oSheet As Worksheet, sEnvName$, sEnvVal$, xArr, xItem, iNdex% Dim sHeader(): sHeader = Array("Environment Name", "Environment Type", "Environment Value (at this computer)") Dim Arr(): Arr = Array("SYSTEM", "VOLATILE", "PROCESS", "USER") ' все возможные типы переменных окружения Dim oDict: Set oDict = CreateObject("Scripting.Dictionary"): oDict.CompareMode = vbTextCompare ' словарь для сбора данных Dim oShell: Set oShell = CreateObject("WScript.Shell") ' ссылка на объект WScript.Shell iNdex = iNdex + 1 ' т.к. sEnvName не уникальны и могут повторяться в разных типах, то как ключ приходится использовать iNdex oDict.Add Key:=iNdex, Item:=Array(sHeader(0), sHeader(1), sHeader(2)) ' заголовки таблицы 'Debug.Print iNdex & vbTab & oDict.Item(iNdex)(0) & vbTab & oDict.Item(iNdex)(1) & vbTab & oDict.Item(iNdex)(2) For Each xArr In Arr ' цикл по всем типам переменных For Each xItem In oShell.Environment(xArr) ' цикл по всем переменным данного типа sEnvName = Left(xItem, 1) & Split(Mid(xItem, 2), "=")(0) sEnvName = IIf(Left(sEnvName, 1) = "=", "'", "") & sEnvName ' патч для предотвращения вычисления формул на листе Excel sEnvVal = Split(Mid(xItem, 2), "=", 2)(1) sEnvVal = IIf(Left(sEnvVal, 1) = "=", "'", "") & sEnvVal ' патч для предотвращения вычисления формул на листе Excel iNdex = iNdex + 1 oDict.Add Key:=iNdex, Item:=Array(sEnvName, xArr, sEnvVal) 'Debug.Print iNdex & vbTab & oDict.Item(iNdex)(0) & vbTab & oDict.Item(iNdex)(1) & vbTab & oDict.Item(iNdex)(2) Next xItem Next xArr With Application.WorksheetFunction ' функция листа ТРАНСП при транспонировании преобразует массив массивов в 2D-массив Arr = .Transpose(.Transpose(oDict.Items)) End With Application.ScreenUpdating = False: Application.EnableEvents = False Set oSheet = ThisWorkbook.Worksheets.Add Cells(1, 1).Resize(UBound(Arr, 1), UBound(Arr, 2)).Value = Arr ' массив - на лист Rows("2:2").Select: ActiveWindow.FreezePanes = True: Rows("1:1").Font.Bold = True ' красота на листе With Cells: .EntireColumn.AutoFit: .HorizontalAlignment = xlLeft: .VerticalAlignment = xlBottom: End With Application.ScreenUpdating = True: Application.EnableEvents = True End Sub
[/vba]
Но вообще-то, поковырявшись, много нового узнал о работе с переменными окружения. Например, то, что есть разные типы, но для самостоятельного использования доступны только переменные типа User Также то, что имена переменных уникальны только внутри типа. В общем, слепил процедурку, которая на лист выводит перечень всех переменных окружения, их типы и значения. Может быть кому-нибудь пригодится:
[vba]
Код
Sub ENVIROMENTS_Excel_Sheet() ' создать в текущей книге лист со списоком переменных окружения, их типами и значениями ' On Error Resume Next Dim oSheet As Worksheet, sEnvName$, sEnvVal$, xArr, xItem, iNdex% Dim sHeader(): sHeader = Array("Environment Name", "Environment Type", "Environment Value (at this computer)") Dim Arr(): Arr = Array("SYSTEM", "VOLATILE", "PROCESS", "USER") ' все возможные типы переменных окружения Dim oDict: Set oDict = CreateObject("Scripting.Dictionary"): oDict.CompareMode = vbTextCompare ' словарь для сбора данных Dim oShell: Set oShell = CreateObject("WScript.Shell") ' ссылка на объект WScript.Shell iNdex = iNdex + 1 ' т.к. sEnvName не уникальны и могут повторяться в разных типах, то как ключ приходится использовать iNdex oDict.Add Key:=iNdex, Item:=Array(sHeader(0), sHeader(1), sHeader(2)) ' заголовки таблицы 'Debug.Print iNdex & vbTab & oDict.Item(iNdex)(0) & vbTab & oDict.Item(iNdex)(1) & vbTab & oDict.Item(iNdex)(2) For Each xArr In Arr ' цикл по всем типам переменных For Each xItem In oShell.Environment(xArr) ' цикл по всем переменным данного типа sEnvName = Left(xItem, 1) & Split(Mid(xItem, 2), "=")(0) sEnvName = IIf(Left(sEnvName, 1) = "=", "'", "") & sEnvName ' патч для предотвращения вычисления формул на листе Excel sEnvVal = Split(Mid(xItem, 2), "=", 2)(1) sEnvVal = IIf(Left(sEnvVal, 1) = "=", "'", "") & sEnvVal ' патч для предотвращения вычисления формул на листе Excel iNdex = iNdex + 1 oDict.Add Key:=iNdex, Item:=Array(sEnvName, xArr, sEnvVal) 'Debug.Print iNdex & vbTab & oDict.Item(iNdex)(0) & vbTab & oDict.Item(iNdex)(1) & vbTab & oDict.Item(iNdex)(2) Next xItem Next xArr With Application.WorksheetFunction ' функция листа ТРАНСП при транспонировании преобразует массив массивов в 2D-массив Arr = .Transpose(.Transpose(oDict.Items)) End With Application.ScreenUpdating = False: Application.EnableEvents = False Set oSheet = ThisWorkbook.Worksheets.Add Cells(1, 1).Resize(UBound(Arr, 1), UBound(Arr, 2)).Value = Arr ' массив - на лист Rows("2:2").Select: ActiveWindow.FreezePanes = True: Rows("1:1").Font.Bold = True ' красота на листе With Cells: .EntireColumn.AutoFit: .HorizontalAlignment = xlLeft: .VerticalAlignment = xlBottom: End With Application.ScreenUpdating = True: Application.EnableEvents = True End Sub
А сколько переменных выводит Environ и сколько CreateObject("WScript.Shell").Environment(sEnvType) Вы видели? Да и, как правильно сказал Udik, Environ только читает переменные. Это я и сам давно выяснил, потому и закопался в Environment Но вопрос со странным временем работы с Environment так и остался открытым...
А сколько переменных выводит Environ и сколько CreateObject("WScript.Shell").Environment(sEnvType) Вы видели? Да и, как правильно сказал Udik, Environ только читает переменные. Это я и сам давно выяснил, потому и закопался в Environment Но вопрос со странным временем работы с Environment так и остался открытым...Alex_ST
С уважением, Алексей MS Excel 2003 - the best!!!
Сообщение отредактировал Alex_ST - Вторник, 29.11.2016, 22:10
Я тут нашёл как переменные через апишки читать/устанавливать, вроде быстрее получается. [vba]
Код
Option Explicit
#If VBA7 And Win64 Then Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long Private Declare PtrSafe Function SetEnvironmentVariable Lib "kernel32" Alias "SetEnvironmentVariableA" (ByVal lpName As String, ByVal lpValue As String) As Long Private Declare PtrSafe Function GetEnvironmentVariable Lib "kernel32" Alias "GetEnvironmentVariableA" (ByVal lpName As String, ByVal lpBuffer As String, ByVal nSize As Long) As Long #Else Private Declare Function GetTickCount Lib "kernel32" () As Long Private Declare Function SetEnvironmentVariable Lib "kernel32" Alias "SetEnvironmentVariableA" (ByVal lpName As String, ByVal lpValue As String) As Long Private Declare Function GetEnvironmentVariable Lib "kernel32" Alias "GetEnvironmentVariableA" (ByVal lpName As String, ByVal lpBuffer As String, ByVal nSize As Long) As Long #End If
Sub test_ENVIROMENT_READ_WRITE2() Dim t As Long t = GetTickCount SetEnvironmentVariable "NLS1_LANG", "test: AMERICAN_AMERICA.CL8MSWIN1251" 'установка переменной Debug.Print GetEnvironmentVar("NLS1_LANG") ' чтение переменной Debug.Print "= " & (GetTickCount - t) / 1000 & "s" t = GetTickCount SetEnvironmentVariable "NLS1_LANG", "test: =========" 'установка переменной Debug.Print GetEnvironmentVar("NLS1_LANG") ' чтение переменной Debug.Print "= " & (GetTickCount - t) / 1000 & "s" t = GetTickCount SetEnvironmentVariable "test", "test: 111111111" 'установка переменной Debug.Print GetEnvironmentVar("test") ' чтение переменной Debug.Print "= " & (GetTickCount - t) / 1000 & "s" t = GetTickCount SetEnvironmentVariable "test", "test: ************" 'установка переменной Debug.Print GetEnvironmentVar("test") ' чтение переменной Debug.Print "= " & (GetTickCount - t) / 1000 & "s" End Sub
'===
Function GetEnvironmentVar(sName As String) As String GetEnvironmentVar = String(255, 0) GetEnvironmentVariable sName, GetEnvironmentVar, Len(GetEnvironmentVar) If InStr(1, GetEnvironmentVar, Chr$(0)) > 0 Then GetEnvironmentVar = Left$(GetEnvironmentVar, InStr(1, GetEnvironmentVar, Chr$(0)) - 1) GetEnvironmentVar = sName + ": " + GetEnvironmentVar End Function
Я тут нашёл как переменные через апишки читать/устанавливать, вроде быстрее получается. [vba]
Код
Option Explicit
#If VBA7 And Win64 Then Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long Private Declare PtrSafe Function SetEnvironmentVariable Lib "kernel32" Alias "SetEnvironmentVariableA" (ByVal lpName As String, ByVal lpValue As String) As Long Private Declare PtrSafe Function GetEnvironmentVariable Lib "kernel32" Alias "GetEnvironmentVariableA" (ByVal lpName As String, ByVal lpBuffer As String, ByVal nSize As Long) As Long #Else Private Declare Function GetTickCount Lib "kernel32" () As Long Private Declare Function SetEnvironmentVariable Lib "kernel32" Alias "SetEnvironmentVariableA" (ByVal lpName As String, ByVal lpValue As String) As Long Private Declare Function GetEnvironmentVariable Lib "kernel32" Alias "GetEnvironmentVariableA" (ByVal lpName As String, ByVal lpBuffer As String, ByVal nSize As Long) As Long #End If
Sub test_ENVIROMENT_READ_WRITE2() Dim t As Long t = GetTickCount SetEnvironmentVariable "NLS1_LANG", "test: AMERICAN_AMERICA.CL8MSWIN1251" 'установка переменной Debug.Print GetEnvironmentVar("NLS1_LANG") ' чтение переменной Debug.Print "= " & (GetTickCount - t) / 1000 & "s" t = GetTickCount SetEnvironmentVariable "NLS1_LANG", "test: =========" 'установка переменной Debug.Print GetEnvironmentVar("NLS1_LANG") ' чтение переменной Debug.Print "= " & (GetTickCount - t) / 1000 & "s" t = GetTickCount SetEnvironmentVariable "test", "test: 111111111" 'установка переменной Debug.Print GetEnvironmentVar("test") ' чтение переменной Debug.Print "= " & (GetTickCount - t) / 1000 & "s" t = GetTickCount SetEnvironmentVariable "test", "test: ************" 'установка переменной Debug.Print GetEnvironmentVar("test") ' чтение переменной Debug.Print "= " & (GetTickCount - t) / 1000 & "s" End Sub
'===
Function GetEnvironmentVar(sName As String) As String GetEnvironmentVar = String(255, 0) GetEnvironmentVariable sName, GetEnvironmentVar, Len(GetEnvironmentVar) If InStr(1, GetEnvironmentVar, Chr$(0)) > 0 Then GetEnvironmentVar = Left$(GetEnvironmentVar, InStr(1, GetEnvironmentVar, Chr$(0)) - 1) GetEnvironmentVar = sName + ": " + GetEnvironmentVar End Function
А феномен отрицательного времени по таймеру мне на Планете объяснили: я, оказывается лоханулся и значение, получаемое от Timer запихивал в переменную Long вместо Single Вот при округлении близкого к нулю значения лажа и получалась.
А феномен отрицательного времени по таймеру мне на Планете объяснили: я, оказывается лоханулся и значение, получаемое от Timer запихивал в переменную Long вместо Single Вот при округлении близкого к нулю значения лажа и получалась.Alex_ST
Udik, попробовал я-таки твой пример с API-шным обращением. Скорость, конечно, впечатляет. И то, что переменные создаются процессные (PROCESS), а не пользовательские (USER), а потому их нет необходимости удалять, т.к. сами умирают после перезапуска Excel - это отлично. Но до чего же не люблю я API Их в отличие от VBA запомнить невозможно, а нужно тупо как иероглифы из справочника дёргать. Да и процедуры с их использованием получаются размазанными по всему модулю: сама процедура в одном месте листинга, а API-шки для неё - в декларациях. Копирнёшь куда-нибудь процедуру, а про декларации забудешь...
Udik, попробовал я-таки твой пример с API-шным обращением. Скорость, конечно, впечатляет. И то, что переменные создаются процессные (PROCESS), а не пользовательские (USER), а потому их нет необходимости удалять, т.к. сами умирают после перезапуска Excel - это отлично. Но до чего же не люблю я API Их в отличие от VBA запомнить невозможно, а нужно тупо как иероглифы из справочника дёргать. Да и процедуры с их использованием получаются размазанными по всему модулю: сама процедура в одном месте листинга, а API-шки для неё - в декларациях. Копирнёшь куда-нибудь процедуру, а про декларации забудешь...Alex_ST