none
Как определить имя владельца процесса, который выполняется под терминалом на SQL сервере (планируется вставить в SQL триггер и отслеживать изменение данных). Как получить значения из коллекции sp_OAGetProperty (wbemObjectSet) RRS feed

  • Вопрос

  • Есть такой SQL запрос

    SET NOCOUNT ON
    --use Master
    go
    sp_configure 'show advanced options', 1 
    go
    reconfigure
    go
    sp_configure 'Ole Automation Procedures', 1 
    go
    reconfigure
    go
    
    GRANT EXECUTE ON [sys].[sp_OACreate] TO [public]
    GRANT EXECUTE ON [sys].[sp_OAGetErrorInfo] TO [public]
    GRANT EXECUTE ON [sys].[sp_OAGetProperty] TO [public]
    GRANT EXECUTE ON [sys].[sp_OASetProperty] TO [public]
    GRANT EXECUTE ON [sys].[sp_OAMethod] TO [public]
    GRANT EXECUTE ON [sys].[sp_OAStop] TO [public]
    GRANT EXECUTE ON [sys].[sp_OADestroy] TO [public]
    
    go
    
    Declare @ResRecordSet int
    Declare @ItemRecordSet int
    Declare @HR int
    Declare @Object int
    Declare @service int
    Declare @command varchar(255)
    Declare @output varchar(255)
    Declare @source varchar(255)
    Declare @description varchar(255)
    Declare @Res Table(PID int, UserName VarChar)
    Declare @Count Int
    
    Declare @UserName Varchar
    Declare @ProcessID int
    
    -- создаем обїект
    Exec @HR = sp_OACreate 'wbemScripting.SwbemLocator', @object OUT
    IF @HR <> 0 
    BEGIN	
    	EXEC @HR = sp_OAGetErrorInfo @object, @source OUT, @description OUT
    	IF @HR = 0 
    	BEGIN
    		SELECT @output = ' Description: '+@description
    		PRINT @output
    	END
    END
    /*
    -- установка настроек доступа
    Exec @HR = sp_OASetProperty @object, 'Security_.ImpersonationLevel', 1
    IF @HR <> 0 
    BEGIN
    	EXEC @HR = sp_OAGetErrorInfo @object, @source OUT, @description OUT
    	IF @HR = 0 
    	BEGIN
    		SELECT @output = ' Security error: '+@description
    		PRINT @output
    	END
    END 
    */
    
    -- подключаемся
    Exec @HR = sp_OAMethod @object, 'ConnectServer', @service OUT, '.','root\cimv2'
    IF @HR <> 0 
    BEGIN
    	EXEC @HR = sp_OAGetErrorInfo @object, @source OUT, @description OUT
    	IF @HR = 0 
    	BEGIN
    		SELECT @output = ' Service error: '+@description
    		PRINT @output
    		RETURN 
    	END
    END
    
    --drop table ##tmpRes
    --Create Table ##tmpRes1 (UserName Varchar, ProcessID int)
    --Insert Into #tmpRes
    
    Select @Command = 'Select Name, ProcessID From Win32_Process WHERE Name Like ''1cv7%''' --ProcessID = '+CAST(Host_id() as varchar)
    
    EXEC @HR = sp_OAMethod @service, 'ExecQuery', @ResRecordSet OUT, @command
    IF @HR <> 0 
    BEGIN
    	EXEC @HR = sp_OAGetErrorInfo @object, @source OUT, @description OUT
    	IF @HR = 0 
    	BEGIN
    		SELECT @output = ' SQL error: '+@description
    		PRINT @output
    		RETURN
    	END
    END
    
    
    EXEC @HR = sp_OAGetProperty @ResRecordSet, 'Count', @Count OUT
    IF @HR = 0 
    	Select @HR, @Count as Count 
    ELSE
    BEGIN
    	EXEC @HR = sp_OAGetErrorInfo @object, @source OUT, @description OUT
    	IF @HR = 0 
    	BEGIN
    		SELECT @output = ' SQL error (Count): '+@description
    		PRINT @output
    		RETURN
    	END
    END
    
    
    EXEC @HR = sp_OAGetProperty @ResRecordSet, 'Item', @ItemRecordSet OUTPUT, 1 
    IF @HR = 0 
    	Select @HR, @ItemRecordSet as Item
    ELSE
    BEGIN
    	PRINT @HR
    	EXEC @HR = sp_OAGetErrorInfo @object, @source OUT, @description OUT
    	IF @HR = 0 
    	BEGIN
    		SELECT @output = ' SQL error (Item): '+@description
    		PRINT @output
    		RETURN
    	END
    END


    В момент попытки получить результаты из коллекции Item выдает ошибку -2147217407  (Generic failure)

    как получить данные из результата запроса? 
    Как написать правильно методо sp_OAGetProperty чтобы полуичть нужные значения?

    • Изменено madyka 27 ноября 2012 г. 11:53
    27 ноября 2012 г. 11:10

Ответы

  • Задача ясна. Наверное, правильнее будет решать её с 1с-ной стороны. Как я понимаю, в рамках семёрки вы живёте с одной коннекцией и можете выполнять произвольные запросы. Сделайте какую-то табличку, в которую при старте системы будете класть, к примеру, host_id, время логина, идентификатор текущего пользователя и всё что ещё захотите. Потом в триггере будете находить последнюю запись по номеру процесса(старые вообще можно стирать при вставке, если у вас вся работа исключительно с терминала идёт) и получите желаемый результат. Ещё можно CONTEXT_INFO использовать.

    Если же идти по вашему пути, то лучше делать через CLR. Но вообще дёргать interop из триггера - очень плохая мысль. Из соображений производительности, в первую очередь. Да и секурность этого всего оставляет желать.

    • Помечено в качестве ответа Abolmasov Dmitry 5 декабря 2012 г. 10:38
    29 ноября 2012 г. 16:43

Все ответы

  • У меня ничего не падает. :) 

    С моей точки зрения, вы идёте по неправильному пути. Это 10 лет назад надо было мучаться с sp_OA* процедурами. Сейчас есть более разумные способы

    Но даже это не так важно, как то, что вообще весьма сомнительной представляется работа со списком процессов из sql server'а напрямую. Т.е. можно, конечно, и в SQL CLR это обернуть, да и с вашим кодом разобраться. Но есть практически полная уверенность в том, что вы решаете проблему не с того конца. 

    В чём именно состоит ваша задача?

    • Предложено в качестве ответа Naomi N 28 ноября 2012 г. 0:17
    27 ноября 2012 г. 21:51
  • Есть база 1С, которая подключается к серверу SQL, посредством sa (windows autentification не взлетит, вход то один для всех) 
    Пользователи подключаются через терминал. 
    К таблице БД 1С прикручиваю (к справочнику например, который будет меняться изредка, 1-2 раза в неделю)
    триггер. В триггере я могу определить PID процесса, который выполняет запрос изменения таблицы (INSERT, DELETE, UPDATE)
    но имя пользователя по командам UserName, suser_sname() и так далее - всегда будет sa или dbo (от имени того кто вошел в SQL Server)
    Чтобы получить имя владельца процесса (УЗ), планировал обратиться к win32_Process и получить оттуда нужную информацию. 
    Проблема может и в старом подходе, но и тот старый подход не могу запустить (sp_OACreate MSScriptControl.scriptControl выдает invalid class string = как победить не пойму; а с sp_OACreate wbemScripting - объект создается и выполняет нужный мне запрос, но коллекцию выборки я получить не могу - нигде не указано что указывать и как вообще с ней работать)

    Еще был совет CLR использовать, но там похоже надо прикручивать dll, которую еще нужно создать. 
    28 ноября 2012 г. 7:59
  • Задача ясна. Наверное, правильнее будет решать её с 1с-ной стороны. Как я понимаю, в рамках семёрки вы живёте с одной коннекцией и можете выполнять произвольные запросы. Сделайте какую-то табличку, в которую при старте системы будете класть, к примеру, host_id, время логина, идентификатор текущего пользователя и всё что ещё захотите. Потом в триггере будете находить последнюю запись по номеру процесса(старые вообще можно стирать при вставке, если у вас вся работа исключительно с терминала идёт) и получите желаемый результат. Ещё можно CONTEXT_INFO использовать.

    Если же идти по вашему пути, то лучше делать через CLR. Но вообще дёргать interop из триггера - очень плохая мысль. Из соображений производительности, в первую очередь. Да и секурность этого всего оставляет желать.

    • Помечено в качестве ответа Abolmasov Dmitry 5 декабря 2012 г. 10:38
    29 ноября 2012 г. 16:43