excelvbaでCreateThreadの動作

このQ&Aのポイント
  • Excel2007で、VBAを利用した簡単なデータエントリ、管理ソフトを作成しています。ACCESSが無いため、データベースもExcelファイルを使用しています。ADODBで、データベース用のExcelファイルを開くのですが、エントリ数が増えるに従い、openに時間がかかるようになってきました。
  • CreateThreadでスレッドを作成して、connectionOpenのスレッドと、プログレスバーのコントロールを分離しようと作成してみましたが、CreateThreadで作成した方のプログラムがうまいこと動作してくれません。ConnectionOpenをメイン、プログレスバーを別スレッドにしたもの、プログレスバーをメイン、ConnectionOpenを別スレッドにしたものを両方作成してみましたが、どちらも別スレッドにした方がうまく動きません。
  • もしかして、CreateThreadは割り込みがかけられないような状況では別のスレッドは動作しないのでしょうか?また、CreateThreadで作成されたスレッドは、重たい処理は無理なのでしょうか?
回答を見る
  • ベストアンサー

excelvbaでCreateThreadの動作

Excel2007で、VBAを利用した簡単なデータエントリ、管理ソフトを作成しています。 ACCESSが無いため、データベースもExcelファイルを使用しています。  ADODBで、データベース用のExcelファイルを開くのですが、エントリ数が増えるに従い、openに時間がかかるようになってきました。そのため、プログレスバーで、VBAが動作していることをアピールすることとしました。  まず、非同期接続を試したのですが、connectionを数回OpenとCloseを繰り返すと、coinitializeでエラーが出てしまい、Excelが落ちる状況となってしまうためあきらめました。  次の手段として、CreateThreadでスレッドを作成して、connectionOpenのスレッドと、プログレスバーのコントロールを分離しようと作成してみましたが、CreateThreadで作成した方のプログラムがうまいこと動作してくれません。  ConnectionOpenをメイン、プログレスバーを別スレッドにしたもの、プログレスバーをメイン、ConnectionOpenを別スレッドにしたものを両方作成してみましたが、どちらも別スレッドにした方がうまく動きません。  debug.print "test"を別スレッドの1行目に入れたところ、イミディエイトに表示されるので、処理が渡っていないわけではないようです。  また、openをメインスレッドにした時にわかっているのは、メインスレッドのADOCon.Openの行が実行されたと同時に、別スレッドが止まってしまっているようです。  もしかして、CreateThreadは割り込みがかけられないような状況では別のスレッドは動作しないのでしょうか?また、CreateThreadで作成されたスレッドは、重たい処理は無理なのでしょうか? テスト用のデータです。 'Busyというユーザーフォームに、PBerというプログレスバーを配置 'C:\Users\xx\Desktop\に、DBファイルを配置 XXは、ユーザー名 'mihon.xlsxは、約5MB '変数等は、両タイプとも共通 Public bRun As Boolean Public adoCON As New ADODB.Connection Public Declare Function CreateThread Lib "kernel32" (ByVal lpThreadAttributes As Long, _ ByVal dwStackSize As Long, ByVal lpStartAddress As Long, _ ByRef lpParameter As Long, ByVal dwCreationFlags As Long, _ ByRef lpThreadID As Long) As Long Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long) 'connectionOpenをメイン、プログレスバーを別スレッド Sub AdoOpen() Dim ThreadId As Long Dim hThread As Long With Busy .BusyMes.Caption = "DB接続処理中" .PBar.Visible = True .PBar.Value = 0 .PBar.Min = 0 .PBar.Max = 10 .Show vbModeless End With DoEvents bRun = False hThread = CreateThread(0&, 0&, AddressOf Counter, 0&, 0&, ThreadId) Application.Wait [NOW()+"0:00:00.5"] With adoCON .Provider = "Microsoft.ACE.OLEDB.12.0" .Properties("Extended Properties") = "Excel 12.0" .Open "C:\Users\xx\Desktop\mihon.xlsx" End With bRun = True If hThread Then CloseHandle hThread hThread = 0 End If With Busy .BusyMes.Caption = "" .PBar.Value = 0 .PBar.Visible = False .Hide End With DoEvents End Sub Function Counter() ' As Boolean Dim bCountup As Boolean Do Until bRun Select Case Busy.PBar.Value Case 0 bCountup = True Case 10 bCountup = False End Select If bCountup Then Busy.PBar.Value = Busy.PBar.Value + 1 Else Busy.PBar.Value = Busy.PBar.Value - 1 End If Sleep 500 Loop End Function 'プログレスバーをメイン、connectionOpenを別スレッド Sub CounterStart() Dim bCountup As Boolean Dim ThreadId As Long Dim hThread As Long 'スレッドハンドル With Busy .BusyMes.Caption = "DB接続処理中" .PBar.Visible = True .PBar.Value = 0 .PBar.Min = 0 .PBar.Max = 10 .Show vbModeless End With DoEvents bRun = False hThread = CreateThread(0&, 0&, AddressOf Counter2, 0&, 0&, ThreadId) Do Until bRun Select Case Busy.PBar.Value Case 0 bCountup = True Case 10 bCountup = False End Select If bCountup Then Busy.PBar.Value = Busy.PBar.Value + 1 Else Busy.PBar.Value = Busy.PBar.Value - 1 End If Application.Wait [NOW()+"0:00:01.5"] Loop If hThread Then CloseHandle hThread hThread = 0 End If With Busy .BusyMes.Caption = "" .PBar.Value = 0 .PBar.Visible = False .Hide End With DoEvents End Sub Function Counter2() With adoCON .Provider = "Microsoft.ACE.OLEDB.12.0" .ConnectionString = "Data Source=" & ObjDB.Value & "; Extended Properties=""Excel 12.0;""" .Open "C:\Users\xx\Desktop\mihon.xlsx" End With bRun = True End Function

質問者が選んだベストアンサー

  • ベストアンサー
  • yorozu_ya
  • ベストアンサー率54% (76/140)
回答No.2

> ACCESSが無いため、データベースもExcelファイルを使用しています。 ACCESSが無くてもMDBファイルをデータベースとして扱えますよ。 データ量が多いなら、MDBファイルを使うことを検討してはいかがでしょう。

takotako_agare
質問者

お礼

返信が遅くなりました。 結局、Excelファイルをデータベースファイルとして利用することをあきらめました。 ExcelでMDBを作成し、今までのデータをインポートして利用することができました。 adoで作成していたため、変更箇所も少なく、スムーズに移行しました。 ありがとうございました。

その他の回答 (1)

  • nda23
  • ベストアンサー率54% (777/1415)
回答No.1

先ずCreateThreadで指定するメソッドは 整数のパラメータを1個持ち、整数の値を 返すpascal型でなければなりません。 VBAの関数はpascal型なので無意識でも よいのですが、掲題のCounter2はパラ メータが無く、戻り値がVARIANT型です。 VARIANT型を返す関数は内部的には 戻り値ではなく、値をセットするポインタを パラメータに持つので、形式はたまたま合い ますが、「運よく動いている」だけであり、 好ましい状態ではありません。 VBAおよびオブジェクトの大半はスレッド セーフではありません。よって、VBAを使う マルチスレッドは実行不可能といってよいと 思います。 C言語でDLLを作り、その中でマルチスレッドに することはできます。但し、オブジェクトを スレッド間で使うのはできないと思った方が よいでしょう。スレッド内でオブジェクトの インスタンス化~解放までするなら使用でき ます。しかし、C言語でCOMオブジェクトを扱う のはとても面倒で、VBでやるようなものとは ワケが違います。 MySQLやPostgreSQLなどを使ってDBを構築し、 マルチプロセスでデータをロードすることを 考えてみてはいかがでしょうか。

takotako_agare
質問者

お礼

Excelファイルでデータベースはあきらめました。 他の方の意見を参考に、MDBファイルに移行することにどうにか成功しました。 ありがとうございました。

takotako_agare
質問者

補足

nda23様 早速の回答ありがとうございます。 やはり、VBAでマルチスレッドは難しいのですね。 MySQLなどのフリーソフトや自作Cなどは、会社のセキュリティポリシーの関係で自由な取り扱いができないんです。 業務に必要だからと本社のシステムグループに使用許可をもらうにしても、他のインストール済みソフト(業務用の基幹システム等)への影響や、複数のソフトの組み合わせによる著作権等への影響がないか、あらゆるチェックをしなければ許可は出せないそうです。(実際には影響範囲がチェックしきれないため許可はでない) そのため、VBAであれば前述の問題が発生しないため、VBAのみでどこまでできるかに執着していた次第です。 CounterStartから、Counter2を別スレッドで呼び出すのは、戻り値が整数値ではないためだめなのですね。まぁ、connectionを開くのだからそうですよね。 実際、プログレスバーが動いてもconnectionが開かないので、Counter2が止まっているのは間違いないと思います。 AdoOpenから、Counterを別スレッドで呼び出す方のは、戻り値がVARIANT型ではないですけど、やはり難しいのでしょうか。 ネットで調べた中に、VBですが、フォームに配置したラベルのバックカラーを別スレッドで変更するっていうのがあって、それはExcelVBAでも問題なく動きました。 しかし、質問に記載したもの(フォームに配置したプログレスバーを別スレッドで操作する)を実行すると、メインスレッドのADOCon.openまで来ると、open処理が終了するまで別スレッドに処理がいかないようなんですが。

関連するQ&A

  • Mutexの次の使い方で

    typedef struct{ HWND hwnd;BOOL th_end;HANDLE hmutex; } DATA, *PDATA; /////////////////////////////////////// static HANDLE hThread1,hThread2; DWORD threadID1,threadID2; static DATA data; static HANDLE hMutex; switch (msg) { case WM_CREATE: //hMutex= //CreateMutex(NULL,FALSE,NULL); data.hwnd = hWnd; data.th_end = FALSE; data.hmutex = hMutex; hThread1=CreateThread(NULL,0, (LPTHREAD_START_ROUTINE)Thread1, (LPVOID)&data, 0,(LPDWORD)&threadID1); hThread2=CreateThread(NULL,0, (LPTHREAD_START_ROUTINE)Thread2, (LPVOID)&data, 0,(LPDWORD)&threadID2); /*x*/hMutex= /*x*/CreateMutex(NULL,FALSE,NULL); break; とするのは正しくて/*x*/の行を削除して//をとり hMutexの位置を前に持ってくるのは間違っているのでしょうか?

  • ExcelVBAについて

    以上~以下検索についてです。 現在、1文字以上一致で検索し、listboxに検索結果を表示させることができます コードは下記 Private Sub CommandButton1_Click() Dim lastRow As Long Dim myData, myData2(), myno Dim i As Long, j As Long, cn As Long With Workbooks("Master.xlsm").Worksheets("Sheet1") myData = .Range(.Cells(3, 2), .Cells(Rows.Count, 5).End(xlUp)).Value lastRow = .Cells(Rows.Count, 2).End(xlUp).Row End With ReDim myData2(1 To lastRow, 1 To 4) For i = LBound(myData) To UBound(myData) If myData(i, 2) Like "*" & TextBox2.Value & "*" And _ myData(i, 3) Like "*" & TextBox3.Value & "*" And _ myData(i, 4) Like "*" & TextBox4.Value & "*" _ Then cn = cn + 1 myData2(cn, 1) = myData(i, 1) myData2(cn, 2) = myData(i, 2) myData2(cn, 3) = myData(i, 3) myData2(cn, 4) = myData(i, 4) End If Next i If cn = 0 Then MsgBox "検索結果は見つかりませんでした・・・" Else End If With ListBox1 .ColumnCount = 4 .ColumnWidths = "20;40;20;60" End With End Sub そして今回教えていただきたいのが userfoamで 例えば,金額が1000~100000の間のものを検索し、 それに該当するものすべてをリストボックスに表示させることです。 このコードに以上~以下検索を追加するにはどうすればいいでしょうか? 新しい方法、これよりいい方法があればお教えください。 よろしくお願いいたします。

  • ExcelVBAでのkernel32(64bit)

    今までExcel2000のVBAから、以下のようなコードを使ってC++で作ったコマンドプロンプトで動くプログラムを動かすプログラムを作っていましたが、これを64bitのWindows7上で動いているExcel2010で使おうとしたらメッセージが出ました。いろいろ調べてみたところ、たぶんDeclareにPtrSafeを付ければ良いようなのですが、その際、他のコードはそのままで良いのでしょうか。特に、コード中のLongはそのままで良いのか気になるのですが...。ちなみに、下記コードの条件コンパイルはネットで調べて見よう見まねで付けたもので、Excel2000のときには付けていないものでした。ご存じの方がいらっしゃいましたらご教授ください。 '------------------------------------------------------------------------------ ' Win32 API関数・定数の宣言 '------------------------------------------------------------------------------ #If VBA7 And Win64 Then '64bit Declare PtrSafe Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, _   ByVal dwMilliseconds As Long) As Long Declare PtrSafe Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, _   ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long Declare PtrSafe Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long #Else '32bit Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, _   ByVal dwMilliseconds As Long) As Long Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, _   ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long #End If Private Const PROCESS_ALL_ACCESS As Long = &H1F0FFF Private Const INFINITE As Long = &HFFFF '------------------------------------------------------------------------------ ' Run '------------------------------------------------------------------------------ Public Sub Run(ByVal project_name As String)   Dim program As String   Dim task_id As Long   Dim h_proc As Variant   program = mdlFunc.ProgramPath() & mdlFunc.ProgramOption(project_name) 'プログラム名   task_id = Shell(program, vbHide)   h_proc = OpenProcess(PROCESS_ALL_ACCESS, False, task_id)   If OpenProcess(PROCESS_ALL_ACCESS, False, task_id) <> vbNull Then     Call WaitForSingleObject(h_proc, INFINITE)     CloseHandle h_proc   End If End Sub

  • スレッドの廃棄について

    ウインドウ上のボタンを押すとあるスレッドがスタートするような プログラムがあるとします。 ボタンのクリックイベントプロシージャ内で 以下のように書いてスレッドをスタートさせることにしたのですが、 Sub MainWnd_CommandButton1_Click() 'スレッドのスタート hThread1=CreateThread(ByVal 0,0,AddressOf(MainOperation),0,0,VarPtr(thread1_ID)) End Sub このままではボタンがクリックされるたびに次々新しくスレッドが 生成されてしまい収拾がつかなくなるのでは?と思います。 基本的に一つのスレッドのみを存在させたいので、 CreateThreadの直前に「CloseHandle(hThread1)」と書こうかとも 思ったのですが、仮にスレッドが存在して無い場合、その場合は 無効なハンドルをクローズすることになりそうで何だか不都合が ありそうです。 スレッドの存在を調べて、あれば安全に廃棄して新しいものをスタート させる、といったことはどのように実装するべきでしょうか? ActiveBasicを使っていますが、教えて頂く際には CやVBでも構いません。

  • ExcelVBA マクロの数値を2ケタに変更したい

    http://www.exvba.com/blog/?p=3974 こちらの百ます計算のシートをダウンロードし活用させて頂こうと思っていますが、2ケタのものが作れるかを試してみたいと思っています。例えば26+57とか、81-32とかです。掛け算だけは、2ケタ×1ケタにしたいです。 当方プログラムが初心者でスキル不足のため、VBAがよく分かりません。以下がスクリプトのようですが、色々数値を変えてみましたがダメでした。 <--ここから--> Option Explicit '100マス計算ジェネレータ by 達人養成塾 http://www.exvba.com/ Sub main() Dim calc As Long With Application calc = .Calculation .Calculation = xlCalculationManual InputBase Range("B5"), "+", False InputBase Range("B18"), "-", True InputBase Range("B31"), "×", False .Calculation = calc ' .Calculation = xlAutomatic End With End Sub Sub InputBase(bs As Range, op As String, bType As Boolean) With bs If Not IsEmpty(bs) Then .CurrentRegion.ClearContents End If If Not IsEmpty(.Offset(, 12)) Then .Offset(, 12).CurrentRegion.ClearContents End If SetLine bs, op, True, False SetLine bs, op, False, bType .CurrentRegion.Copy Destination:=.Offset(, 12) ExeCalc bs, op End With End Sub Private Sub SetLine(base As Range, ope As String, bRow As Boolean, add10 As Boolean) '起点セル、演算子、行か列か、値に10を足すか Dim c As Long, ar(9) As Long With base .Value = ope Application.Calculate With Worksheets("rnd") .Range("A2:B12").Sort key1:=.Range("B2"), order1:=xlAscending, Header:=xlYes For c = 3 To 12 ar(c - 3) = .Range("A" & c).Value Next End With If bRow Then For c = 1 To 10 .Offset(c).Value = ar(c - 1) Next ElseIf Not add10 Then For c = 1 To 10 .Offset(, c).Value = ar(c - 1) Next Else For c = 1 To 10 .Offset(, c).Value = ar(c - 1) + 10 Next End If End With End Sub Private Sub ExeCalc(base As Range, ope As String) Dim r As Long, c As Long With base.Offset(, 12) Select Case ope Case "+" For r = 1 To 10 For c = 1 To 10 .Offset(r, c).Value = .Offset(r).Value + .Offset(, c).Value Next Next Case "-" For r = 1 To 10 For c = 1 To 10 .Offset(r, c).Value = .Offset(, c).Value - .Offset(r).Value Next Next Case Else For r = 1 To 10 For c = 1 To 10 .Offset(r, c).Value = .Offset(r).Value * .Offset(, c).Value Next Next End Select End With End Sub <--ここまで--> こちら他者様の著作物になりますので、もしこうした質問が不適切でしたら削除させて頂きます。 もしよろしければアドバイスを頂けましたら幸いに思います。

  • ExcelVBA .cells(… が働きません

    お世話になります メモリーの、壁と 格闘して、います 其の、中で OfficeTANAKA様の http://officetanaka.net/excel/vba/tips/tips71.htm の、ページを 拝見し Private Declare Sub GlobalMemoryStatus Lib "kernel32" (lpBuffer As MEMORYSTATUS) と、モジュールに、 書き加えた、ところ With Ws   ↓此処、メソットがダメ  With .Range(.cells(… が、突如エラーに なり始めました Private Declare S… を、外して 全て、元通りに しても 回復、しません 回復可能で、しょうか? 宜しく お願い、します                  記 Option Explicit Option Base 0 Private Declare Sub GlobalMemoryStatus Lib "kernel32" (lpBuffer As MEMORYSTATUS) '↑  ↑  ↑  ↑ '此れを、加えると Type 変数取得順速度差を減らす  s1 As Long  s2 As Long  Ch As Long  du As Long  St As String  Rg As Range End Type Const n As Long = 2000 Dim lp As 変数取得順速度差を減らす, Data(1 To n, 1 To n) As Long, k As Long, Ws As Worksheets Sub ダミーデータ作成1()  Set Ws = Worksheets.Add()  Worksheets("Sheet1").Select  With Ws’   ↓此処で、エラー   With .Range(.Cells(1, 1), .Cells(n, n))    Let lp.St = "Min(" & Ws.Name & "!" & .Address & ")"   End With   For k = 0 To Int(n / 100) - 1    For lp.s2 = 1 To 100     For lp.s1 = 1 To n      With .Cells(k * 100 + lp.s2, lp.s1)       .Formula = "=RANDBETWEEN(1," & n & ")"       .Calculate       .Value = .Value       Data(k * 100 + lp.s2, lp.s1) = .Value      End With     Next lp.s1    Next lp.s2    Worksheets("Sheet1").Range("c1").Value = k    Application.DisplayAlerts = False    ThisWorkbook.Save    Application.DisplayAlerts = True   Next k   Set lp.Rg = .Range(.Cells(1, 1), .Cells(n, n))  End With End Sub                              以上

  • オプションボタンとリストボックスの連動

    下記コードを作りましたが、全く反応がありません。 どこが悪いのか、指摘いただけませんでしょうか? よろしくお願いします。 ・オプションボタン1を選択=リストシートのB列をリストボックスへ表示 ・オプションボタン2を選択=リストシートのC列をリストボックスへ表示 ・リストボックスから一項目を選択、値を入力シートへ入力 Private Sub UserForm2_Initialize() If OptionButton1.Value = True Then With UserForm2 .ListBox1.RowSource = "リスト!B3:B" & lastRow End With End If If OptionButton2.Value = True Then With UserForm2 .ListBox1.RowSource = "リスト!C3:C" & lastRow End With End If End Sub Private Sub ListBox1_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, _ ByVal X As Single, ByVal Y As Single) Worksheets("入力!").ActiveCell.Value = ListBox1.Value End Sub 以上

  • ExcelVBA オブジェクト変数の取扱について…

    お恥ずかしい限りです、 やはり色々解っていないですね。 今回はオブジェクト変数についてお教えください 例としてはこんな感じでしょうか? Option Explicit Sub Test() Dim カウンタ As Long, オブジェ As Object, データ(0 To 1) As String, メンバ As Long     データ(0) = "A"     データ(1) = "B"     メンバ = 間乱数(0, 1)     オブジェ.Value = データ(メンバ) '       此所と     Set Sheets(1).Ranje("A1") = オブジェ '  此所の書き方 End Sub Function 間乱数(ByVal Val1 As Long, ByVal Val2 As Long) As Long     間乱数 = Evaluate("RANDBETWEEN(" & Val1 & "," & Val2 & ")") End Function 間違っていると思うのですが、 どう書き直せば良いか、Netを調べても解りません。 ご教示を、どうぞ宜しくお願い致します。 P・s・ SpecialThanks mt2008 様 Evaluate、助かりました。

  • ExcelVBAでユーザーフォームのイベント対応

    Excel2010です。ユーザーフォーム上に配置した6個のComboBox(1から6)があります。 それぞれのComboBox上にマウスポインターを当てた際、そこが選択されていれば(="名称" 以外であれば)同じユーザーフォーム上にあるLabel23に説明の文字を表示させたいのです。 以下のようにComboBox1からComboBox6までComboBoxの番号以外は全く同じコードを書けばそうできると思いますが、これをもっと簡単に書くことはできないでしょうか?教えてください。 あと、もう1点 下記のコードの変数(Button、Shift、X、Y)の意味と役割は何でしょうか? Private Sub ComboBox1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single) If Me.ComboBox1.Value = "名称" Then Exit Sub Me.Label23.Caption = Me.ComboBox1.Value & Range(Me.ComboBox1.ControlSource).Offset(, 9).Value End Sub 途中ComboBox 2から5は省略 Private Sub ComboBox2_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single) If Me.ComboBox6.Value = "名称" Then Exit Sub Me.Label23.Caption = Me.ComboBox6.Value & Range(Me.ComboBox6.ControlSource).Offset(, 9).Value End Sub

  • 2つのVBAを組み合わせる方法

    お世話になります、2つのVBAを組み合わせる方法で迷っています。 1つ目が Private Sub Worksheet_Change(ByVal Target As Range) Dim i As Long, k As Long, myNum As Long If Intersect(Target, Range("C1,B9:B39")) Is Nothing Or Target.Count > 1 Then Exit Sub Application.EnableEvents = False With Target If .Column = 3 Then myNum = WorksheetFunction.Max(Range("B9:B39")) If IsDate(.Value) Then For i = 9 To 39 If Cells(i, "A").Value = "" Then Cells(i, "B").Value = "" Else Cells(i, "B") = myNum + i - 8 End If Next i End If Else i = .Row If .Value = "" Then Range(Cells(i + 1, "B"), Cells(39, "B")).ClearContents Else For k = i + 1 To 39 If Cells(k, "A").Value = "" Then Cells(k, "B").Value = "" Else Cells(k, "B") = Cells(k - 1, "B") + 1 End If Next k End If End If End With Application.EnableEvents = True End Sub です。 2つめが Private Sub Worksheet_Change(ByVal Target As Range)  Application.EnableEvents = True If Intersect(Target, Range("R8:R38")) Is Nothing Then Exit Sub Application.EnableEvents = False Range(Cells(Target.Row, 18), Cells(39, 18)).Value = Target.Value Application.EnableEvents = True End Sub です。2つのPrivate Sub Worksheet_Change(ByVal Target As Range)イベントのVBAですが、どのようにして組み合わせれば良いのでしょうか?

専門家に質問してみよう