I need to implement an event handler for this NewCivicAddressReport event of the CivicFactory object.
This following code, using event handler naming convention, works perfectly in VBScript:
Dim CivicFactory
Set CivicFactory = WScript.CreateObject("LocationDisp.CivicAddressReportFactory", "CivicFactory_")
Function CivicFactory_NewCivicAddressReport(report)
MsgBox "Location changed!"
End Function
However in PowerShell the following code fails:
$CivicFactory = new-object -comObject LocationDisp.CivicAddressReportFactory
Register-ObjectEvent $CivicFactory -EventName "NewCivicAddressReport" -Action ({ echo "hello" })
The error message is : Register-ObjectEvent : Cannot register for event. An event with name 'NewCivicAddressReport' does not exist.
I also tried $CivicFactory.add_NewCivicAddressRerport( {"hello"} ) and it failed too.
So I turned to $CivicFactory | Get-Member: it did return its methods and properties but NO events.
So I suspect PowerShell doesn't support COM events very well. I installed the pseventing snapin and tried Get-EventBinding CivicFactory -IncludeUnboundEvents | Format-Table -Auto - it returned nothing, which means the system doesn't believe this object has events.
So now I'm doubting: is it possible at all to bind an event handler to an object?
Can anyone show me the correct way?
PowerShell doesn't work very well with COM objects if there is no type library available. It's not just events that are missing, typically other members are not there either if you try to use get-member to explore the instance. On the whole, late binding support is sadly lacking but this is probably because of the way powershell uses "adapters" to expose objects' members to the runtime. You can invoke members in a late-bound fashion by using InvokeMember. Take a look at:
http://www.sorrell.mcleod.co.uk/Scotty/powershell/COMinterop.htm
I thought I was finished with PSEventing but it appears there might be room for a v2.0 that can work with late-bound events. Hmmm.
I'm not saying that COM events don't work, they work pefectly well but only if there's an interop or type library available.
-Oisin
(author of pseventing)
Related
I have a .NET application that interacts with Outlook like this:
Microsoft.Office.Interop.Outlook.Application app = new
Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Interop.Outlook.MailItem item = app.CreateItem((Microsoft.Office.Interop.Outlook.OlItemType.olMailItem));
item.PropertyAccessor.SetProperty(PsInternetHeaders + Foobar, 1031);
item.BodyFormat = Microsoft.Office.Interop.Outlook.OlBodyFormat.olFormatHTML;
item.To = "a#test.com;b#test.com;c#test.com";
item.BCC = "cc#test.com";
item.Body = "Hello There!";
item.Display();
Be aware that I need to access the "PropertyAccessor" property.
In a normal environment this runs fine, but in a "secure" enviroment with this registry keys in place it just fails with Operation aborted (Exception from HRESULT: 0x80004004 (E_ABORT)):
[HKEY_CURRENT_USER\Software\Policies\Microsoft\office\16.0\outlook\security]
"PromptOOMAddressBookAccess"=dword:00000000
"AdminSecurityMode"=dword:00000003
"PromptOOMAddressInformationAccess"=dword:00000000
Outlooks security model seems to have a "trustedaddins" list, but I'm not really sure if this applies to "external applications" as well and that exactly I need to register unter TrustedAddins (see here).
My main question would be: Can I just register and foobar.exe unter trustedaddins or is this not possible at all?
I know that I could lower or disable the security stuff, but this is not my choice ;)
Your only options are listed at How to avoid Outlook Security Alert when sending Outlook message from VBScript?
You also might want to set PsInternetHeaders properties to strings only, not ints.
edit: My issue was never quite resolved, but the answer below provided an interesting result.
The app will eventually be called by a powershell script and in this situation, I don't have the issue explained below. No additional {ENTER} is required in the PS console, so no more issue! I couldn't care less if an extra ENTER is required when my app is launched manually via cmd.exe
Problem: In a VB.NET
form app, I'm unable to get the console back to it's "default" state after the code is finished running. I need to press enter manually.
My app can also be executed from command line (in this case, no form is opened. Code is being executed automatically and output sent to console for user to see what happens)
I call AttachConsole(-1), run some code, and when everything's finished I see my latest message in console, but it's as if the process wasn't quite finished.
I have tried SendKeys.SendWait("{ENTER}"). It works well, but only when the console is the current focus. If I click outside the console while the code is running, the ENTER key is sent to whichever window I made active.
So I tried to make the console the current window:
Dim bProcess As Process = Process.GetProcessesByName("cmd").FirstOrDefault()
SetForegroundWindow(bProcess.MainWindowHandle)
// I also tried AppActivate(bProcess.Id)
SendKeys.SendWait("{ENTER}")
FreeConsole()
Nope, the ENTER key will still be sent somewhere else and not to the console. But it does make the console blink orange, so SetForegroundWindow seems to do something...
Any help will be greatly appreciated :)
EDIT
In response to #TnTinMn's answer:
FreeConsole()
// SendKeys.SendWait("test")
PieceOfCodeFromTnTinMn()
Same behavior as I've had so far: This will send the SendKeys.SendWait("~")command "outside" the console if it loses focus while the code is running.
BUT with the 2nd line above un-commented, "test" is sent outside the console and SendKeys.SendWait("~") is sent to the console, as expected.
I'm still trying to figure out what is happening here...
You can use the VB Interaction.AppActivate Method to activate the parent console prior to calling SendKeys.SendWait. This requires that you obtain the ProcessID of the console window that is the parent process of your application.
One way to do this is using Windows Management Instrumentation (WMI). The following is not pretty, but it appears to work. You would execute this after calling FreeConsole
Using currentProcess As Process = Process.GetCurrentProcess
Dim query As New SelectQuery()
Dim props As New StringCollection
props.Add("ProcessId")
props.Add("ParentProcessId")
With query
.ClassName = "Win32_Process"
.Condition = $"ProcessId={currentProcess.Id}"
.SelectedProperties = props
End With
Dim parentProcessId As Int32
Using searcher As New ManagementObjectSearcher(query)
Using mos As ManagementObjectCollection = searcher.Get
Using en As ManagementObjectCollection.ManagementObjectEnumerator = mos.GetEnumerator
If en.MoveNext() Then
parentProcessId = CInt(en.Current.Item("ParentProcessId"))
End If
End Using 'en
End Using ' mos
End Using 'searcher
If parentProcessId <> 0 Then
AppActivate(parentProcessId)
SendKeys.SendWait("~")
End If
End Using 'currentProcess
I'm trying to use botframework v4 in Azure Functions Powershell. I have this working successfully by just calling the raw REST APIs and using the botframework types like Activity.
Now I'm trying to actually use the framework and adapters proper. However, when I call SendActivityAsync on the Turn Context, I get "Object reference not set to an instance of an object" and really no way to find out where that's coming from.
Is there anything obviously missing here, or perhaps another approach to using BotFrameWork adapter I'm missing? I'm trying to adapt the EchoBot sample to Powershell, as the ConsoleBot sample doesn't call http back to the bot framework.
Assume that Activity is real, I know that's not where my null is coming from(it wouldn't let me create the turncontext if it was)
$activity = [JsonConvert]::DeserializeObject(($QueueItem -replace '^---JSON---',''), [activity])
#Only Process Message Activities for now
if ($activity.Type -ne 'message') {return}
$reply = $activity.CreateReply("I got your message that says $($activity.text)")
$appCredentials = [MicrosoftAppCredentials]::new('c28d55e3-fb0b-4367-b4a6-f00959a54e5b','n=_y:3LsJo/MYAPPSECRETP5')
$botFrameworkAdapter = [BotFrameworkAdapter]::new($appCredentials, [AuthenticationConfiguration]::new())
$turnContext = [TurnContext]::new($botFrameworkAdapter, $Activity)
$turnContext.SendActivityAsync($reply).GetAwaiter().GetResult()
So, just a heads up, this is my first time working with google's apis and developer console, so forgive me if I missed something obvious that a more seasoned google developer would deem common sense. That being said. I am trying to create an installed application which will upload a video to youtube under my account. I am writing the application in powershell, so I am importing the appropriate google .Net libraries when the script is launched. From there, I essentially used the sample located here and just converted the content to powershell:
Add-Type -AssemblyName mscorlib
Add-Type -AssemblyName System.Net.Http
Add-Type -AssemblyName System
Add-Type -AssemblyName System.Core
Add-Type -AssemblyName System.Numerics
Add-Type -AssemblyName System.Xml
Add-Type -AssemblyName System.Xml.Linq
Add-Type -AssemblyName System.Data
Add-Type -AssemblyName System.Runtime.Serialization
#the below command imports the following assemblies: Google.Apis.Auth.dll, Google.Apis.Auth.PlatformServices.dll, Google.Apis.Core.dll, Google.Apis.dll, Google.Apis.PlatformServices.dll, Google.Apis.YouTube.v3.dll
Get-ChildItem 'C:\Users\whiggs\Documents\SAPIEN\PowerShell Studio\Projects\youtube\*.dll' | % {[reflection.assembly]::LoadFrom($_.FullName)}
$vid = "C:\Users\whiggs\Documents\gery2.mp4"
#$file = [System.IO.File]::OpenRead("C:\Users\whiggs\Documents\SAPIEN\PowerShell Studio\Projects\youtube\client_id.json")
$filemode = [System.IO.FileMode]::Open
$fileaccess = [System.IO.FileAccess]::Read
$stream = New-object System.IO.FileStream -ArgumentList "C:\Users\whiggs\Documents\SAPIEN\PowerShell Studio\Projects\youtube\client_secret.json", $filemode, $fileaccess
$googlebroker = New-object Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker
$thing = [Google.Apis.Auth.OAuth2.GoogleClientSecrets]::Load($stream)
[string[]]$scope = [Google.Apis.YouTube.v3.YouTubeService+ScopeConstants]::YoutubeUpload
#$scope = [Google.Apis.YouTube.v3.YouTubeService+Scope]::YoutubeUpload
$cancellation = [System.Threading.CancellationToken]::None
$googlebroker = [Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker]::AuthorizeAsync($thing.Secrets, $scope, "<google_username>", $cancellation)
$googlebroker.Wait()
[Google.Apis.Auth.OAuth2.UserCredential]$cred = $googlebroker.Result
$baseclient = new-object Google.Apis.Services.BaseClientService+Initializer
$baseclient.HttpClientInitializer = $cred
$baseclient.ApplicationName = "Contacts Tool"
$service = New-Object Google.Apis.YouTube.v3.YouTubeService($baseclient)
$video = New-Object Google.Apis.YouTube.v3.Data.Video
$video.Snippet = New-Object Google.Apis.YouTube.v3.Data.VideoSnippet
$video.Snippet.Title = "test"
$video.Snippet.Description = "none"
$video.Status = New-Object Google.Apis.YouTube.v3.Data.VideoStatus
$video.Status.PrivacyStatus = "public"
$vidstream = New-Object System.IO.FileStream -ArgumentList $vid, $filemode
$request = $service.Videos.Insert($video, "public", $vidstream, "video/*")
$task = $request.UploadAsync()
$task.Wait()
$vidstream.close()
$vidstream.Dispose()
Don't really need to include the code, because I know it is written correctly as no exception is generated. When I run the above code, it runs to completion without generating an exception, but if I take a look at the object stored in $task (type System.Threading.Tasks.Task), while the overall object reports that it ran to completion, digging deeper into the object's "Result" property reveals the task actually failed, and digging even further into the "exception" property provides the below error message:
The service youtube has thrown an exception: Google.GoogleApiException: Google.Apis.Requests.RequestError
Access Not Configured. YouTube Data API has not been used in project <snip> before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/youtube.googleapis.com/overview?project=<snip> then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry. [403]
Errors [
Message[Access Not Configured. YouTube Data API has not been used in project <snip> before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/youtube.googleapis.com/overview?project=<snip> then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.] Location[ - ] Reason[accessNotConfigured] Domain[usageLimits]
]
at Google.Apis.Upload.ResumableUpload`1.<InitiateSessionAsync>d__25.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Google.Apis.Upload.ResumableUpload.<UploadAsync>d__70.MoveNext()
So, it is clear that there is some kind of issue with the app as it is configured or the way in which I am authenticating to it. However, I know that the app is at least receiving the requests, as you can see here. So, after doing so research, I have a couple of educated guesses as to what the problem might be, and need some input as to a) which of these (if any) is the actual problem and b) what needs to be done to correct it. My first educated guess involves the parameters I passed to the "AuthorizeAsync" method of the Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker class. According to this document:
"In this sample code a new UserCredential instance is created by calling the GoogleWebAuthorizationBroker.AuthorizeAsync method. This static method gets the client secret (or a stream to the client secret), the required scopes, the user identifier, the cancellation token for cancelling an operation, and an optional data store. If the data store is not specified, the default is a FileDataStore with a default Google.Apis.Auth folder. The folder is created in Environment.SpecialFolder.ApplicationData."
The part I want to focus on in the above statement is "the user identifier", as that is all the information that provided as to this parameter's description.
The value that I put in was a string containing the user name for the google account that the app is registered under and the account that would upload the youtube videos, but I don't know if that was the value that was needed, as I had to login to the account anyway via a web browser as part of this process. If this is indeed the problem, what is "the user identifier" as far as this parameter is concerned. A little more detail in the documentation can go a long way. My second educated guess as to what is causing this has to do with the configuration of the application, but more specifically, the generated oauth credentials. The scopes that the app would need access to are apparently considered sensitive, and, if I am understanding this correctly, I have to authenticate from a verified domain and configure a bunch of advanced settings that, as someone writing this project for myself and not a company, I just do not have access to. I just want to upload a youtube video to my account, so why do I need to authenticate from a verified domain? What can I do to get around this? Any info would be great.
So, just a heads up, this is my first time working with google's apis and developer console, so forgive me if I missed something obvious that a more seasoned google developer would deem common sense. That being said. I am trying to create an installed application which will upload a video to youtube under my account. I am writing the application in powershell, so I am importing the appropriate google .Net libraries when the script is launched. From there, I essentially used the sample located here and just converted the content to powershell:
Add-Type -AssemblyName mscorlib
Add-Type -AssemblyName System.Net.Http
Add-Type -AssemblyName System
Add-Type -AssemblyName System.Core
Add-Type -AssemblyName System.Numerics
Add-Type -AssemblyName System.Xml
Add-Type -AssemblyName System.Xml.Linq
Add-Type -AssemblyName System.Data
Add-Type -AssemblyName System.Runtime.Serialization
#the below command imports the following assemblies: Google.Apis.Auth.dll, Google.Apis.Auth.PlatformServices.dll, Google.Apis.Core.dll, Google.Apis.dll, Google.Apis.PlatformServices.dll, Google.Apis.YouTube.v3.dll
Get-ChildItem 'C:\Users\whiggs\Documents\SAPIEN\PowerShell Studio\Projects\youtube\*.dll' | % {[reflection.assembly]::LoadFrom($_.FullName)}
$vid = "C:\Users\whiggs\Documents\gery2.mp4"
#$file = [System.IO.File]::OpenRead("C:\Users\whiggs\Documents\SAPIEN\PowerShell Studio\Projects\youtube\client_id.json")
$filemode = [System.IO.FileMode]::Open
$fileaccess = [System.IO.FileAccess]::Read
$stream = New-object System.IO.FileStream -ArgumentList "C:\Users\whiggs\Documents\SAPIEN\PowerShell Studio\Projects\youtube\client_secret.json", $filemode, $fileaccess
$googlebroker = New-object Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker
$thing = [Google.Apis.Auth.OAuth2.GoogleClientSecrets]::Load($stream)
[string[]]$scope = [Google.Apis.YouTube.v3.YouTubeService+ScopeConstants]::YoutubeUpload
#$scope = [Google.Apis.YouTube.v3.YouTubeService+Scope]::YoutubeUpload
$cancellation = [System.Threading.CancellationToken]::None
$googlebroker = [Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker]::AuthorizeAsync($thing.Secrets, $scope, "<google_username>", $cancellation)
$googlebroker.Wait()
[Google.Apis.Auth.OAuth2.UserCredential]$cred = $googlebroker.Result
$baseclient = new-object Google.Apis.Services.BaseClientService+Initializer
$baseclient.HttpClientInitializer = $cred
$baseclient.ApplicationName = "Contacts Tool"
$service = New-Object Google.Apis.YouTube.v3.YouTubeService($baseclient)
$video = New-Object Google.Apis.YouTube.v3.Data.Video
$video.Snippet = New-Object Google.Apis.YouTube.v3.Data.VideoSnippet
$video.Snippet.Title = "test"
$video.Snippet.Description = "none"
$video.Status = New-Object Google.Apis.YouTube.v3.Data.VideoStatus
$video.Status.PrivacyStatus = "public"
$vidstream = New-Object System.IO.FileStream -ArgumentList $vid, $filemode
$request = $service.Videos.Insert($video, "public", $vidstream, "video/*")
$task = $request.UploadAsync()
$task.Wait()
$vidstream.close()
$vidstream.Dispose()
Don't really need to include the code, because I know it is written correctly as no exception is generated. When I run the above code, it runs to completion without generating an exception, but if I take a look at the object stored in $task (type System.Threading.Tasks.Task), while the overall object reports that it ran to completion, digging deeper into the object's "Result" property reveals the task actually failed, and digging even further into the "exception" property provides the below error message:
The service youtube has thrown an exception: Google.GoogleApiException: Google.Apis.Requests.RequestError
Access Not Configured. YouTube Data API has not been used in project <snip> before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/youtube.googleapis.com/overview?project=<snip> then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry. [403]
Errors [
Message[Access Not Configured. YouTube Data API has not been used in project <snip> before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/youtube.googleapis.com/overview?project=<snip> then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.] Location[ - ] Reason[accessNotConfigured] Domain[usageLimits]
]
at Google.Apis.Upload.ResumableUpload`1.<InitiateSessionAsync>d__25.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Google.Apis.Upload.ResumableUpload.<UploadAsync>d__70.MoveNext()
So, it is clear that there is some kind of issue with the app as it is configured or the way in which I am authenticating to it. However, I know that the app is at least receiving the requests, as you can see here. So, after doing so research, I have a couple of educated guesses as to what the problem might be, and need some input as to a) which of these (if any) is the actual problem and b) what needs to be done to correct it. My first educated guess involves the parameters I passed to the "AuthorizeAsync" method of the Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker class. According to this document:
"In this sample code a new UserCredential instance is created by calling the GoogleWebAuthorizationBroker.AuthorizeAsync method. This static method gets the client secret (or a stream to the client secret), the required scopes, the user identifier, the cancellation token for cancelling an operation, and an optional data store. If the data store is not specified, the default is a FileDataStore with a default Google.Apis.Auth folder. The folder is created in Environment.SpecialFolder.ApplicationData."
The part I want to focus on in the above statement is "the user identifier", as that is all the information that provided as to this parameter's description.
The value that I put in was a string containing the user name for the google account that the app is registered under and the account that would upload the youtube videos, but I don't know if that was the value that was needed, as I had to login to the account anyway via a web browser as part of this process. If this is indeed the problem, what is "the user identifier" as far as this parameter is concerned. A little more detail in the documentation can go a long way. My second educated guess as to what is causing this has to do with the configuration of the application, but more specifically, the generated oauth credentials. The scopes that the app would need access to are apparently considered sensitive, and, if I am understanding this correctly, I have to authenticate from a verified domain and configure a bunch of advanced settings that, as someone writing this project for myself and not a company, I just do not have access to. I just want to upload a youtube video to my account, so why do I need to authenticate from a verified domain? What can I do to get around this? Any info would be great.
Correcting error in your understanding
"<google_username>" is used by filedatastore to store the credeitals for the user once they have consented access to your client. If you want to understand more about this then you should try and read my tutorial on file datastore
$googlebroker = [Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker]::AuthorizeAsync($thing.Secrets, $scope, "<google_username>", $cancellation)
Answer for your issue
YouTube Data API has not been used in project before or it is disabled.
Means that you have not enabled the YouTube api in your project on google Developer console or you have not applied and been granted any quota to this api yet. In Google developer console go to API Library -> YouTube Data API v3 and enable it. Once you have done that click manage then go to Quota. If you have not previously enabled it which i suspect you hadn't then now you will have 0 quota.
click the penile icon and apply for a quota for this api. It can take a while to get a response.
That error message simply means that you have not enabled the API in the Cloud Console. Can you post the error message you get, after you enabled the API? Also, make sure you're running the script with the credentials for the right project. You can run glcoud config list to see which project you're using.