- PR -

【C#】C#アプリからによるタスク登録の自動化について

投稿者投稿内容
Makoto
大ベテラン
会議室デビュー日: 2004/03/31
投稿数: 133
投稿日時: 2005-12-22 17:50
みさなま、回答ありがとうございます。

TO:todoさん

NetScheduleJobAddに関してサンプルを作成したところ
マーシャリングする型を間違えて、動かないと思っていたら...
下記URLにサンプルが出ていました。
(2時間の苦労が...)

http://dotnet247.com/247reference/msgs/47/239675.aspx

ただ上記のサンプルを実行したところ、
タスクは作成できたのですが、『実行ユーザ』が『System』
になっているようです。
(ユーザのアカウント/パスワードを入力してないんだから当たり前!?)

Win認証みたいな仕組みがあれば、良かったのにといったところです。

あと、MSDNに下記のようにありました。
(これってローカルの場合にも、Admin権限いりそうだなって
 深読みしてしまいました...どうなんでしょうね...)

>『NetScheduleJobAddのセキュリティの要件』
>
>『Administrators ローカルグループのメンバだけが
> リモートサーバーでこの関数を実行できます。』

TO:渋木宏明(ひどり)さん

mstask.tlbファイルが見つかりませんでした。
(これってタイプライブラリファイルですよね?)
もしかするとVisual Studioインストール時に、
インストール外モジュールにしてしまっているのかもしれません...!?

『HEY_CLASSES_ROOT』レジストリにはDLL名はありましたが、
タイプライブラリファイルは登録されていませんでした。
(もちろん、C:\\Windows\\System32以下にファイルもありませんでした。)

>・使用するインターフェースを自分の使う言語用に書き下ろす。

これって引数や戻りの型をうまく変換する(マーシャリング)ってことで
OKでしょうか?

>・CoCreateInstance() API により、使用する COM オブジェクトの
>インスタンスを生成する。

CoCreateInstance() って、APIだったんですね...
COM専用(=C++専用のATLあたりの関数)かと思ってました。
マーシャリングすれば、CoCreateInstance() もC#でCall可能ってことですよね。

>・オブジェクトインスタンスの参照をインターフェースでキャストし、
>必要なメソッドを呼び出す。

この辺は、QueryInterfaceとかってやつを使う話ですよね。

COMは以前挫折したので、やはり敷居が高いですね...

回答ありがとうございました。
もうちょっと、がんばってみます。
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2005-12-22 23:51
こんばんは。

引用:

Makotoさんの書き込み (2005-12-22 17:50) より:

>・使用するインターフェースを自分の使う言語用に書き下ろす。

これって引数や戻りの型をうまく変換する(マーシャリング)ってことで
OKでしょうか?



暇なのでCOMを使ったタスク登録のサンプルでも作ってみようかなと思ったんですが…
ITaskSchedulerインターフェイスやその他のインターフェイス(IScheduledWorkItemインターフェイスなど)を
自分で定義してやらないといけないんですね(~_~;)

System.Runtime.InteropServices.UCOMI**には、よく使われるCOMインターフェイスしか定義してないし、
PINVOKE.NETにも、登録されていなかったし…
他に誰か書き下ろしてくれていないのかなぁ。

C#(.NET)に不慣れな私には、自分でCOMインターフェイスを定義するなんて、めんどくさ過ぎる…
挫折しました。

でもITaskSchedulerインターフェイスを使わないと、
その他の方法は(何処かのサイトで書かれてましたが…『レガシー』な)ATコマンドとしてしか使えないんですよねぇ。

う〜ん、Makotoさん頑張ってください。
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2005-12-23 05:07
おはようございます。

引用:

Tdnr_Symの書き込み (2005-12-22 23:51) より:

C#(.NET)に不慣れな私には、自分でCOMインターフェイスを定義するなんて、めんどくさ過ぎる…
挫折しました。



よく考えてみたら、クソ真面目にCOMインターフェイスを実装する必要なんてなかったですね(~_~;)
必要なインターフェイスメソッドだけ、ちゃんと定義すれば良かったんですね。
あと、vtable(仮想関数テーブル)が、ちゃんと並んでいる必要はありますが。
#vtable(仮想関数テーブル)って、C++ユーザーにしか通じないのかな?


というわけで、ITaskSchedulerの「手抜きCOMインターフェイス」を使って
タスク登録するサンプルプログラムを作ってみました。

ちょっとコードが長いですが、コピペ投下してみたいと思います。
Tdnr_Sym
ぬし
会議室デビュー日: 2005/09/13
投稿数: 464
お住まい・勤務地: 明石・神戸
投稿日時: 2005-12-23 05:12
ITaskSchedulerを使用したタスク登録のサンプルプログラムです。

説明  :2006年の年明け(2006/01/01 00:00)にメモ帳を1回のみ起動します。
注意  :「アカウント名」と「パスワード」は、ご自身の環境に合わせて設定してください。
問題点 :途中で例外が発生した場合、COMが正しくReleaseされません。面倒くさかったので…
     TaskShedulerオブジェクトはインプロセスサーバーなので、とくに問題ないでしょう。
開発環境:Visual C#(Visual Studio.NET 2003)
開発OS:Windows XP Home Edition

#汚いソースコードでスイマセン。参考になれば幸いです。


コード:
// ITaskSchedulerを使用したタスク登録のサンプルプログラム

using System;
using System.Runtime.InteropServices;

namespace ITaskSchedulerSampleCs
{
	class Class1
	{
		[STAThread]
		static void Main(string[] args)
		{
			try
			{
				// タスクスケジューラ オブジェクトを取得する
				ITaskScheduler iTaskScheduler = (ITaskScheduler)CoCreateInstance(CLSID_CTaskScheduler, null, CLSCTX.CLSCTX_INPROC_SERVER, IID_ITaskScheduler);

				// 新しいタスクを作成する
				ITask iTask = (ITask)iTaskScheduler.NewWorkItem("TestTask", CLSID_CTask, IID_ITask);

				// ITaskSchedulerインターフェイスを解放する
				Marshal.ReleaseComObject(iTaskScheduler);

				// 起動するアプリケーション名を設定する
				iTask.SetApplicationName("notepad.exe");

				// アカウント名、パスワードを設定する
				iTask.SetAccountInformation("myAccount", "myPassword");

				// トリガーを作成する
				ushort iNewTrigger;
				ITaskTrigger iTaskTrigger = (ITaskTrigger)iTask.CreateTrigger(out iNewTrigger);

				// スケジュールを設定する
				TASK_TRIGGER pTrigger = new TASK_TRIGGER();

				pTrigger.cbTriggerSize = (ushort)Marshal.SizeOf(pTrigger); 
				pTrigger.wBeginYear =2006;
				pTrigger.wBeginMonth =1;
				pTrigger.wBeginDay =1;
				pTrigger.wStartHour = 0;
				pTrigger.wStartMinute = 0;
				pTrigger.TriggerType = TASK_TRIGGER_TYPE.TASK_TIME_TRIGGER_ONCE;

				iTaskTrigger.SetTrigger(ref pTrigger);

				// ITaskTriggerインターフェイスを解放する
				Marshal.ReleaseComObject(iTaskTrigger);

				// IPersistFileインターフェイスを取得する
				IntPtr pPersistFile = IntPtr.Zero;
				Guid IID_IPersistFile = Marshal.GenerateGuidForType(typeof(UCOMIPersistFile));

				Marshal.QueryInterface(
					Marshal.GetIUnknownForObject(iTask),
					ref IID_IPersistFile,
					out pPersistFile);

				// ITaskインターフェイスを解放する
				Marshal.ReleaseComObject(iTask);

				// タスクをディスクへ保存する
				UCOMIPersistFile iPersistFile = (UCOMIPersistFile)Marshal.GetObjectForIUnknown(pPersistFile);
				iPersistFile.Save(null, true);

				// IPersistFileインターフェイスを解放する
				Marshal.ReleaseComObject(iPersistFile);

			}
			catch (Exception e)
			{
				// 例外発生時にメッセージとスタックとレースを出力する
				Console.WriteLine(e.Message);
				Console.WriteLine(e.StackTrace);
			}
		}

		// CoCreateInstanceのインポート

		[DllImport("ole32.dll", ExactSpelling=true, PreserveSig=false)]
		[return: MarshalAs(UnmanagedType.Interface)]
		static extern object CoCreateInstance(
			[In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
			[MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter,
			CLSCTX dwClsContext,
			[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);

		// CLSCTX列挙子の定義

		[Flags]
		enum CLSCTX : uint
		{
			CLSCTX_INPROC_SERVER		= 0x1, 
			CLSCTX_INPROC_HANDLER		= 0x2, 
			CLSCTX_LOCAL_SERVER			= 0x4, 
			CLSCTX_INPROC_SERVER16		= 0x8,
			CLSCTX_REMOTE_SERVER		= 0x10,
			CLSCTX_INPROC_HANDLER16		= 0x20,
			CLSCTX_RESERVED1			= 0x40,
			CLSCTX_RESERVED2			= 0x80,
			CLSCTX_RESERVED3			= 0x100,
			CLSCTX_RESERVED4			= 0x200,
			CLSCTX_NO_CODE_DOWNLOAD		= 0x400,
			CLSCTX_RESERVED5			= 0x800,
			CLSCTX_NO_CUSTOM_MARSHAL	= 0x1000,
			CLSCTX_ENABLE_CODE_DOWNLOAD	= 0x2000,
			CLSCTX_NO_FAILURE_LOG		= 0x4000,
			CLSCTX_DISABLE_AAA			= 0x8000,
			CLSCTX_ENABLE_AAA			= 0x10000,
			CLSCTX_FROM_DEFAULT_CONTEXT	= 0x20000,
			CLSCTX_INPROC				= CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER,
			CLSCTX_SERVER				= CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER|CLSCTX_REMOTE_SERVER,
			CLSCTX_ALL					= CLSCTX_SERVER|CLSCTX_INPROC_HANDLER
		}

		// CLSID, IIDの定数定義

		static Guid CLSID_CTaskScheduler = new Guid("{148BD52A-A2AB-11CE-B11F-00AA00530503}");
		static Guid CLSID_CTask          = new Guid("{148BD520-A2AB-11CE-B11F-00AA00530503}");
		static Guid IID_ITaskScheduler   = new Guid("{148BD527-A2AB-11CE-B11F-00AA00530503}");
		static Guid IID_ITask            = new Guid("{148BD524-A2AB-11CE-B11F-00AA00530503}");

		// ITaskSchedulerインターフェイスの定義

		[ComImport]
		[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
		[Guid("148BD527-A2AB-11CE-B11F-00AA00530503")]
		interface ITaskScheduler
		{
			void SetTargetComputer();		// dummy
			void GetTargetComputer(); 		// dummy
			void Enum();					// dummy
			void Activate();				// dummy
			void Delete();					// dummy

			[return: MarshalAs(UnmanagedType.Interface)]
			object NewWorkItem(
				[In, MarshalAs(UnmanagedType.LPWStr)] string pwszTaskName,
				[In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
				[In, MarshalAs(UnmanagedType.LPStruct)] Guid riid);
			        
			void AddWorkItem();				// dummy
			void IsOfType();				// dummy
		}

		// ITaskインターフェイスの定義

		[ComImport]
		[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
		[Guid("148BD524-A2AB-11CE-B11F-00AA00530503")]
		interface ITask
		{
			// IScheduledWorkItemインターフェイスメソッド
			[return: MarshalAs(UnmanagedType.Interface)]
			object CreateTrigger(out ushort iNewTrigger);

			void DeleteTrigger();			// dummy
			void GetTriggerCount();			// dummy
			void GetTrigger();				// dummy
			void GetTriggerString();		// dummy
			void GetRunTimes();				// dummy
			void GetNextRunTime();			// dummy
			void SetIdleWait();				// dummy
			void GetIdleWait();				// dummy
			void Run();						// dummy
			void Terminate();				// dummy
			void EditWorkItem();			// dummy
			void GetMostRecentRunTime();	// dummy
			void GetStatus();				// dummy
			void GetExitCode();				// dummy
			void SetComment();				// dummy
			void GetComment();				// dummy
			void SetCreator();				// dummy
			void GetCreator();				// dummy
			void SetWorkItemData();			// dummy
			void GetWorkItemData();			// dummy
			void SetErrorRetryCount();		// dummy
			void GetErrorRetryCount();		// dummy
			void SetErrorRetryInterval();	// dummy
			void GetErrorRetryInterval();	// dummy
			void SetFlags();				// dummy
			void GetFlags();				// dummy

			void SetAccountInformation(
				[In, MarshalAs(UnmanagedType.LPWStr)] string pwszAccountName,
				[In, MarshalAs(UnmanagedType.LPWStr)] string pwszPassword);

			void GetAccountInformation();	// dummy

			// ITaskインターフェイスメソッド

			void SetApplicationName(
				[In, MarshalAs(UnmanagedType.LPWStr)] string pwszApplicationName);

			void GetApplicationName();		// dummy
			void SetParameters();			// dummy
			void GetParameters();			// dummy
			void SetWorkingDirectory();		// dummy
			void GetWorkingDirectory();		// dummy
			void SetPriority();				// dummy
			void GetPriority();				// dummy
			void SetTaskFlags();			// dummy
			void GetTaskFlags();			// dummy
			void SetMaxRunTime();			// dummy
			void GetMaxRunTime();			// dummy
		}

		// ITaskTriggerインターフェイスの定義

		[ComImport]
		[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
		[Guid("148BD52B-A2AB-11CE-B11F-00AA00530503")]
		interface ITaskTrigger
		{
			void SetTrigger(
				ref TASK_TRIGGER pTrigger);

			void GetTrigger();			// dummy
			void GetTriggerString();	// dummy
		}

		// TASK_TRIGGER構造体の定義

		[StructLayout(LayoutKind.Sequential)]
		public struct TASK_TRIGGER
		{
			public ushort cbTriggerSize;
			public ushort Reserved1;
			public ushort wBeginYear;
			public ushort wBeginMonth;
			public ushort wBeginDay;
			public ushort wEndYear;
			public ushort wEndMonth;
			public ushort wEndDay;
			public ushort wStartHour;
			public ushort wStartMinute;
			public uint MinutesDuration;
			public uint MinutesInterval;
			public uint rgFlags;
			public TASK_TRIGGER_TYPE TriggerType;

			//TRIGGER_TYPE_UNION Type;
			public ushort Type1;
			public ushort Type2;
			public ushort Type3;
			public ushort TypePadding;

			public ushort Reserved2;
			public ushort wRandomMinutesInterval;
		}

		// TASK_TRIGGER_TYPE列挙子の定義

		[Flags]
		public enum TASK_TRIGGER_TYPE : uint
		{
			TASK_TIME_TRIGGER_ONCE	= 0,
			TASK_TIME_TRIGGER_DAILY	= 1,
			TASK_TIME_TRIGGER_WEEKLY	= 2,
			TASK_TIME_TRIGGER_MONTHLYDATE	= 3,
			TASK_TIME_TRIGGER_MONTHLYDOW	= 4,
			TASK_EVENT_TRIGGER_ON_IDLE	= 5,
			TASK_EVENT_TRIGGER_AT_SYSTEMSTART	= 6,
			TASK_EVENT_TRIGGER_AT_LOGON	= 7
		};
	}
}


Makoto
大ベテラン
会議室デビュー日: 2004/03/31
投稿数: 133
投稿日時: 2005-12-26 09:42

To:Tdnr_Symさん

サンプル、ありがとうございます。
(サンプルを超越している成果です、本当にありがとうございます。)

熟読させていただいて、勉強してみようと思います。

やはり問題は、入力無しに『パスワード』をどのように得るかにかかってきますね。

※たいていの場合は、Windows認証を識別するセキュリティパラメータが
 enum値になってたりするんでしょうが、今回はやはり入力するしかなさそうですね。
 (やはり古いAPIってことでしょうか!?)

以上、お休み中にもかかわらず、多大なアドバイスありがとうございました。

スキルアップ/キャリアアップ(JOB@IT)